2001-01-11 00:59:25 +01:00
|
|
|
/*
|
|
|
|
* msvcrt.dll mbcs functions
|
|
|
|
*
|
|
|
|
* Copyright 1999 Alexandre Julliard
|
|
|
|
* Copyright 2000 Jon Griffths
|
|
|
|
*
|
|
|
|
* FIXME
|
2001-01-22 03:22:26 +01:00
|
|
|
* Not currently binary compatible with win32. MSVCRT_mbctype must be
|
2001-01-11 00:59:25 +01:00
|
|
|
* populated correctly and the ismb* functions should reference it.
|
|
|
|
*/
|
|
|
|
#include "msvcrt.h"
|
|
|
|
|
|
|
|
DEFAULT_DEBUG_CHANNEL(msvcrt);
|
|
|
|
|
|
|
|
unsigned char MSVCRT_mbctype[257];
|
|
|
|
int MSVCRT___mb_cur_max = 1;
|
|
|
|
|
|
|
|
int __cdecl MSVCRT_isleadbyte(int);
|
|
|
|
char *__cdecl MSVCRT__strset(char *, int);
|
|
|
|
char *__cdecl MSVCRT__strnset(char *, int, unsigned int);
|
|
|
|
extern unsigned int MSVCRT_current_lc_all_cp;
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* __p__mbctype (MSVCRT.@)
|
|
|
|
*/
|
|
|
|
unsigned char *__cdecl MSVCRT___p__mbctype(void)
|
|
|
|
{
|
|
|
|
return MSVCRT_mbctype;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* __p___mb_cur_max(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int *__cdecl MSVCRT___p___mb_cur_max(void)
|
|
|
|
{
|
|
|
|
return &MSVCRT___mb_cur_max;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsnextc(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
unsigned int __cdecl MSVCRT__mbsnextc(const unsigned char *str)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*str))
|
|
|
|
return *str << 8 | str[1];
|
|
|
|
return *str; /* ASCII CP or SB char */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbscmp(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__mbscmp(const char *str, const char *cmp)
|
|
|
|
{
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
unsigned int strc, cmpc;
|
|
|
|
do {
|
|
|
|
if(!*str)
|
|
|
|
return *cmp ? -1 : 0;
|
|
|
|
if(!*cmp)
|
|
|
|
return 1;
|
|
|
|
strc = MSVCRT__mbsnextc(str);
|
|
|
|
cmpc = MSVCRT__mbsnextc(cmp);
|
|
|
|
if(strc != cmpc)
|
|
|
|
return strc < cmpc ? -1 : 1;
|
|
|
|
str +=(strc > 255) ? 2 : 1;
|
|
|
|
cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
|
|
|
|
} while(1);
|
|
|
|
}
|
|
|
|
return strcmp(str, cmp); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsicmp(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__mbsicmp(const char *str, const char *cmp)
|
|
|
|
{
|
|
|
|
/* FIXME: No tolower() for mb strings yet */
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
return MSVCRT__mbscmp(str, cmp);
|
|
|
|
return strcasecmp(str, cmp); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsncmp (MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__mbsncmp(const char *str, const char *cmp, unsigned int len)
|
|
|
|
{
|
|
|
|
if(!len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
unsigned int strc, cmpc;
|
|
|
|
while(len--)
|
|
|
|
{
|
|
|
|
if(!*str)
|
|
|
|
return *cmp ? -1 : 0;
|
|
|
|
if(!*cmp)
|
|
|
|
return 1;
|
|
|
|
strc = MSVCRT__mbsnextc(str);
|
|
|
|
cmpc = MSVCRT__mbsnextc(cmp);
|
|
|
|
if(strc != cmpc)
|
|
|
|
return strc < cmpc ? -1 : 1;
|
|
|
|
str +=(strc > 255) ? 2 : 1;
|
|
|
|
cmp +=(strc > 255) ? 2 : 1; /* Equal, use same increment */
|
|
|
|
}
|
|
|
|
return 0; /* Matched len chars */
|
|
|
|
}
|
|
|
|
return strncmp(str, cmp, len); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsnicmp(MSVCRT.@)
|
|
|
|
*
|
|
|
|
* Compare two multibyte strings case insensitively to 'len' characters.
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__mbsnicmp(const char *str, const char *cmp, unsigned int len)
|
|
|
|
{
|
|
|
|
/* FIXME: No tolower() for mb strings yet */
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
return MSVCRT__mbsncmp(str, cmp, len);
|
|
|
|
return strncasecmp(str, cmp, len); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsinc(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
char *__cdecl MSVCRT__mbsinc(const unsigned char *str)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
2001-01-12 20:56:22 +01:00
|
|
|
if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*str))
|
|
|
|
return (char *)str + 2; /* MB char */
|
2001-01-11 00:59:25 +01:00
|
|
|
|
2001-01-12 20:56:22 +01:00
|
|
|
return (char *)str + 1; /* ASCII CP or SB char */
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsninc(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
char *MSVCRT__mbsninc(const char *str, unsigned int num)
|
|
|
|
{
|
|
|
|
if(!str || num < 1)
|
|
|
|
return NULL;
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
while(num--)
|
|
|
|
str = MSVCRT__mbsinc(str);
|
2001-01-12 20:56:22 +01:00
|
|
|
return (char *)str;
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
2001-01-12 20:56:22 +01:00
|
|
|
return (char *)str + num; /* ASCII CP */
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbslen(MSVCRT.206)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
int __cdecl MSVCRT__mbslen(const unsigned char *str)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
int len = 0;
|
|
|
|
while(*str)
|
|
|
|
{
|
|
|
|
str += MSVCRT_isleadbyte(*str) ? 2 : 1;
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
return strlen(str); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsrchr(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
char *__cdecl MSVCRT__mbsrchr(const char *s,unsigned int x)
|
|
|
|
{
|
|
|
|
/* FIXME: handle multibyte strings */
|
|
|
|
return strrchr(s,x);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* mbtowc(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
int __cdecl MSVCRT_mbtowc(WCHAR *dst, const unsigned char *str, unsigned int n)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
if(n <= 0 || !str)
|
|
|
|
return 0;
|
|
|
|
if(!MultiByteToWideChar(CP_ACP, 0, str, n, dst, 1))
|
|
|
|
return 0;
|
|
|
|
/* return the number of bytes from src that have been used */
|
|
|
|
if(!*str)
|
|
|
|
return 0;
|
|
|
|
if(n >= 2 && MSVCRT_isleadbyte(*str) && str[1])
|
|
|
|
return 2;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbccpy(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
void __cdecl MSVCRT__mbccpy(char *dest, const unsigned char *src)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
*dest++ = *src;
|
|
|
|
if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*src))
|
|
|
|
*dest = *++src; /* MB char */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbbtombc(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
unsigned int __cdecl MSVCRT__mbbtombc(unsigned int c)
|
|
|
|
{
|
|
|
|
if(MSVCRT___mb_cur_max > 1 &&
|
|
|
|
((c >= 0x20 && c <=0x7e) ||(c >= 0xa1 && c <= 0xdf)))
|
|
|
|
{
|
|
|
|
/* FIXME: I can't get this function to return anything
|
|
|
|
* different to what I pass it...
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
return c; /* ASCII CP or no MB char */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbclen(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
unsigned int __cdecl MSVCRT__mbclen(const unsigned char *str)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
return MSVCRT_isleadbyte(*str) ? 2 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _ismbbkana(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__ismbbkana(unsigned int c)
|
|
|
|
{
|
|
|
|
/* FIXME: use lc_ctype when supported, not lc_all */
|
|
|
|
if(MSVCRT_current_lc_all_cp == 932)
|
|
|
|
{
|
|
|
|
/* Japanese/Katakana, CP 932 */
|
2001-01-12 20:56:22 +01:00
|
|
|
return (c >= 0xa1 && c <= 0xdf);
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _ismbchira(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__ismbchira(unsigned int c)
|
|
|
|
{
|
|
|
|
/* FIXME: use lc_ctype when supported, not lc_all */
|
|
|
|
if(MSVCRT_current_lc_all_cp == 932)
|
|
|
|
{
|
|
|
|
/* Japanese/Hiragana, CP 932 */
|
2001-01-12 20:56:22 +01:00
|
|
|
return (c >= 0x829f && c <= 0x82f1);
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _ismbckata(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__ismbckata(unsigned int c)
|
|
|
|
{
|
|
|
|
/* FIXME: use lc_ctype when supported, not lc_all */
|
|
|
|
if(MSVCRT_current_lc_all_cp == 932)
|
|
|
|
{
|
|
|
|
if(c < 256)
|
|
|
|
return MSVCRT__ismbbkana(c);
|
|
|
|
/* Japanese/Katakana, CP 932 */
|
2001-01-12 20:56:22 +01:00
|
|
|
return (c >= 0x8340 && c <= 0x8396 && c != 0x837f);
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _ismbblead(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__ismbblead(unsigned int c)
|
|
|
|
{
|
|
|
|
/* FIXME: should reference MSVCRT_mbctype */
|
|
|
|
return MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _ismbbtrail(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
int __cdecl MSVCRT__ismbbtrail(unsigned int c)
|
|
|
|
{
|
|
|
|
/* FIXME: should reference MSVCRT_mbctype */
|
|
|
|
return !MSVCRT__ismbblead(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _ismbslead(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
int __cdecl MSVCRT__ismbslead(const unsigned char *start, const unsigned char *str)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
/* Lead bytes can also be trail bytes if caller messed up
|
|
|
|
* iterating through the string...
|
|
|
|
*/
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
while(start < str)
|
|
|
|
start += MSVCRT_isleadbyte(*str) ? 2 : 1;
|
|
|
|
|
|
|
|
if(start == str)
|
|
|
|
return MSVCRT_isleadbyte(*str);
|
|
|
|
}
|
|
|
|
return 0; /* Must have been a trail, we skipped it */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _ismbstrail(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
int __cdecl MSVCRT__ismbstrail(const char *start, const unsigned char *str)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
/* Must not be a lead, and must be preceeded by one */
|
|
|
|
return !MSVCRT__ismbslead(start, str) && MSVCRT_isleadbyte(str[-1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsdec(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
char *__cdecl MSVCRT__mbsdec(const char *start, const char *cur)
|
|
|
|
{
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
2001-01-12 20:56:22 +01:00
|
|
|
return (char *)(MSVCRT__ismbstrail(start,cur-1) ? cur - 2 : cur -1);
|
2001-01-11 00:59:25 +01:00
|
|
|
|
2001-01-12 20:56:22 +01:00
|
|
|
return (char *)cur - 1; /* ASCII CP or SB char */
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsset(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
char *__cdecl MSVCRT__mbsset(char *str, unsigned int c)
|
|
|
|
{
|
|
|
|
char *ret = str;
|
|
|
|
|
|
|
|
if(MSVCRT___mb_cur_max == 1 || c < 256)
|
|
|
|
return MSVCRT__strset(str, c); /* ASCII CP or SB char */
|
|
|
|
|
|
|
|
c &= 0xffff; /* Strip high bits */
|
|
|
|
|
|
|
|
while(str[0] && str[1])
|
|
|
|
{
|
|
|
|
*str++ = c >> 8;
|
|
|
|
*str++ = c & 0xff;
|
|
|
|
}
|
|
|
|
if(str[0])
|
|
|
|
str[0] = '\0'; /* FIXME: OK to shorten? */
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsnset(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
char *__cdecl MSVCRT__mbsnset(char *str, unsigned int c, unsigned int len)
|
|
|
|
{
|
|
|
|
char *ret = str;
|
|
|
|
|
|
|
|
if(!len)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if(MSVCRT___mb_cur_max == 1 || c < 256)
|
|
|
|
return MSVCRT__strnset(str, c, len); /* ASCII CP or SB char */
|
|
|
|
|
|
|
|
c &= 0xffff; /* Strip high bits */
|
|
|
|
|
|
|
|
while(str[0] && str[1] && len--)
|
|
|
|
{
|
|
|
|
*str++ = c >> 8;
|
|
|
|
*str++ = c & 0xff;
|
|
|
|
}
|
|
|
|
if(len && str[0])
|
|
|
|
str[0] = '\0'; /* FIXME: OK to shorten? */
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbstrlen(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
int __cdecl MSVCRT__mbstrlen(const unsigned char *str)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
int len = 0;
|
|
|
|
while(*str)
|
|
|
|
{
|
|
|
|
str += MSVCRT_isleadbyte(*str) ? 2 : 1;
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
return strlen(str); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsncpy(MSVCRT.@)
|
|
|
|
*/
|
|
|
|
char *__cdecl MSVCRT__mbsncpy(char *dst, const char *src, unsigned int len)
|
|
|
|
{
|
|
|
|
if(!len)
|
|
|
|
return dst;
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
char *ret = dst;
|
|
|
|
while(src[0] && src[1] && len--)
|
|
|
|
{
|
|
|
|
*dst++ = *src++;
|
|
|
|
*dst++ = *src++;
|
|
|
|
}
|
|
|
|
if(len--)
|
|
|
|
{
|
|
|
|
*dst++ = *src++; /* Last char or '\0' */
|
|
|
|
while(len--)
|
|
|
|
*dst++ = '\0';
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return strncpy(dst, src, len); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbschr(MSVCRT.@)
|
|
|
|
*
|
|
|
|
* Find a multibyte character in a multibyte string.
|
|
|
|
*/
|
|
|
|
char *__cdecl MSVCRT__mbschr(const char *str, unsigned int c)
|
|
|
|
{
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
unsigned int next;
|
|
|
|
while((next = MSVCRT__mbsnextc(str)))
|
|
|
|
{
|
|
|
|
if(next == c)
|
2001-01-12 20:56:22 +01:00
|
|
|
return (char *)str;
|
2001-01-11 00:59:25 +01:00
|
|
|
str += next > 255 ? 2 : 1;
|
|
|
|
}
|
|
|
|
return c ? NULL :(char *)str;
|
|
|
|
}
|
|
|
|
return strchr(str, c); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsnccnt(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
unsigned int __cdecl MSVCRT__mbsnccnt(const unsigned char *str, unsigned int len)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
while(*str && len-- > 0)
|
|
|
|
{
|
|
|
|
if(MSVCRT_isleadbyte(*str))
|
|
|
|
{
|
|
|
|
str++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
ret++;
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return min(strlen(str), len); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _mbsncat(MSVCRT.@)
|
|
|
|
*/
|
2001-01-12 20:56:22 +01:00
|
|
|
char *__cdecl MSVCRT__mbsncat(char *dst, const unsigned char *src, unsigned int len)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
if(MSVCRT___mb_cur_max > 1)
|
|
|
|
{
|
|
|
|
char *res = dst;
|
|
|
|
dst += MSVCRT__mbslen(dst);
|
|
|
|
while(*src && len--)
|
|
|
|
{
|
|
|
|
*dst = *src;
|
|
|
|
if(MSVCRT_isleadbyte(*src))
|
|
|
|
*++dst = *++src;
|
|
|
|
dst++;
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
*dst++ = '\0';
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
return strncat(dst, src, len); /* ASCII CP */
|
|
|
|
}
|
|
|
|
|