I've been working on this patch for a few days now to fulfill my bug report (#47809). However, every time I've gone to try to submit it somebody has proposed a commit that I can't see. So, I know that this patch is already outdated but I wanted to drop it here for the record.
Some notes on this patch: * I know it's not as trivial as most of the other stuff going on this file. Sorry about that, but I couldn't think of a cleaner way to do it. I don't think I did anything unorthodox as far as code calls, but I packaged all the ISO 8601 stuff in a block with its own variables to keep it self-contained. * This is my first Wine patch (and only my second ever software patch) so if you have some constructive criticism, I'm all for it.
From a5e1742f944a5c857283b33653265a8d702b06fc Mon Sep 17 00:00:00 2001
From: Chuck Ricketts chuck.the.pc.guy+winehq@gmail.com Date: Tue, 1 Oct 2019 01:28:07 -0500 Subject: [PATCH] msvcrt: add missing tokens in strftime_helper (except %r)
--- dlls/msvcrt/time.c | 264 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+)
diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c index c72b3ed21e..cfe1463996 100644 --- a/dlls/msvcrt/time.c +++ b/dlls/msvcrt/time.c @@ -1141,6 +1141,7 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f if(!strftime_str(str, &ret, max, time_data->str.names.wday[mstm->tm_wday])) return 0; break; + case 'h': case 'b': if(mstm->tm_mon<0 || mstm->tm_mon>11) goto einval_error; @@ -1153,10 +1154,142 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f if(!strftime_str(str, &ret, max, time_data->str.names.mon[mstm->tm_mon])) return 0; break; + case 'C': + if(!strftime_int(str, &ret, max, (mstm->tm_year+1900)/100, alternate ? 0: 2, 0, 99)) + return 0; + break; case 'd': if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 0, 31)) return 0; break; + case 'D': + { + char* tmp_str = 0; + + if(mstm->tm_mon<0 || mstm->tm_mon>11) + goto einval_error; + if(mstm->tm_mday<1 || mstm->tm_mday>31) + goto einval_error; + + if(!(tmp_str == MSVCRT_malloc(9))) + return 0; + MSVCRT__snprintf(tmp_str, 9, "%0*d/%0*d/%0*d", 2, mstm->tm_mon, 2, mstm->tm_mday, + 2, (mstm->tm_year+1900)%100); + if(!strftime_str(str, &ret, max, tmp_str)) { + MSVCRT_free(tmp_str); + return 0; + } + MSVCRT_free(tmp_str); + } + break; + case 'e': + if(mstm->tm_mday<1 || mstm->tm_mday>31) + goto einval_error; + if(!strftime_int(str, &ret, max, mstm->tm_mday, 2, 1, 31)) + return 0; + if(str[ret-2] == '0') + str[ret-2] = ' '; + break; + case 'F': + { + char* tmp_str = 0; + + if(mstm->tm_mon<0 || mstm->tm_mon>11) + goto einval_error; + if(mstm->tm_mday<1 || mstm->tm_mday>31) + goto einval_error; + + if(!(tmp_str = MSVCRT_malloc(12))) + return 0; + + MSVCRT__snprintf(tmp_str, 12, "%d-%0*d-%0*d", mstm->tm_year+1900, 2, + mstm->tm_mon+1, 2, mstm->tm_mday); + if(!strftime_str(str, &ret, max, tmp_str)) { + MSVCRT_free(tmp_str); + return 0; + } + MSVCRT_free(tmp_str); + } + break; + case 'G': + { +#ifdef _WIN64 + MSVCRT___time64_t firstmontime = 0; + MSVCRT___time64_t temp_time = 0; +#else + MSVCRT___time32_t firstmontime = 0; + MSVCRT___time32_t temp_time = 0; +#endif + struct MSVCRT_tm* tmp_mstm = 0; + struct MSVCRT_tm* oldstate = 0; + struct MSVCRT_tm* firstmontm = 0; + int iso8601yr = -1; + int iso8601wk = -1; + char* tmp_str = 0; + + if(!(tmp_mstm = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) + return 0; + if(msvcrt_get_thread_data()->time_buffer && + !(oldstate = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) { + MSVCRT_free(tmp_mstm); + return 0; + } + + if(oldstate) + memcpy(oldstate, msvcrt_get_thread_data()->time_buffer, + sizeof(struct MSVCRT_tm)); + + memcpy(tmp_mstm, mstm, sizeof(struct MSVCRT_tm)); + + temp_time = MSVCRT_mktime(tmp_mstm); + MSVCRT_free(tmp_mstm); + tmp_mstm = MSVCRT_gmtime(&temp_time); + + iso8601yr = tmp_mstm->tm_year + 1900; + iso8601wk = (tmp_mstm->tm_yday+1-(tmp_mstm->tm_wday==0?7:tmp_mstm->tm_wday)+10)/7; + if(iso8601wk == 53) { + if(!(firstmontm = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) { + MSVCRT_free(oldstate); + return 0; + } + memset(firstmontm, '\0', sizeof(struct MSVCRT_tm)); + firstmontm->tm_mon = 0; + firstmontm->tm_mday = 4; + firstmontm->tm_year = tmp_mstm->tm_year+1; + firstmontime = MSVCRT_mktime(firstmontm); + MSVCRT_free(firstmontm); + firstmontm = MSVCRT_gmtime(&firstmontime); + firstmontime = firstmontime-((firstmontm->tm_wday-1==-1?6:firstmontm->tm_wday-1)*86400); + if(firstmontime <= temp_time) + iso8601yr += 1; + } + if(iso8601wk == 0) + iso8601yr -= 1; + + if(!(tmp_str = MSVCRT_malloc(6))) { + MSVCRT_free(oldstate); + return 0; + } + memset(tmp_str, '\0', 6); + + MSVCRT__snprintf(tmp_str, 6, "%d", iso8601yr); + if(!strftime_str(str, &ret, max, tmp_str)) { + if(oldstate) { + memcpy(msvcrt_get_thread_data()->time_buffer, oldstate, + sizeof(struct MSVCRT_tm)); + MSVCRT_free(oldstate); + } + MSVCRT_free(tmp_str); + return 0; + } + if(oldstate) { + memcpy(msvcrt_get_thread_data()->time_buffer, oldstate, + sizeof(struct MSVCRT_tm)); + MSVCRT_free(oldstate); + } + MSVCRT_free(tmp_str); + } + break; case 'H': if(!strftime_int(str, &ret, max, mstm->tm_hour, alternate ? 0 : 2, 0, 23)) return 0; @@ -1217,6 +1350,137 @@ static MSVCRT_size_t strftime_helper(char *str, MSVCRT_size_t max, const char *f return 0; break; #endif + case 'u': + { +#ifdef _WIN64 + MSVCRT___time64_t tmp_time = 0; +#else + MSVCRT___time32_t tmp_time = 0; +#endif + struct MSVCRT_tm* tmp_mstm = 0; + struct MSVCRT_tm* oldstate = 0; + + if(!(tmp_mstm = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) + return 0; + if( msvcrt_get_thread_data()->time_buffer && + !(oldstate = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) { + MSVCRT_free(tmp_mstm); + return 0; + } + + if( oldstate ) + memcpy(oldstate, msvcrt_get_thread_data()->time_buffer, + sizeof(struct MSVCRT_tm)); + memcpy(tmp_mstm, mstm, sizeof(struct MSVCRT_tm)); + + tmp_time = MSVCRT_mktime(tmp_mstm); + MSVCRT_free(tmp_mstm); + tmp_mstm = MSVCRT_gmtime(&tmp_time); + + if(!strftime_int(str, &ret, max, (tmp_mstm->tm_wday-1==-1?6:tmp_mstm->tm_wday-1)+1, + 0, 1, 7)) { + if( oldstate ) { + memcpy(msvcrt_get_thread_data()->time_buffer, oldstate, + sizeof(struct MSVCRT_tm)); + MSVCRT_free(oldstate); + } + return 0; + } + + if( oldstate ) { + memcpy(msvcrt_get_thread_data()->time_buffer, oldstate, + sizeof(struct MSVCRT_tm)); + MSVCRT_free(oldstate); + } + } + break; + case 'V': + { +#ifdef _WIN64 + MSVCRT___time64_t tmp_time = 0; + MSVCRT___time64_t tmp_time_mon = 0; +#else + MSVCRT___time32_t tmp_time = 0; + MSVCRT___time32_t tmp_time_mon = 0; +#endif + int iso8601wk; + + struct MSVCRT_tm* tmp_mstm = 0; + struct MSVCRT_tm* oldstate = 0; + + if(!(tmp_mstm = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) + return 0; + if(msvcrt_get_thread_data()->time_buffer && + !(oldstate = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) { + MSVCRT_free(tmp_mstm); + return 0; + } + + if( oldstate ) + memcpy(oldstate, msvcrt_get_thread_data()->time_buffer, + sizeof(struct MSVCRT_tm)); + + memcpy(tmp_mstm, mstm, sizeof(struct MSVCRT_tm)); + + tmp_time = MSVCRT_mktime(tmp_mstm); + MSVCRT_free(tmp_mstm); + tmp_mstm = MSVCRT_gmtime(&tmp_time); + + iso8601wk = ((tmp_mstm->tm_yday+1-(tmp_mstm->tm_wday==0?7:tmp_mstm->tm_wday)+10)/7); + if (iso8601wk == 53 ) { + if(!(tmp_mstm = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) { + if(oldstate) + MSVCRT_free(oldstate); + return 0; + } + + memset(tmp_mstm, '\0', sizeof(struct MSVCRT_tm)); + + tmp_mstm->tm_mday = 4; + tmp_mstm->tm_mon = 0; + tmp_mstm->tm_year = (mstm->tm_year+1); + tmp_time_mon = MSVCRT_mktime(tmp_mstm); + MSVCRT_free(tmp_mstm); + tmp_time_mon = tmp_time_mon-((tmp_mstm->tm_wday-1==-1?6:tmp_mstm->tm_wday-1)*86400); + if(tmp_time_mon <= tmp_time) + iso8601wk = 1; + else + iso8601wk = 53; + } + if(iso8601wk == 0) { + if(!(tmp_mstm = MSVCRT_malloc(sizeof(struct MSVCRT_tm)))) { + if(oldstate) + MSVCRT_free(oldstate); + return 0; + } + + memset(tmp_mstm, '\0', sizeof(struct MSVCRT_tm)); + + tmp_mstm->tm_mday = 4; + tmp_mstm->tm_mon = 0; + tmp_mstm->tm_year = mstm->tm_year; + tmp_time_mon = MSVCRT_mktime(tmp_mstm); + tmp_time_mon = (tmp_time_mon-((tmp_mstm->tm_wday-1==-1?6:tmp_mstm->tm_wday-1)*86400))-604800; + MSVCRT_free(tmp_mstm); + tmp_mstm = MSVCRT_gmtime(&tmp_time_mon); + iso8601wk = (tmp_mstm->tm_yday+1-(tmp_mstm->tm_wday==0?7:tmp_mstm->tm_wday)+10)/7; + } + + if(!strftime_int(str, &ret, max, iso8601wk, 2, 0, 53)) { + if( oldstate ) { + memcpy(msvcrt_get_thread_data()->time_buffer, oldstate, + sizeof(struct MSVCRT_tm)); + MSVCRT_free(oldstate); + } + return 0; + } + if( oldstate ) { + memcpy(msvcrt_get_thread_data()->time_buffer, oldstate, + sizeof(struct MSVCRT_tm)); + MSVCRT_free(oldstate); + } + } + break; case 'w': if(!strftime_int(str, &ret, max, mstm->tm_wday, 0, 0, 6)) return 0;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=57261
Your paranoid android.
=== debian10 (build log) ===
error: corrupt patch at line 29 Task: Patch failed to apply
=== debian10 (build log) ===
error: corrupt patch at line 29 Task: Patch failed to apply
Hi Chuck,
I'll comment on 'h' and 'G' formats that are not implemented in wine (and there are no patches for them to be reviewed).
- case 'h': > case 'b':The 'h' format was introduced in ucrtbase. All
other options that were introduced in newer version of crt library are implemented inside #if _MSVCR_VER >= 140 block.
- case 'G':
- {
+#ifdef _WIN64
- MSVCRT___time64_t firstmontime = 0;
- MSVCRT___time64_t temp_time = 0;
+#else
- MSVCRT___time32_t firstmontime = 0;
- MSVCRT___time32_t temp_time = 0;
+#endif
- struct MSVCRT_tm* tmp_mstm = 0;
- struct MSVCRT_tm* oldstate = 0;
- struct MSVCRT_tm* firstmontm = 0;
- int iso8601yr = -1;
- int iso8601wk = -1;
- char* tmp_str = 0;
- if(!(tmp_mstm = MSVCRT_malloc(sizeof(struct MSVCRT_tm))))
- return 0;
- if(msvcrt_get_thread_data()->time_buffer &&
- !(oldstate = MSVCRT_malloc(sizeof(struct
MSVCRT_tm)))) {
- MSVCRT_free(tmp_mstm);
- return 0;
- }
- if(oldstate)
- memcpy(oldstate, msvcrt_get_thread_data()->time_buffer,
- sizeof(struct MSVCRT_tm));
- memcpy(tmp_mstm, mstm, sizeof(struct MSVCRT_tm));
- temp_time = MSVCRT_mktime(tmp_mstm);
- MSVCRT_free(tmp_mstm);
- tmp_mstm = MSVCRT_gmtime(&temp_time);
- iso8601yr = tmp_mstm->tm_year + 1900;
- iso8601wk =
(tmp_mstm->tm_yday+1-(tmp_mstm->tm_wday==0?7:tmp_mstm->tm_wday)+10)/7;
- if(iso8601wk == 53) {
- if(!(firstmontm = MSVCRT_malloc(sizeof(struct
MSVCRT_tm)))) {
- MSVCRT_free(oldstate);
- return 0;
- }
- memset(firstmontm, '\0', sizeof(struct MSVCRT_tm));
- firstmontm->tm_mon = 0;
- firstmontm->tm_mday = 4;
- firstmontm->tm_year = tmp_mstm->tm_year+1;
- firstmontime = MSVCRT_mktime(firstmontm);
- MSVCRT_free(firstmontm);
- firstmontm = MSVCRT_gmtime(&firstmontime);
- firstmontime =
firstmontime-((firstmontm->tm_wday-1==-1?6:firstmontm->tm_wday-1)*86400);
- if(firstmontime <= temp_time)
- iso8601yr += 1;
- }
- if(iso8601wk == 0)
- iso8601yr -= 1;
- if(!(tmp_str = MSVCRT_malloc(6))) {
- MSVCRT_free(oldstate);
- return 0;
- }
- memset(tmp_str, '\0', 6);
- MSVCRT__snprintf(tmp_str, 6, "%d", iso8601yr);
- if(!strftime_str(str, &ret, max, tmp_str)) {
- if(oldstate) {
- memcpy(msvcrt_get_thread_data()->time_buffer,
oldstate,
- sizeof(struct MSVCRT_tm));
- MSVCRT_free(oldstate);
- }
- MSVCRT_free(tmp_str);
- return 0;
- }
- if(oldstate) {
- memcpy(msvcrt_get_thread_data()->time_buffer, oldstate,
- sizeof(struct MSVCRT_tm));
- MSVCRT_free(oldstate);
- }
- MSVCRT_free(tmp_str);
- }
- break;
It's not the simplest way of implementing it. There's no need for any allocations, the value returned by this format is in tm_year-1...tm_year range+1.
I think that the code should look like this: tmp = 1900 + mstm->tm_year; if (mstm->tm_yday - mstm->tm_wday + 4 < 0) tmp--; else if(mstm->tm_yday + mstm->tm_wday - X > 365 + IsLeapYear(tmp)) tmp++; if (!strftime_int(str, &ret, max, tmp, 4, 0, 9999)) return 0; break;
It also needs some tests.
Thanks, Piotr