[PATCH v2 3/5] msvcrt: Correct behavior of strftime formats %c %x.
Jeff Smith
whydoubt at gmail.com
Mon Nov 18 01:07:28 CST 2019
Signed-off-by: Jeff Smith <whydoubt at gmail.com>
---
dlls/msvcrt/tests/time.c | 8 +--
dlls/msvcrt/time.c | 131 ++++++++++++++++++++++++-------------
dlls/ucrtbase/tests/misc.c | 6 +-
3 files changed, 94 insertions(+), 51 deletions(-)
diff --git a/dlls/msvcrt/tests/time.c b/dlls/msvcrt/tests/time.c
index 4a3d81d2dd..a71c3efee2 100644
--- a/dlls/msvcrt/tests/time.c
+++ b/dlls/msvcrt/tests/time.c
@@ -611,13 +611,13 @@ static void test_strftime(void)
} tests[] = {
{"e%#%e", "e%e", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
{"%c", "01/01/70 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
- {"%c", "02/30/70 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE},
+ {"%c", "02/30/70 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%#c", "Thursday, January 01, 1970 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
- {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE},
+ {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
- {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE},
+ {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%#x", "Thursday, January 01, 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
- {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE},
+ {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%X", "00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
{"%X", "14:00:00", { 0, 0, 14, 1, 0, 70, 4, 0, 0 }},
{"%a", "Thu", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c
index ae6fcc8b7a..058a8a53c9 100644
--- a/dlls/msvcrt/time.c
+++ b/dlls/msvcrt/time.c
@@ -954,46 +954,6 @@ char ** CDECL __p__tzname(void)
return MSVCRT__tzname;
}
-static inline BOOL strftime_date(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max,
- BOOL alternate, const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data)
-{
- char *format;
- SYSTEMTIME st;
- MSVCRT_size_t ret;
- LCID lcid;
-
- 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;
-
-#if _MSVCR_VER < 110
- lcid = time_data->lcid;
-#else
- lcid = LocaleNameToLCID(time_data->locname, 0);
-#endif
-
- format = alternate ? time_data->str.names.date : time_data->str.names.short_date;
- ret = GetDateFormatA(lcid, 0, &st, format, NULL, 0);
- if(ret && ret<max-*pos)
- ret = GetDateFormatA(lcid, 0, &st, format, str+*pos, max-*pos);
- if(!ret) {
- *str = 0;
- *MSVCRT__errno() = MSVCRT_EINVAL;
- return FALSE;
- }else if(ret > max-*pos) {
- *str = 0;
- *MSVCRT__errno() = MSVCRT_ERANGE;
- return FALSE;
- }
- *pos += ret-1;
- return TRUE;
-}
-
static inline BOOL strftime_time(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max,
const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data)
{
@@ -1057,9 +1017,12 @@ static inline BOOL strftime_tzdiff(char *str, MSVCRT_size_t *pos, MSVCRT_size_t
return TRUE;
}
-static inline BOOL strftime_str(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max, char *src)
+#define strftime_str(a,b,c,d) strftime_nstr(a,b,c,d,MSVCRT_SIZE_MAX)
+static inline BOOL strftime_nstr(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max,
+ const char *src, MSVCRT_size_t len)
{
- MSVCRT_size_t len = strlen(src);
+ if(len == MSVCRT_SIZE_MAX)
+ len = strlen(src);
if(len > max-*pos) {
*str = 0;
*MSVCRT__errno() = MSVCRT_ERANGE;
@@ -1093,6 +1056,84 @@ static inline BOOL strftime_int(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max
return TRUE;
}
+static inline BOOL strftime_format(char *str, MSVCRT_size_t *pos, MSVCRT_size_t max,
+ const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data, const char *format)
+{
+ BOOL ret = TRUE;
+
+ while(*format && ret)
+ {
+ const char *substr = format++;
+ switch(*substr) {
+ case '\'':
+ while(*format && *format != '\'') format++;
+ substr++;
+ ret = strftime_nstr(str, pos, max, substr, (*substr == '\'') ? 1 : format - substr);
+ if(ret && *format == '\'')
+ format++;
+ break;
+ case 'd':
+ while(*format == *substr) format++;
+ if(format - substr < 3)
+ ret = strftime_int(str, pos, max, mstm->tm_mday, (format - substr < 2) ? 0 : 2, 0, 31);
+ else {
+ if(!MSVCRT_CHECK_PMT(mstm->tm_wday>=0 && mstm->tm_wday<=6))
+ goto einval_error;
+ if(format - substr == 3)
+ ret = strftime_str(str, pos, max, time_data->str.names.short_wday[mstm->tm_wday]);
+ else
+ ret = strftime_str(str, pos, max, time_data->str.names.wday[mstm->tm_wday]);
+ }
+ break;
+ case 'M':
+ while(*format == *substr) format++;
+ if(format - substr < 3)
+ ret = strftime_int(str, pos, max, mstm->tm_mon+1, (format - substr < 2) ? 0 : 2, 1, 12);
+ else {
+ if(!MSVCRT_CHECK_PMT(mstm->tm_mon>=0 && mstm->tm_mon<=11))
+ goto einval_error;
+ if(format - substr == 3)
+ ret = strftime_str(str, pos, max, time_data->str.names.short_mon[mstm->tm_mon]);
+ else
+ ret = strftime_str(str, pos, max, time_data->str.names.mon[mstm->tm_mon]);
+ }
+ break;
+ case 'y':
+ while(*format == *substr) format++;
+ if(format - substr < 3) {
+ int year = mstm->tm_year + 1900;
+#if _MSVCR_VER>=140
+ if(!MSVCRT_CHECK_PMT(year>=0 && year<=9999))
+ goto einval_error;
+#else
+ if(!MSVCRT_CHECK_PMT(year>=1900))
+ goto einval_error;
+#endif
+ ret = strftime_int(str, pos, max, year%100, 2, 0, 99);
+ } else
+ ret = strftime_int(str, pos, max, mstm->tm_year+1900, 4, 0, 9999);
+ break;
+ case 'g':
+ while(*format == *substr) format++;
+ /* TODO: era */
+ break;
+ default:
+ while(*format && *format != '\'' &&
+ *format != 'd' && *format != 'M' &&
+ *format != 'y' && *format != 'g')
+ format++;
+ ret = strftime_nstr(str, pos, max, substr, format - substr);
+ break;
+ }
+ }
+
+ return ret;
+
+einval_error:
+ *str = 0;
+ return FALSE;
+}
+
static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *format,
const struct MSVCRT_tm *mstm, MSVCRT___lc_time_data *time_data, MSVCRT__locale_t loc)
{
@@ -1136,7 +1177,8 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f
switch(*format) {
case 'c':
- if(!strftime_date(str, &ret, max, alternate, mstm, time_data))
+ if(!strftime_format(str, &ret, max, mstm, time_data,
+ alternate ? time_data->str.names.date : time_data->str.names.short_date))
return 0;
if(ret < max)
str[ret++] = ' ';
@@ -1144,7 +1186,8 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f
return 0;
break;
case 'x':
- if(!strftime_date(str, &ret, max, alternate, mstm, time_data))
+ if(!strftime_format(str, &ret, max, mstm, time_data,
+ alternate ? time_data->str.names.date : time_data->str.names.short_date))
return 0;
break;
case 'X':
diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c
index fa5f29372c..35b7375cd8 100644
--- a/dlls/ucrtbase/tests/misc.c
+++ b/dlls/ucrtbase/tests/misc.c
@@ -939,11 +939,11 @@ static void test_strftime(void)
{"%c", "Thu Jan 1 00:00:00 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }, TRUE},
{"%c", "Thu Feb 30 00:00:00 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE},
{"%#c", "Thursday, January 01, 1970 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
- {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE},
+ {"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
- {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE},
+ {"%x", "02/30/70", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%#x", "Thursday, January 01, 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
- {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE},
+ {"%#x", "Thursday, February 30, 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%X", "00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
{"%X", "14:00:00", { 0, 0, 14, 1, 0, 70, 4, 0, 0 }},
{"%X", "23:59:60", { 60, 59, 23, 1, 0, 70, 4, 0, 0 }, TRUE},
--
2.23.0
More information about the wine-devel
mailing list