diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h index 6103c1e7bb2..26a11777126 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -895,6 +895,7 @@ int __cdecl MSVCRT_vsnwprintf(MSVCRT_wchar_t *str, MSVCRT_size_t len, const MSVCRT_wchar_t *format, __ms_va_list valist ); int __cdecl MSVCRT__snwprintf(MSVCRT_wchar_t*, unsigned int, const MSVCRT_wchar_t*, ...); int __cdecl MSVCRT_sprintf(char*,const char*,...); +int __cdecl MSVCRT__snprintf(char*,unsigned int,const char*,...); int __cdecl MSVCRT__scprintf(const char*,...); int __cdecl MSVCRT_raise(int sig); diff --git a/dlls/msvcrt/tests/time.c b/dlls/msvcrt/tests/time.c index a7790d5a6a6..16f8b9c622c 100644 --- a/dlls/msvcrt/tests/time.c +++ b/dlls/msvcrt/tests/time.c @@ -48,6 +48,7 @@ static int* (__cdecl *p__daylight)(void); static int* (__cdecl *p___p__daylight)(void); static size_t (__cdecl *p_strftime)(char *, size_t, const char *, const struct tm *); static size_t (__cdecl *p_wcsftime)(wchar_t *, size_t, const wchar_t *, const struct tm *); +static char* (__cdecl *p_asctime)(const struct tm *); static void init(void) { @@ -65,6 +66,7 @@ static void init(void) p___p__daylight = (void*)GetProcAddress(hmod, "__p__daylight"); p_strftime = (void*)GetProcAddress(hmod, "strftime"); p_wcsftime = (void*)GetProcAddress(hmod, "wcsftime"); + p_asctime = (void*)GetProcAddress(hmod, "asctime"); } static int get_test_year(time_t *start) @@ -612,6 +614,71 @@ todo_wine ok(strcmp(bufA, buf) == 0, "expected %s, got %s\n", bufA, buf); } +static void test_asctime(void) +{ + struct tm* gmt_tm; + time_t gmt; + char *ret; + + if(!p_asctime || !p_gmtime) + { + win_skip("asctime or gmtime is not available\n"); + return; + } + + gmt = 0; + gmt_tm = p_gmtime(&gmt); + ret = p_asctime(gmt_tm); + ok(!strcmp(ret, "Thu Jan 01 00:00:00 1970\n"), "asctime retunred %s\n", ret); + + gmt = 312433121; + gmt_tm = p_gmtime(&gmt); + ret = p_asctime(gmt_tm); + ok(!strcmp(ret, "Mon Nov 26 02:58:41 1979\n"), "asctime retunred %s\n", ret); + + /* Week day is only checked if it's in 0..6 range */ + gmt_tm->tm_wday = 3; + ret = p_asctime(gmt_tm); + ok(!strcmp(ret, "Wed Nov 26 02:58:41 1979\n"), "asctime returned %s\n", ret); + + errno = 0xdeadbeef; + gmt_tm->tm_wday = 7; + ret = p_asctime(gmt_tm); + ok(!ret || broken(!ret[0]), "asctime returned %s\n", ret); + ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); + + /* Year day is ignored */ + gmt_tm->tm_wday = 3; + gmt_tm->tm_yday = 1300; + ret = p_asctime(gmt_tm); + ok(!strcmp(ret, "Wed Nov 26 02:58:41 1979\n"), "asctime returned %s\n", ret); + + /* Dates that can't be displayed using 26 characters are broken */ + gmt_tm->tm_mday = 28; + gmt_tm->tm_year = 8100; + ret = p_asctime(gmt_tm); + ok(!strcmp(ret, "Wed Nov 28 02:58:41 :000\n"), "asctime returned %s\n", ret); + + gmt_tm->tm_year = 264100; + ret = p_asctime(gmt_tm); + ok(!strcmp(ret, "Wed Nov 28 02:58:41 :000\n"), "asctime returned %s\n", ret); + + /* asctime works from year 1900 */ + errno = 0xdeadbeef; + gmt_tm->tm_year = -1; + ret = p_asctime(gmt_tm); + ok(!ret || broken(!strcmp(ret, "Wed Nov 28 02:58:41 190/\n")), "asctime returned %s\n", ret); + ok(errno==EINVAL || broken(errno == 0xdeadbeef), "errno = %d\n", errno); + + errno = 0xdeadbeef; + gmt_tm->tm_mon = 1; + gmt_tm->tm_mday = 30; + gmt_tm->tm_year = 79; + ret = p_asctime(gmt_tm); + ok(!ret || broken(!strcmp(ret, "Wed Feb 30 02:58:41 1979\n")), "asctime returned %s\n", ret); + ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno); +} + START_TEST(time) { init(); @@ -628,4 +695,5 @@ START_TEST(time) test_localtime32_s(); test_localtime64_s(); test_daylight(); + test_asctime(); } diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c index f9de5c5de0d..90dc79096d3 100644 --- a/dlls/msvcrt/time.c +++ b/dlls/msvcrt/time.c @@ -877,30 +877,46 @@ MSVCRT_size_t CDECL MSVCRT_wcsftime( MSVCRT_wchar_t *str, MSVCRT_size_t max, return len; } +static char* asctime_buf(char *buf, const struct MSVCRT_tm *mstm) +{ + static const char wday[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + static const char month[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (mstm->tm_sec<0 || mstm->tm_sec>59 + || mstm->tm_min<0 || mstm->tm_min>59 + || mstm->tm_hour<0 || mstm->tm_hour>23 + || mstm->tm_mon<0 || mstm->tm_mon>11 + || mstm->tm_wday<0 || mstm->tm_wday>6 + || mstm->tm_year<0 || mstm->tm_mday<0 + || mstm->tm_mday>MonthLengths[IsLeapYear(1900+mstm->tm_year)][mstm->tm_mon]) { + *MSVCRT__errno() = MSVCRT_EINVAL; + return NULL; + } + + MSVCRT__snprintf(buf, 26, "%s %s %02d %02d:%02d:%02d %c%03d\n", wday[mstm->tm_wday], + month[mstm->tm_mon], mstm->tm_mday, mstm->tm_hour, mstm->tm_min, + mstm->tm_sec, '1'+(mstm->tm_year+900)/1000, (900+mstm->tm_year)%1000); + return buf; +} + /********************************************************************* * asctime (MSVCRT.@) */ char * CDECL MSVCRT_asctime(const struct MSVCRT_tm *mstm) { - char bufferA[30]; - WCHAR bufferW[30]; - thread_data_t *data = msvcrt_get_thread_data(); - struct tm tm; - msvcrt_tm_to_unix( &tm, mstm ); + /* asctime returns date in format that always has exactly 26 characters */ + if (!data->asctime_buffer) { + data->asctime_buffer = MSVCRT_malloc(26); + if (!data->asctime_buffer) { + *MSVCRT__errno() = MSVCRT_ENOMEM; + return NULL; + } + } - if (!data->asctime_buffer) - data->asctime_buffer = MSVCRT_malloc( 30 ); /* ought to be enough */ - -#ifdef HAVE_ASCTIME_R - asctime_r( &tm, bufferA ); -#else - strcpy( bufferA, asctime(&tm) ); -#endif - MultiByteToWideChar( CP_UNIXCP, 0, bufferA, -1, bufferW, 30 ); - WideCharToMultiByte( CP_ACP, 0, bufferW, -1, data->asctime_buffer, 30, NULL, NULL ); - return data->asctime_buffer; + return asctime_buf(data->asctime_buffer, mstm); } /********************************************************************* @@ -908,23 +924,27 @@ char * CDECL MSVCRT_asctime(const struct MSVCRT_tm *mstm) */ int CDECL MSVCRT_asctime_s(char* time, MSVCRT_size_t size, const struct MSVCRT_tm *mstm) { - char* asc; - unsigned int len; - - if (!MSVCRT_CHECK_PMT(time != NULL) || !MSVCRT_CHECK_PMT(mstm != NULL)) { + if (!MSVCRT_CHECK_PMT(time != NULL) + || !MSVCRT_CHECK_PMT(mstm != NULL) + || !MSVCRT_CHECK_PMT(size >= 26)) { + if (time && size) + time[0] = 0; *MSVCRT__errno() = MSVCRT_EINVAL; return MSVCRT_EINVAL; } - asc = MSVCRT_asctime(mstm); - len = strlen(asc) + 1; - - if(!MSVCRT_CHECK_PMT(size >= len)) { - *MSVCRT__errno() = MSVCRT_ERANGE; - return MSVCRT_ERANGE; + if (!MSVCRT_CHECK_PMT(mstm->tm_sec>=0) || !MSVCRT_CHECK_PMT(mstm->tm_sec<60) + || !MSVCRT_CHECK_PMT(mstm->tm_min>=0) || !MSVCRT_CHECK_PMT(mstm->tm_min<60) + || !MSVCRT_CHECK_PMT(mstm->tm_hour>=0) || !MSVCRT_CHECK_PMT(mstm->tm_hour<24) + || !MSVCRT_CHECK_PMT(mstm->tm_mon>=0) || !MSVCRT_CHECK_PMT(mstm->tm_mon<12) + || !MSVCRT_CHECK_PMT(mstm->tm_wday>=0) || !MSVCRT_CHECK_PMT(mstm->tm_wday<7) + || !MSVCRT_CHECK_PMT(mstm->tm_year>=0) || !MSVCRT_CHECK_PMT(mstm->tm_mday>=0) + || !MSVCRT_CHECK_PMT(mstm->tm_mday <= MonthLengths[IsLeapYear(1900+mstm->tm_year)][mstm->tm_mon])) { + *MSVCRT__errno() = MSVCRT_EINVAL; + return MSVCRT_EINVAL; } - strcpy(time, asc); + asctime_buf(time, mstm); return 0; }