- Massive update to go towards a proper localization implementation
Based on wine code

svn path=/trunk/; revision=57833
This commit is contained in:
Jérôme Gardou 2012-12-09 03:17:14 +00:00
parent 359c37fdf9
commit b588451fcf
14 changed files with 2026 additions and 904 deletions

View file

@ -77,17 +77,11 @@ DllMain(PVOID hinstDll, ULONG dwReason, PVOID reserved)
/* Initialization of the WINE code */
msvcrt_init_mt_locks();
//if(!msvcrt_init_locale()) {
// msvcrt_free_mt_locks();
// msvcrt_free_tls_mem();
// return FALSE;
//}
//msvcrt_init_math();
msvcrt_init_io();
//msvcrt_init_console();
//msvcrt_init_args();
//msvcrt_init_signals();
_setmbcp(_MB_CP_LOCALE);
TRACE("Attach done\n");
break;
@ -110,7 +104,8 @@ DllMain(PVOID hinstDll, ULONG dwReason, PVOID reserved)
msvcrt_free_tls_mem();
if (!msvcrt_free_tls())
return FALSE;
//MSVCRT__free_locale(MSVCRT_locale);
if(global_locale)
MSVCRT__free_locale(global_locale);
if (__winitenv && __winitenv != _wenviron)
FreeEnvironment((char**)__winitenv);

View file

@ -1042,7 +1042,7 @@
@ cdecl _wcstoi64(wstr ptr long)
# @ cdecl _wcstoi64_l(wstr ptr long ptr)
# stub _wcstol_l
# stub _wcstombs_l
@ cdecl _wcstombs_l(ptr ptr long ptr)
# @ cdecl _wcstombs_s_l(ptr ptr long wstr long ptr)
@ cdecl _wcstoui64(wstr ptr long)
# @ cdecl _wcstoui64_l(wstr ptr long ptr)

View file

@ -61,6 +61,7 @@ list(APPEND CRT_SOURCE
math/sinf.c
math/sinh.c
math/tanh.c
mbstring/_setmbcp.c
mbstring/hanzen.c
mbstring/ischira.c
mbstring/iskana.c
@ -323,7 +324,6 @@ list(APPEND CRT_SOURCE
time/utime64.c
time/utime.c
time/wasctime.c
time/wcsftime.c
time/wctime32.c
time/wctime64.c
time/wctime.c

View file

@ -0,0 +1,32 @@
#ifndef __CRT_INTERNAL_LOCALE_H
#define __CRT_INTERNAL_LOCALE_H
typedef struct MSVCRT_threadmbcinfostruct *MSVCRT_pthreadmbcinfo;
typedef struct __lc_time_data {
union {
char *str[43];
struct {
char *short_wday[7];
char *wday[7];
char *short_mon[12];
char *mon[12];
char *am;
char *pm;
char *short_date;
char *date;
char *time;
} names;
} str;
LCID lcid;
int unk[2];
wchar_t *wstr[43];
char data[1];
} MSVCRT___lc_time_data;
int _setmbcp_l(int, LCID, MSVCRT_pthreadmbcinfo) DECLSPEC_HIDDEN;
MSVCRT_pthreadmbcinfo get_mbcinfo(void) DECLSPEC_HIDDEN;
LCID MSVCRT_locale_to_LCID(const char *locale) DECLSPEC_HIDDEN;
#endif //__CRT_INTERNAL_LOCALE_H

View file

@ -39,7 +39,7 @@
#define MAX_LOCALE_LENGTH 256
extern unsigned char _mbctype[257];
extern int MSVCRT___lc_codepage;
extern unsigned int MSVCRT___lc_codepage;
extern char MSVCRT_current_lc_all[MAX_LOCALE_LENGTH];
#if defined (_MSC_VER)

View file

@ -17,7 +17,7 @@
#include <internal/wine/eh.h>
typedef struct MSVCRT_threadlocaleinfostruct {
int refcount;
LONG refcount;
unsigned int lc_codepage;
unsigned int lc_collate_cp;
unsigned long lc_handle[6];
@ -36,14 +36,14 @@ typedef struct MSVCRT_threadlocaleinfostruct {
struct MSVCRT_lconv *lconv;
int *ctype1_refcount;
unsigned short *ctype1;
unsigned short *pctype;
const unsigned short *pctype;
unsigned char *pclmap;
unsigned char *pcumap;
struct __lc_time_data *lc_time_curr;
} MSVCRT_threadlocinfo;
typedef struct MSVCRT_threadmbcinfostruct {
int refcount;
LONG refcount;
int mbcodepage;
int ismbcodepage;
int mblcid;
@ -130,7 +130,16 @@ extern inline void msvcrt_free_tls_mem(void);
#define MSVCRT_ENABLE_PER_THREAD_LOCALE 1
#define MSVCRT_DISABLE_PER_THREAD_LOCALE 2
extern MSVCRT__locale_t MSVCRT_locale;
void __init_global_locale();
extern MSVCRT__locale_t global_locale;
#define MSVCRT_locale __get_MSVCRT_locale()
extern inline MSVCRT__locale_t __get_MSVCRT_locale()
{
if(!global_locale)
__init_global_locale();
return global_locale;
}
MSVCRT_pthreadlocinfo get_locinfo(void);
void __cdecl MSVCRT__free_locale(MSVCRT__locale_t);
void free_locinfo(MSVCRT_pthreadlocinfo);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,222 @@
/*
* msvcrt.dll mbcs functions
*
* Copyright 1999 Alexandre Julliard
* Copyright 2000 Jon Griffths
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* FIXME
* Not currently binary compatible with win32. MSVCRT_mbctype must be
* populated correctly and the ismb* functions should reference it.
*/
#include <precomp.h>
#include <mbctype.h>
/* It seems that the data about valid trail bytes is not available from kernel32
* so we have to store is here. The format is the same as for lead bytes in CPINFO */
struct cp_extra_info_t
{
int cp;
BYTE TrailBytes[MAX_LEADBYTES];
};
static struct cp_extra_info_t g_cpextrainfo[] =
{
{932, {0x40, 0x7e, 0x80, 0xfc, 0, 0}},
{936, {0x40, 0xfe, 0, 0}},
{949, {0x41, 0xfe, 0, 0}},
{950, {0x40, 0x7e, 0xa1, 0xfe, 0, 0}},
{1361, {0x31, 0x7e, 0x81, 0xfe, 0, 0}},
{20932, {1, 255, 0, 0}}, /* seems to give different results on different systems */
{0, {1, 255, 0, 0}} /* match all with FIXME */
};
/*********************************************************************
* INTERNAL: _setmbcp_l
*/
int _setmbcp_l(int cp, LCID lcid, MSVCRT_pthreadmbcinfo mbcinfo)
{
const char format[] = ".%d";
int newcp;
CPINFO cpi;
BYTE *bytes;
WORD chartypes[256];
char bufA[256];
WCHAR bufW[256];
int charcount;
int ret;
int i;
if(!mbcinfo)
mbcinfo = get_mbcinfo();
switch (cp)
{
case _MB_CP_ANSI:
newcp = GetACP();
break;
case _MB_CP_OEM:
newcp = GetOEMCP();
break;
case _MB_CP_LOCALE:
newcp = get_locinfo()->lc_codepage;
if(newcp)
break;
/* fall through (C locale) */
case _MB_CP_SBCS:
newcp = 20127; /* ASCII */
break;
default:
newcp = cp;
break;
}
if(lcid == -1) {
sprintf(bufA, format, newcp);
mbcinfo->mblcid = MSVCRT_locale_to_LCID(bufA);
} else {
mbcinfo->mblcid = lcid;
}
if(mbcinfo->mblcid == -1)
{
WARN("Can't assign LCID to codepage (%d)\n", mbcinfo->mblcid);
mbcinfo->mblcid = 0;
}
if (!GetCPInfo(newcp, &cpi))
{
WARN("Codepage %d not found\n", newcp);
*_errno() = EINVAL;
return -1;
}
/* setup the _mbctype */
memset(mbcinfo->mbctype, 0, sizeof(unsigned char[257]));
memset(mbcinfo->mbcasemap, 0, sizeof(unsigned char[256]));
bytes = cpi.LeadByte;
while (bytes[0] || bytes[1])
{
for (i = bytes[0]; i <= bytes[1]; i++)
mbcinfo->mbctype[i + 1] |= _M1;
bytes += 2;
}
if (cpi.MaxCharSize > 1)
{
/* trail bytes not available through kernel32 but stored in a structure in msvcrt */
struct cp_extra_info_t *cpextra = g_cpextrainfo;
mbcinfo->ismbcodepage = 1;
while (TRUE)
{
if (cpextra->cp == 0 || cpextra->cp == newcp)
{
if (cpextra->cp == 0)
FIXME("trail bytes data not available for DBCS codepage %d - assuming all bytes\n", newcp);
bytes = cpextra->TrailBytes;
while (bytes[0] || bytes[1])
{
for (i = bytes[0]; i <= bytes[1]; i++)
mbcinfo->mbctype[i + 1] |= _M2;
bytes += 2;
}
break;
}
cpextra++;
}
}
else
mbcinfo->ismbcodepage = 0;
/* we can't use GetStringTypeA directly because we don't have a locale - only a code page
*/
charcount = 0;
for (i = 0; i < 256; i++)
if (!(mbcinfo->mbctype[i + 1] & _M1))
bufA[charcount++] = i;
ret = MultiByteToWideChar(newcp, 0, bufA, charcount, bufW, charcount);
if (ret != charcount)
ERR("MultiByteToWideChar of chars failed for cp %d, ret=%d (exp %d), error=%d\n", newcp, ret, charcount, GetLastError());
GetStringTypeW(CT_CTYPE1, bufW, charcount, chartypes);
charcount = 0;
for (i = 0; i < 256; i++)
if (!(mbcinfo->mbctype[i + 1] & _M1))
{
if (chartypes[charcount] & C1_UPPER)
{
mbcinfo->mbctype[i + 1] |= _SBUP;
bufW[charcount] = tolowerW(bufW[charcount]);
}
else if (chartypes[charcount] & C1_LOWER)
{
mbcinfo->mbctype[i + 1] |= _SBLOW;
bufW[charcount] = toupperW(bufW[charcount]);
}
charcount++;
}
ret = WideCharToMultiByte(newcp, 0, bufW, charcount, bufA, charcount, NULL, NULL);
if (ret != charcount)
ERR("WideCharToMultiByte failed for cp %d, ret=%d (exp %d), error=%d\n", newcp, ret, charcount, GetLastError());
charcount = 0;
for (i = 0; i < 256; i++)
{
if(!(mbcinfo->mbctype[i + 1] & _M1))
{
if(mbcinfo->mbctype[i] & (C1_UPPER|C1_LOWER))
mbcinfo->mbcasemap[i] = bufA[charcount];
charcount++;
}
}
if (newcp == 932) /* CP932 only - set _MP and _MS */
{
/* On Windows it's possible to calculate the _MP and _MS from CT_CTYPE1
* and CT_CTYPE3. But as of Wine 0.9.43 we return wrong values what makes
* it hard. As this is set only for codepage 932 we hardcode it what gives
* also faster execution.
*/
for (i = 161; i <= 165; i++)
mbcinfo->mbctype[i + 1] |= _MP;
for (i = 166; i <= 223; i++)
mbcinfo->mbctype[i + 1] |= _MS;
}
mbcinfo->mbcodepage = newcp;
if(MSVCRT_locale && mbcinfo == MSVCRT_locale->mbcinfo)
memcpy(_mbctype, MSVCRT_locale->mbcinfo->mbctype, sizeof(_mbctype));
return 0;
}
/*********************************************************************
* _setmbcp (MSVCRT.@)
*/
int CDECL _setmbcp(int cp)
{
return _setmbcp_l(cp, -1, NULL);
}

View file

@ -13,17 +13,18 @@
#include <precomp.h>
#include <mbstring.h>
extern int g_mbcp_is_multibyte;
/*
* @implemented
/*********************************************************************
* _mbsncpy(MSVCRT.@)
* REMARKS
* The parameter n is the number or characters to copy, not the size of
* the buffer. Use _mbsnbcpy for a function analogical to strncpy
*/
unsigned char* _mbsncpy(unsigned char *dst, const unsigned char *src, size_t n)
unsigned char* CDECL _mbsncpy(unsigned char* dst, const unsigned char* src, size_t n)
{
unsigned char* ret = dst;
if(!n)
return dst;
if (g_mbcp_is_multibyte)
if (get_mbcinfo()->ismbcodepage)
{
while (*src && n)
{
@ -55,48 +56,12 @@ unsigned char* _mbsncpy(unsigned char *dst, const unsigned char *src, size_t n)
return ret;
}
/*
* The _mbsnbcpy function copies count bytes from src to dest. If src is shorter
* than dest, the string is padded with null characters. If dest is less than or
* equal to count it is not terminated with a null character.
*
* @implemented
*/
unsigned char * _mbsnbcpy(unsigned char *dst, const unsigned char *src, size_t n)
{
unsigned char* ret = dst;
if(!n)
return dst;
if(g_mbcp_is_multibyte)
{
int is_lead = 0;
while (*src && n)
{
is_lead = (!is_lead && _ismbblead(*src));
n--;
*dst++ = *src++;
}
if (is_lead) /* if string ends with a lead, remove it */
*(dst - 1) = 0;
}
else
{
while (n)
{
n--;
if (!(*dst++ = *src++)) break;
}
}
while (n--) *dst++ = 0;
return ret;
}
/*
/*********************************************************************
* _mbsnbcpy_s(MSVCRT.@)
* REMARKS
* Unlike _mbsnbcpy this function does not pad the rest of the dest
* string with 0
*/
*/
int CDECL _mbsnbcpy_s(unsigned char* dst, size_t size, const unsigned char* src, size_t n)
{
size_t pos = 0;
@ -111,7 +76,7 @@ int CDECL _mbsnbcpy_s(unsigned char* dst, size_t size, const unsigned char* src,
if(!n)
return 0;
if(g_mbcp_is_multibyte)
if(get_mbcinfo()->ismbcodepage)
{
int is_lead = 0;
while (*src && n)
@ -155,3 +120,40 @@ int CDECL _mbsnbcpy_s(unsigned char* dst, size_t size, const unsigned char* src,
return 0;
}
/*********************************************************************
* _mbsnbcpy(MSVCRT.@)
* REMARKS
* Like strncpy this function doesn't enforce the string to be
* NUL-terminated
*/
unsigned char* CDECL _mbsnbcpy(unsigned char* dst, const unsigned char* src, size_t n)
{
unsigned char* ret = dst;
if(!n)
return dst;
if(get_mbcinfo()->ismbcodepage)
{
int is_lead = 0;
while (*src && n)
{
is_lead = (!is_lead && _ismbblead(*src));
n--;
*dst++ = *src++;
}
if (is_lead) /* if string ends with a lead, remove it */
*(dst - 1) = 0;
}
else
{
while (n)
{
n--;
if (!(*dst++ = *src++)) break;
}
}
while (n--) *dst++ = 0;
return ret;
}

View file

@ -41,8 +41,8 @@ thread_data_t *msvcrt_get_thread_data(void)
ptr->tid = GetCurrentThreadId();
ptr->handle = INVALID_HANDLE_VALUE;
ptr->random_seed = 1;
//ptr->locinfo = MSVCRT_locale->locinfo;
//ptr->mbcinfo = MSVCRT_locale->mbcinfo;
ptr->locinfo = MSVCRT_locale->locinfo;
ptr->mbcinfo = MSVCRT_locale->mbcinfo;
}
SetLastError( err );
return ptr;

View file

@ -57,6 +57,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
#include <internal/atexit.h>
#include <internal/console.h>
#include <internal/ieee.h>
#include <internal/locale.h>
#include <internal/math.h>
#include <internal/mbstring.h>
#include <internal/mtdll.h>

View file

@ -548,24 +548,12 @@ const unsigned short _wctype[] = {
0x0100 | _LOWER, /* 0xfe */
0x0100 | _LOWER /* 0xff */
};
const unsigned short *_pctype = _ctype + 1;
const unsigned short *_pwctype = _wctype + 1;
extern const unsigned short wine_wctype_table[];
/*
* @implemented
*/
const unsigned short **__p__pctype(void)
{
return &_pctype;
}
const unsigned short* __cdecl __pctype_func(void)
{
return _pctype;
}
/*
* @implemented
*/

View file

@ -238,122 +238,81 @@ wchar_t* CDECL wcspbrk( const wchar_t* str, const wchar_t* accept )
/*********************************************************************
* wctomb (MSVCRT.@)
*/
INT CDECL wctomb(char *mbchar, wchar_t wchar)
/*********************************************************************
* wctomb (MSVCRT.@)
*/
INT CDECL wctomb( char *dst, wchar_t ch )
{
BOOL bUsedDefaultChar;
char chMultiByte[MB_LEN_MAX];
int nBytes;
BOOL error;
INT size;
/* At least one parameter needs to be given, the length of a null character cannot be queried (verified by tests under WinXP SP2) */
if(!mbchar && !wchar)
return 0;
/* Use WideCharToMultiByte for doing the conversion using the codepage currently set with setlocale() */
nBytes = WideCharToMultiByte(MSVCRT___lc_codepage, 0, &wchar, 1, chMultiByte, MB_LEN_MAX, NULL, &bUsedDefaultChar);
/* Only copy the character if an 'mbchar' pointer was given.
The "C" locale is emulated with codepage 1252 here. This codepage has a default character "?", but the "C" locale doesn't have one.
Therefore don't copy the character in this case. */
if(mbchar && !(MSVCRT_current_lc_all[0] == 'C' && !MSVCRT_current_lc_all[1] && bUsedDefaultChar))
memcpy(mbchar, chMultiByte, nBytes);
/* If the default character was used, set errno to EILSEQ and return -1. */
if(bUsedDefaultChar)
{
_set_errno(EILSEQ);
return -1;
size = WideCharToMultiByte(get_locinfo()->lc_codepage, 0, &ch, 1, dst, dst ? 6 : 0, NULL, &error);
if(!size || error) {
*_errno() = EINVAL;
return EOF;
}
/* Otherwise return the number of bytes this character occupies. */
return nBytes;
return size;
}
/*********************************************************************
* wcsrtombs_l (INTERNAL)
*/
static size_t CDECL wcsrtombs_l(char *mbstr, const wchar_t **wcstr, size_t count, _locale_t locale)
{
MSVCRT_pthreadlocinfo locinfo;
size_t tmp = 0;
BOOL used_default;
if(!locale)
locinfo = get_locinfo();
else
locinfo = ((MSVCRT__locale_t)locale)->locinfo;
if(!mbstr) {
tmp = WideCharToMultiByte(locinfo->lc_codepage, WC_NO_BEST_FIT_CHARS,
*wcstr, -1, NULL, 0, NULL, &used_default)-1;
if(used_default)
return -1;
return tmp;
}
while(**wcstr) {
char buf[3];
size_t i, size;
size = WideCharToMultiByte(locinfo->lc_codepage, WC_NO_BEST_FIT_CHARS,
*wcstr, 1, buf, 3, NULL, &used_default);
if(used_default)
return -1;
if(tmp+size > count)
return tmp;
for(i=0; i<size; i++)
mbstr[tmp++] = buf[i];
(*wcstr)++;
}
if(tmp < count) {
mbstr[tmp] = '\0';
*wcstr = NULL;
}
return tmp;
}
/*********************************************************************
* _wcstombs_l (MSVCRT.@)
*/
size_t CDECL _wcstombs_l(char *mbstr, const wchar_t *wcstr, size_t count, _locale_t locale)
{
return wcsrtombs_l(mbstr, &wcstr, count, locale);
}
/*********************************************************************
* wcstombs (MSVCRT.@)
*/
size_t CDECL wcstombs(char *mbstr, const wchar_t *wcstr, size_t count)
{
BOOL bUsedDefaultChar;
char* p = mbstr;
int nResult;
/* Does the caller query for output buffer size? */
if(!mbstr)
{
int nLength;
/* If we currently use the "C" locale, the length of the input string is returned (verified by tests under WinXP SP2) */
if(MSVCRT_current_lc_all[0] == 'C' && !MSVCRT_current_lc_all[1])
return wcslen(wcstr);
/* Otherwise check the length each character needs and build a final return value out of this */
count = wcslen(wcstr);
nLength = 0;
while((int)(--count) >= 0 && *wcstr)
{
/* Get the length of this character */
nResult = wctomb(NULL, *wcstr++);
/* If this character is not convertible in the current locale, the end result will be -1 */
if(nResult == -1)
return -1;
nLength += nResult;
}
/* Return the final length */
return nLength;
}
/* Convert the string then */
bUsedDefaultChar = FALSE;
for(;;)
{
char chMultiByte[MB_LEN_MAX];
UINT uLength;
/* Are we at the terminating null character? */
if(!*wcstr)
{
/* Set the null character, but don't increment the pointer as the returned length never includes the terminating null character */
*p = 0;
break;
}
/* Convert this character into the temporary chMultiByte variable */
ZeroMemory(chMultiByte, MB_LEN_MAX);
nResult = wctomb(chMultiByte, *wcstr++);
/* Check if this was an invalid character */
if(nResult == -1)
bUsedDefaultChar = TRUE;
/* If we got no character, stop the conversion process here */
if(!chMultiByte[0])
break;
/* Determine whether this is a double-byte or a single-byte character */
if(chMultiByte[1])
uLength = 2;
else
uLength = 1;
/* Decrease 'count' by the character length and check if the buffer can still hold the full character */
count -= uLength;
if((int)count < 0)
break;
/* It can, so copy it and move the pointer forward */
memcpy(p, chMultiByte, uLength);
p += uLength;
}
if(bUsedDefaultChar)
return -1;
/* Return the length in bytes of the copied characters (without the terminating null character) */
return p - mbstr;
return wcsrtombs_l(mbstr, &wcstr, count, NULL);
}
#endif

View file

@ -6,14 +6,320 @@
* PROGRAMER:
*/
#include <precomp.h>
#include <tchar.h>
size_t
_tcsftime(_TCHAR *strDest,
size_t maxsize,
const _TCHAR *format,
const struct tm *timeptr)
static inline BOOL strftime_date(char *str, size_t *pos, size_t max,
BOOL alternate, const struct tm *mstm, MSVCRT___lc_time_data *time_data)
{
char *format;
SYSTEMTIME st;
size_t ret;
st.wYear = mstm->tm_year + 1900;
st.wMonth = mstm->tm_mon + 1;
st.wDayOfWeek = mstm->tm_wday;
st.wDay = mstm->tm_mday;
st.wHour = mstm->tm_hour;
st.wMinute = mstm->tm_min;
st.wSecond = mstm->tm_sec;
st.wMilliseconds = 0;
format = alternate ? time_data->str.names.date : time_data->str.names.short_date;
ret = GetDateFormatA(time_data->lcid, 0, &st, format, NULL, 0);
if(ret && ret<max-*pos)
ret = GetDateFormatA(time_data->lcid, 0, &st, format, str+*pos, max-*pos);
if(!ret) {
*str = 0;
*_errno() = EINVAL;
return FALSE;
}else if(ret > max-*pos) {
*str = 0;
*_errno() = ERANGE;
return FALSE;
}
*pos += ret-1;
return TRUE;
}
static inline BOOL strftime_time(char *str, size_t *pos, size_t max,
const struct tm *mstm, MSVCRT___lc_time_data *time_data)
{
SYSTEMTIME st;
size_t ret;
st.wYear = mstm->tm_year + 1900;
st.wMonth = mstm->tm_mon + 1;
st.wDayOfWeek = mstm->tm_wday;
st.wDay = mstm->tm_mday;
st.wHour = mstm->tm_hour;
st.wMinute = mstm->tm_min;
st.wSecond = mstm->tm_sec;
st.wMilliseconds = 0;
ret = GetTimeFormatA(time_data->lcid, 0, &st, time_data->str.names.time, NULL, 0);
if(ret && ret<max-*pos)
ret = GetTimeFormatA(time_data->lcid, 0, &st, time_data->str.names.time,
str+*pos, max-*pos);
if(!ret) {
*str = 0;
*_errno() = EINVAL;
return FALSE;
}else if(ret > max-*pos) {
*str = 0;
*_errno() = ERANGE;
return FALSE;
}
*pos += ret-1;
return TRUE;
}
static inline BOOL strftime_str(char *str, size_t *pos, size_t max, char *src)
{
size_t len = strlen(src);
if(len > max-*pos) {
*str = 0;
*_errno() = ERANGE;
return FALSE;
}
memcpy(str+*pos, src, len);
*pos += len;
return TRUE;
}
static inline BOOL strftime_int(char *str, size_t *pos, size_t max,
int src, int prec, int l, int h)
{
size_t len;
if(src<l || src>h) {
*str = 0;
*_errno() = EINVAL;
return FALSE;
}
len = _snprintf(str+*pos, max-*pos, "%0*d", prec, src);
if(len == -1) {
*str = 0;
*_errno() = ERANGE;
return FALSE;
}
*pos += len;
return TRUE;
}
/*********************************************************************
* _Strftime (MSVCRT.@)
*/
size_t CDECL _Strftime(char *str, size_t max, const char *format,
const struct tm *mstm, MSVCRT___lc_time_data *time_data)
{
size_t ret, tmp;
BOOL alternate;
TRACE("(%p %ld %s %p %p)\n", str, max, format, mstm, time_data);
if(!str || !format) {
if(str && max)
*str = 0;
*_errno() = EINVAL;
return 0;
}
if(!time_data)
time_data = get_locinfo()->lc_time_curr;
for(ret=0; *format && ret<max; format++) {
if(*format != '%') {
str[ret++] = *format;
continue;
}
format++;
if(*format == '#') {
alternate = TRUE;
format++;
}else {
alternate = FALSE;
}
if(!mstm)
goto einval_error;
switch(*format) {
case 'c':
if(!strftime_date(str, &ret, max, alternate, mstm, time_data))
return 0;
if(ret < max)
str[ret++] = ' ';
if(!strftime_time(str, &ret, max, mstm, time_data))
return 0;
break;
case 'x':
if(!strftime_date(str, &ret, max, alternate, mstm, time_data))
return 0;
break;
case 'X':
if(!strftime_time(str, &ret, max, mstm, time_data))
return 0;
break;
case 'a':
if(mstm->tm_wday<0 || mstm->tm_wday>6)
goto einval_error;
if(!strftime_str(str, &ret, max, time_data->str.names.short_wday[mstm->tm_wday]))
return 0;
break;
case 'A':
if(mstm->tm_wday<0 || mstm->tm_wday>6)
goto einval_error;
if(!strftime_str(str, &ret, max, time_data->str.names.wday[mstm->tm_wday]))
return 0;
break;
case 'b':
if(mstm->tm_mon<0 || mstm->tm_mon>11)
goto einval_error;
if(!strftime_str(str, &ret, max, time_data->str.names.short_mon[mstm->tm_mon]))
return 0;
break;
case 'B':
if(mstm->tm_mon<0 || mstm->tm_mon>11)
goto einval_error;
if(!strftime_str(str, &ret, max, time_data->str.names.mon[mstm->tm_mon]))
return 0;
break;
case 'd':
if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 0, 31))
return 0;
break;
case 'H':
if(!strftime_int(str, &ret, max, mstm->tm_hour, alternate ? 0 : 2, 0, 23))
return 0;
break;
case 'I':
tmp = mstm->tm_hour;
if(tmp > 12)
tmp -= 12;
else if(!tmp)
tmp = 12;
if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 1, 12))
return 0;
break;
case 'j':
if(!strftime_int(str, &ret, max, mstm->tm_yday+1, alternate ? 0 : 3, 1, 366))
return 0;
break;
case 'm':
if(!strftime_int(str, &ret, max, mstm->tm_mon+1, alternate ? 0 : 2, 1, 12))
return 0;
break;
case 'M':
if(!strftime_int(str, &ret, max, mstm->tm_min, alternate ? 0 : 2, 0, 59))
return 0;
break;
case 'p':
if(mstm->tm_hour<0 || mstm->tm_hour>23)
goto einval_error;
if(!strftime_str(str, &ret, max, mstm->tm_hour<12 ?
time_data->str.names.am : time_data->str.names.pm))
return 0;
break;
case 'S':
if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, 59))
return 0;
break;
case 'w':
if(!strftime_int(str, &ret, max, mstm->tm_wday, 0, 0, 6))
return 0;
break;
case 'y':
if(!strftime_int(str, &ret, max, mstm->tm_year%100, alternate ? 0 : 2, 0, 99))
return 0;
break;
case 'Y':
tmp = 1900+mstm->tm_year;
if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 4, 0, 9999))
return 0;
break;
case 'z':
case 'Z':
_tzset();
if(_get_tzname(&tmp, str+ret, max-ret, mstm->tm_isdst ? 1 : 0))
return 0;
ret += tmp;
break;
case 'U':
case 'W':
if(mstm->tm_wday<0 || mstm->tm_wday>6 || mstm->tm_yday<0 || mstm->tm_yday>365)
goto einval_error;
if(*format == 'U')
tmp = mstm->tm_wday;
else if(!mstm->tm_wday)
tmp = 6;
else
tmp = mstm->tm_wday-1;
tmp = mstm->tm_yday/7 + (tmp<=mstm->tm_yday%7);
if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 0, 53))
return 0;
break;
case '%':
str[ret++] = '%';
break;
default:
WARN("unknown format %c\n", *format);
goto einval_error;
}
}
if(ret == max) {
if(max)
*str = 0;
*_errno() = ERANGE;
return 0;
}
str[ret] = 0;
return ret;
einval_error:
*str = 0;
*_errno() = EINVAL;
return 0;
}
/*********************************************************************
* strftime (MSVCRT.@)
*/
size_t CDECL strftime( char *str, size_t max, const char *format,
const struct tm *mstm )
{
return _Strftime(str, max, format, mstm, NULL);
}
/*********************************************************************
* wcsftime (MSVCRT.@)
*/
size_t CDECL wcsftime( wchar_t *str, size_t max,
const wchar_t *format, const struct tm *mstm )
{
char *s, *fmt;
size_t len;
TRACE("%p %ld %s %p\n", str, max, debugstr_w(format), mstm );
len = WideCharToMultiByte( CP_ACP, 0, format, -1, NULL, 0, NULL, NULL );
if (!(fmt = malloc( len ))) return 0;
WideCharToMultiByte( CP_ACP, 0, format, -1, fmt, len, NULL, NULL );
if ((s = malloc( max*4 )))
{
if (!strftime( s, max*4, fmt, mstm )) s[0] = 0;
len = MultiByteToWideChar( CP_ACP, 0, s, -1, str, max );
if (len) len--;
free( s );
}
else len = 0;
free( fmt );
return len;
}