[PATCH 3/5] msvcrt: Correct behavior of strftime formats %c %x.

Jeff Smith whydoubt at gmail.com
Sun Nov 17 22:27:38 CST 2019


Signed-off-by: Jeff Smith <whydoubt at gmail.com>
---
 dlls/msvcrt/tests/time.c   |   8 +--
 dlls/msvcrt/time.c         | 127 ++++++++++++++++++++++++-------------
 dlls/ucrtbase/tests/misc.c |   6 +-
 3 files changed, 90 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..6c6d177ad8 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,80 @@ 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(!MSVCRT_CHECK_PMT(mstm->tm_wday>=0 && mstm->tm_wday<=6))
+                goto einval_error;
+
+            if(format - substr < 3)
+                ret = strftime_int(str, pos, max, mstm->tm_mday, (format - substr < 2) ? 0 : 2, 0, 31);
+            else 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(!MSVCRT_CHECK_PMT(mstm->tm_mon>=0 && mstm->tm_mon<=11))
+                goto einval_error;
+
+            if(format - substr < 3)
+                ret = strftime_int(str, pos, max, mstm->tm_mon+1, (format - substr < 2) ? 0 : 2, 1, 12);
+            else 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) {
+#if _MSVCR_VER>=140
+                if(!MSVCRT_CHECK_PMT(mstm->tm_year>=-1900 && mstm->tm_year<=8099))
+                    goto einval_error;
+                ret = strftime_int(str, pos, max, (mstm->tm_year+1900)%100, 2, 0, 99);
+#else
+                ret = strftime_int(str, pos, max, mstm->tm_year%100, 2, 0, 99);
+#endif
+            } 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 +1173,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 +1182,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