Bill Medland (medbi01@accpac.com) Fix OLE_GetFormatW so that GetDateFormatW works Rearranged for simplicity Index: wine/ole/ole2nls.c =================================================================== RCS file: /home/wine/wine/ole/ole2nls.c,v retrieving revision 1.105 diff -u -r1.105 ole2nls.c --- wine/ole/ole2nls.c 3 Oct 2002 22:57:55 -0000 1.105 +++ wine/ole/ole2nls.c 24 Oct 2002 03:34:12 -0000 @@ -1572,26 +1572,15 @@ /*************************************************************************** *** * OLE_GetFormatW [INTERNAL] + * + * dateformat is set TRUE if being called for a date, false for a time */ static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags, const SYSTEMTIME* xtime, LPCWSTR format, - LPWSTR output, INT outlen) + LPWSTR output, INT outlen, int dateformat) { - INT inpos, outpos; - int count, type=0, inquote; - int Overflow; /* loop check */ - char tmp[16]; - WCHAR buf[40]; - int buflen=0; - WCHAR arg0[] = {0}, arg1[] = {'%','d',0}; - WCHAR arg2[] = {'%','0','2','d',0}; - WCHAR *argarr[3]; - int datevars=0, timevars=0; - - argarr[0] = arg0; - argarr[1] = arg1; - argarr[2] = arg2; + INT outpos; /* make a debug report */ TRACE("args: 0x%lx, 0x%lx, 0x%lx, time(d=%d,h=%d,m=%d,s=%d), fmt:%s (at %p), " @@ -1600,55 +1589,50 @@ xtime->wDay, xtime->wHour, xtime->wMinute, xtime->wSecond, debugstr_w(format), format, output, outlen); - if(outlen == 0) { - FIXME("outlen = 0, returning 255\n"); - return 255; - } - /* initialize state variables */ - inpos = outpos = 0; - count = 0; - inquote = Overflow = 0; - /* this is really just a sanity check */ - output[0] = buf[0] = 0; - - /* this loop is the core of the function */ - for (inpos = 0; /* we have several break points */ ; inpos++) { - if (inquote) { - if (format[inpos] == (WCHAR) '\'') { - if (format[inpos+1] == '\'') { - inpos++; - output[outpos++] = '\''; - } else { - inquote = 0; - continue; - } - } else if (format[inpos] == 0) { - output[outpos++] = 0; - if (outpos > outlen) Overflow = 1; - break; /* normal exit (within a quote) */ - } else { - output[outpos++] = format[inpos]; /* copy input */ - if (outpos > outlen) { - Overflow = 1; - output[outpos-1] = 0; - break; - } - } - } else if ( (count && (format[inpos] != type)) - || ( (count==4 && type =='y') || - (count==4 && type =='M') || - (count==4 && type =='d') || - (count==2 && type =='g') || - (count==2 && type =='h') || - (count==2 && type =='H') || - (count==2 && type =='m') || - (count==2 && type =='s') || - (count==2 && type =='t') ) ) { - switch(type) - { + outpos = 0; + + while (*format) { + /* Literal string: Maybe terminated early by a \0 */ + if (*format == (WCHAR) '\'') { + format++; + while (*format) { + if (*format == (WCHAR) '\'') { + format++; + if (*format != '\'') { + break; /* It was a terminating quote */ + } + } + if (!outlen) + /* We are counting */; + else if (outpos >= outlen) + goto too_short; + else + output[outpos] = *format; + outpos++; + format++; + } + } else if ( (dateformat && (*format=='d' || + *format=='M' || + *format=='y' || + *format=='g') ) || + (!dateformat && (*format=='H' || + *format=='h' || + *format=='m' || + *format=='s' || + *format=='t') ) ) { + int type, count; + char tmp[16]; + WCHAR buf[40]; + int buflen=0; + type = *format; + format++; + for (count = 1; *format == type; format++) + count++; + switch(type) + { case 'd': - if (count == 4) { + if (count >= 4) { GetLocaleInfoW(locale, LOCALE_SDAYNAME1 + (xtime->wDayOfWeek +6)%7, buf, sizeof(buf)/sizeof(WCHAR) ); @@ -1664,7 +1648,7 @@ break; case 'M': - if (count == 4) { + if (count >= 4) { GetLocaleInfoW(locale, LOCALE_SMONTHNAME1 + xtime->wMonth -1, buf, sizeof(buf)/sizeof(WCHAR) ); @@ -1678,12 +1662,10 @@ } break; case 'y': - if (count == 4) { + if (count >= 4) { sprintf( tmp, "%d", xtime->wYear ); - } else if (count == 3) { - strcpy( tmp, "yyy" ); } else { - sprintf( tmp, "%.*d", count, xtime->wYear % 100 ); + sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wYear % 100 ); } MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); break; @@ -1701,22 +1683,22 @@ case 'h': /* hours 1:00-12:00 --- is this right? */ - sprintf( tmp, "%.*d", count, (xtime->wHour-1)%12 +1); + sprintf( tmp, "%.*d", count > 2 ? 2 : count, (xtime->wHour-1)%12 +1); MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); break; case 'H': - sprintf( tmp, "%.*d", count, xtime->wHour ); + sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wHour ); MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); break; case 'm': - sprintf( tmp, "%.*d", count, xtime->wMinute ); + sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wMinute ); MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); break; case 's': - sprintf( tmp, "%.*d", count, xtime->wSecond ); + sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wSecond ); MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); break; @@ -1728,71 +1710,49 @@ buf[1] = 0; } break; - } - - /* no matter what happened, we need to check this next - character the next time we loop through */ - inpos--; + } /* cat buf onto the output */ - outlen = strlenW(buf); - if (outpos + buflen < outlen) { + buflen = strlenW(buf); + if (!outlen) + /* We are counting */; + else if (outpos + buflen < outlen) { strcpyW( output + outpos, buf ); - outpos += buflen; } else { lstrcpynW( output + outpos, buf, outlen - outpos ); - Overflow = 1; - break; /* Abnormal exit */ + /* Is this an undocumented feature we are supporting? */ + goto too_short; } - - /* reset the variables we used this time */ - count = 0; - type = '\0'; - } else if (format[inpos] == 0) { - /* we can't check for this at the beginning, because that - would keep us from printing a format spec that ended the - string */ - output[outpos] = 0; - break; /* NORMAL EXIT */ - } else if (count) { - /* how we keep track of the middle of a format spec */ - count++; - continue; - } else if ( (datevars && (format[inpos]=='d' || - format[inpos]=='M' || - format[inpos]=='y' || - format[inpos]=='g') ) || - (timevars && (format[inpos]=='H' || - format[inpos]=='h' || - format[inpos]=='m' || - format[inpos]=='s' || - format[inpos]=='t') ) ) { - type = format[inpos]; - count = 1; - continue; - } else if (format[inpos] == '\'') { - inquote = 1; - continue; + outpos += buflen; } else { - /* unquoted literals */ - output[outpos++] = format[inpos]; + /* a literal character */ + if (!outlen) + /* We are counting */; + else if (outpos >= outlen) + goto too_short; + else + output[outpos] = *format; + outpos++; + format++; } } - if (Overflow) { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - WARN(" buffer overflow\n"); - }; - /* final string terminator and sanity check */ + if (!outlen) + /* We are counting */; + else if (outpos >= outlen) + goto too_short; + else + output[outpos] = '\0'; outpos++; - if (outpos > outlen-1) outpos = outlen-1; - output[outpos] = '0'; - TRACE(" returning %s\n", debugstr_w(output)); - - return (!Overflow) ? outlen : 0; + TRACE(" returning %d %s\n", outpos, debugstr_w(output)); + return outpos; +too_short: + SetLastError(ERROR_INSUFFICIENT_BUFFER); + WARN(" buffer overflow\n"); + return 0; } @@ -1910,6 +1870,17 @@ TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", locale,flags,xtime,debugstr_w(format),date,datelen); + /* Tests (could be left until OLE_GetFormatW) */ + if (flags && format) + { + SetLastError (ERROR_INVALID_FLAGS); + return 0; + } + if (datelen && !date) + { + SetLastError (ERROR_INVALID_PARAMETER); + return 0; + } if (!locale) { locale = LOCALE_SYSTEM_DEFAULT; }; @@ -1950,7 +1921,7 @@ ret = OLE_GetFormatW(thislocale, flags, 0, thistime, thisformat, - date, datelen); + date, datelen, 1); TRACE("GetDateFormatW() returning %d, with data=%s\n", @@ -3067,7 +3038,7 @@ } ret = OLE_GetFormatW(thislocale, thisflags, flags, thistime, thisformat, - timestr, timelen); + timestr, timelen, 0); return ret; } Index: wine/dlls/kernel/tests/locale.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/tests/locale.c,v retrieving revision 1.4 diff -u -r1.4 locale.c --- wine/dlls/kernel/tests/locale.c 4 Jun 2002 22:12:50 -0000 1.4 +++ wine/dlls/kernel/tests/locale.c 24 Oct 2002 03:34:12 -0000 @@ -30,6 +30,7 @@ #define BUFFER_SIZE 50 /* Buffer used by callback function */ char GlobalBuffer[BUFFER_SIZE]; +#define COUNTOF(x) (sizeof(x)/sizeof(x)[0]) /* TODO : * Unicode versions @@ -183,6 +184,58 @@ eq (ret, 0, "GetDateFormat with len=2", "%d"); } +void TestGetDateFormatW() +{ + int ret, error, cmp; + SYSTEMTIME curtime; + WCHAR buffer[BUFFER_SIZE], format[BUFFER_SIZE], Expected[BUFFER_SIZE]; + LCID lcid; + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT ); + + /* 1. Error cases */ + + /* 1a If flags is not zero then format must be null. */ + ret = GetDateFormatW (LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, NULL, format, buffer, sizeof(buffer)/sizeof(buffer[0])); + error = ret ? 0 : GetLastError(); + ok (ret == 0 && error == ERROR_INVALID_FLAGS, "GetDateFormatW allowed flags and format"); + + /* 1b The buffer can only be null if the count is zero */ + /* For the record other bad pointers result in a page fault (Win98) */ + ret = GetDateFormatW (LOCALE_SYSTEM_DEFAULT, 0, NULL, format, NULL, sizeof(buffer)/sizeof(buffer[0])); + error = ret ? 0 : GetLastError(); + ok (ret == 0 && error == ERROR_INVALID_PARAMETER, "GetDateFormatW did not detect null buffer pointer."); + ret = GetDateFormatW (LOCALE_SYSTEM_DEFAULT, 0, NULL, format, NULL, 0); + error = ret ? 0 : GetLastError(); + ok (ret != 0 && error == 0, "GetDateFormatW did not permit null buffer pointer when counting."); + + /* 1c An incorrect day of week is corrected. */ + curtime.wYear = 2002; + curtime.wMonth = 10; + curtime.wDay = 23; + curtime.wDayOfWeek = 5; /* should be 3 - Wednesday */ + curtime.wHour = 0; + curtime.wMinute = 0; + curtime.wSecond = 0; + curtime.wMilliseconds = 234; + MultiByteToWideChar (CP_ACP, 0, "dddd d MMMM yyyy", -1, format, COUNTOF(format)); + ret = GetDateFormatW (lcid, 0, &curtime, format, buffer, COUNTOF(buffer)); + error = ret ? 0 : GetLastError(); + MultiByteToWideChar (CP_ACP, 0, "Wednesday 23 October 2002", -1, Expected, COUNTOF(Expected)); + cmp = ret ? lstrcmpW (buffer, Expected) : 2; + ok (ret == lstrlenW(Expected)+1 && error == 0 && cmp == 0, "Day of week correction failed\n"); + + /* 1d Invalid year, month or day results in error */ + + /* 1e Insufficient space results in error */ + + /* 2. Standard behaviour */ + /* 1c is a reasonable test */ + + /* 3. Replicated undocumented behaviour */ + /* e.g. unexepected characters are retained. */ +} + void TestGetCurrencyFormat() { @@ -343,6 +396,7 @@ TestGetLocaleInfoA(); TestGetTimeFormatA(); TestGetDateFormatA(); + TestGetDateFormatW(); TestGetNumberFormat(); TestGetCurrencyFormat(); TestCompareStringA();