GetDateFormat() and GetTimeFormat() should check the validity of the

time/date structure it is processing.
Add comments to describe flags behavior.
Add support for TIME_FORCE24HOURFORMAT, TIME_NOMINUTESORSECONDS,
TIME_NOSECONDS and TIME_NOTIMEMARKER.
Add check in GetDateFormatW() for invalid flag combinations.
Added some missing DATE_* defines to winnls.h.
Behavior verified against NT4.0.
This commit is contained in:
Chris Morgan 2002-12-19 21:11:54 +00:00 committed by Alexandre Julliard
parent 5f31b32946
commit af807287ca
2 changed files with 217 additions and 46 deletions

View File

@ -196,6 +196,10 @@ extern "C" {
#define DATE_USE_ALT_CALENDAR 0x00000004 /* use alternate calendar */ #define DATE_USE_ALT_CALENDAR 0x00000004 /* use alternate calendar */
/* alt. calendar support is broken anyway */ /* alt. calendar support is broken anyway */
#define DATE_YEARMONTH 0x00000008 /* use year/month */
#define DATE_LTRREADING 0x00000010 /* left to right reading order */
#define DATE_RTLREADING 0x00000020 /* right to left reading order */
#define TIME_FORCE24HOURFORMAT 0x00000008 /* force 24 hour format*/ #define TIME_FORCE24HOURFORMAT 0x00000008 /* force 24 hour format*/
#define TIME_NOTIMEMARKER 0x00000004 /* show no AM/PM */ #define TIME_NOTIMEMARKER 0x00000004 /* show no AM/PM */
#define TIME_NOSECONDS 0x00000002 /* show no seconds */ #define TIME_NOSECONDS 0x00000002 /* show no seconds */

View File

@ -1362,7 +1362,22 @@ INT WINAPI lstrcmpiW( LPCWSTR str1, LPCWSTR str2 )
'' used to quote literal characters '' used to quote literal characters
'' (within a quoted string) indicates a literal ' '' (within a quoted string) indicates a literal '
If TIME_NOMINUTESORSECONDS or TIME_NOSECONDS is specified, the function
removes the separator(s) preceding the minutes and/or seconds element(s).
If TIME_NOTIMEMARKER is specified, the function removes the separator(s)
preceding and following the time marker.
If TIME_FORCE24HOURFORMAT is specified, the function displays any existing
time marker, unless the TIME_NOTIMEMARKER flag is also set.
These functions REQUIRE valid locale, date, and format. These functions REQUIRE valid locale, date, and format.
If the time or date is invalid, return 0 and set ERROR_INVALID_PARAMETER
Return value is the number of characters written, or if outlen is zero
it is the number of characters required for the output including
the terminating null.
*/ */
static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags, static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
const SYSTEMTIME* xtime, const SYSTEMTIME* xtime,
@ -1370,6 +1385,12 @@ static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
LPWSTR output, INT outlen, int dateformat) LPWSTR output, INT outlen, int dateformat)
{ {
INT outpos; INT outpos;
INT lastFormatPos; /* the position in the output buffer of */
/* the end of the output from the last formatting */
/* character */
BOOL dropUntilNextFormattingChar = FALSE; /* TIME_NOTIMEMARKER drops
all of the text around the dropped marker,
eg. "h@!t@!m" becomes "hm" */
/* make a debug report */ /* 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), " TRACE("args: 0x%lx, 0x%lx, 0x%lx, time(d=%d,h=%d,m=%d,s=%d), fmt:%s (at %p), "
@ -1380,25 +1401,49 @@ static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
/* initialize state variables */ /* initialize state variables */
outpos = 0; outpos = 0;
lastFormatPos = 0;
while (*format) { while (*format) {
/* Literal string: Maybe terminated early by a \0 */ /* Literal string: Maybe terminated early by a \0 */
if (*format == (WCHAR) '\'') { if (*format == (WCHAR) '\'')
{
format++; format++;
while (*format) {
if (*format == (WCHAR) '\'') { /* We loop while we haven't reached the end of the format string */
/* and until we haven't found another "'" character */
while (*format)
{
/* we found what might be the close single quote mark */
/* we need to make sure there isn't another single quote */
/* after it, if there is we need to skip over this quote mark */
/* as the user is trying to put a single quote mark in their output */
if (*format == (WCHAR) '\'')
{
format++; format++;
if (*format != '\'') { if (*format != '\'')
{
break; /* It was a terminating quote */ break; /* It was a terminating quote */
} }
} }
/* if outlen is zero then we are couting the number of */
/* characters of space we need to output this text, don't */
/* modify the output buffer */
if (!outlen) if (!outlen)
/* We are counting */; {
else if (outpos >= outlen) outpos++; /* We are counting */;
} else if (outpos >= outlen)
{
goto too_short; goto too_short;
else } else
{
/* even drop literal strings */
if(!dropUntilNextFormattingChar)
{
output[outpos] = *format; output[outpos] = *format;
outpos++; outpos++;
}
}
format++; format++;
} }
} else if ( (dateformat && (*format=='d' || } else if ( (dateformat && (*format=='d' ||
@ -1409,15 +1454,28 @@ static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
*format=='h' || *format=='h' ||
*format=='m' || *format=='m' ||
*format=='s' || *format=='s' ||
*format=='t') ) ) { *format=='t') ) )
/* if processing a date and we have a date formatting character, OR */
/* if we are processing a time and we have a time formatting character */
{
int type, count; int type, count;
char tmp[16]; char tmp[16];
WCHAR buf[40]; WCHAR buf[40];
int buflen=0; int buflen=0;
type = *format; type = *format;
format++; format++;
/* clear out the drop text flag if we are in here */
dropUntilNextFormattingChar = FALSE;
/* count up the number of the same letter values in a row that */
/* we get, this lets us distinguish between "s" and "ss" and it */
/* eliminates the duplicate to simplify the below case statement */
for (count = 1; *format == type; format++) for (count = 1; *format == type; format++)
count++; count++;
buf[0] = 0; /* always null terminate the buffer */
switch(type) switch(type)
{ {
case 'd': case 'd':
@ -1471,38 +1529,72 @@ static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
break; break;
case 'h': case 'h':
/* fallthrough if we are forced to output in 24 hour format */
if(!(tflags & TIME_FORCE24HOURFORMAT))
{
/* hours 1:00-12:00 --- is this right? */ /* hours 1:00-12:00 --- is this right? */
sprintf( tmp, "%.*d", count > 2 ? 2 : count, (xtime->wHour-1)%12 +1); /* NOTE: 0000 hours is also 12 midnight */
sprintf( tmp, "%.*d", count > 2 ? 2 : count,
xtime->wHour == 0 ? 12 : (xtime->wHour-1)%12 +1);
MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
break; break;
}
case 'H': case 'H':
sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wHour ); sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wHour );
MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
break; break;
case 'm': case 'm':
/* if TIME_NOMINUTESORSECONDS don't display minutes */
if(!(tflags & TIME_NOMINUTESORSECONDS))
{
sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wMinute ); sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wMinute );
MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
} else
{
outpos = lastFormatPos;
}
break; break;
case 's': case 's':
/* if we have a TIME_NOSECONDS or TIME_NOMINUTESORSECONDS
flag then don't display seconds */
if(!(tflags & TIME_NOSECONDS) && !(tflags &
TIME_NOMINUTESORSECONDS))
{
sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wSecond ); sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wSecond );
MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) ); MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
} else
{
outpos = lastFormatPos;
}
break; break;
case 't': case 't':
if(!(tflags & TIME_NOTIMEMARKER))
{
GetLocaleInfoW(locale, (xtime->wHour < 12) ? GetLocaleInfoW(locale, (xtime->wHour < 12) ?
LOCALE_S1159 : LOCALE_S2359, LOCALE_S1159 : LOCALE_S2359,
buf, sizeof(buf) ); buf, sizeof(buf) );
if (count == 1) { if (count == 1)
{
buf[1] = 0; buf[1] = 0;
} }
} else
{
outpos = lastFormatPos; /* remove any prior text up until
the output due to formatting characters */
dropUntilNextFormattingChar = TRUE; /* drop everything
until we hit the next formatting character */
}
break; break;
} }
/* cat buf onto the output */ /* cat buf onto the output */
buflen = strlenW(buf); buflen = strlenW(buf);
/* we are counting how many characters we need for output */
/* don't modify the output buffer... */
if (!outlen) if (!outlen)
/* We are counting */; /* We are counting */;
else if (outpos + buflen < outlen) { else if (outpos + buflen < outlen) {
@ -1513,15 +1605,27 @@ static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
goto too_short; goto too_short;
} }
outpos += buflen; outpos += buflen;
} else { lastFormatPos = outpos; /* record the end of the formatting text we just output */
} else /* we are processing a NON formatting character */
{
/* a literal character */ /* a literal character */
if (!outlen) if (!outlen)
/* We are counting */; {
outpos++; /* We are counting */;
}
else if (outpos >= outlen) else if (outpos >= outlen)
{
goto too_short; goto too_short;
else }
else /* just copy the character into the output buffer */
{
/* unless we are dropping characters */
if(!dropUntilNextFormattingChar)
{
output[outpos] = *format; output[outpos] = *format;
outpos++; outpos++;
}
}
format++; format++;
} }
} }
@ -1533,7 +1637,8 @@ static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
goto too_short; goto too_short;
else else
output[outpos] = '\0'; output[outpos] = '\0';
outpos++;
outpos++; /* add one for the terminating null character */
TRACE(" returning %d %s\n", outpos, debugstr_w(output)); TRACE(" returning %d %s\n", outpos, debugstr_w(output));
return outpos; return outpos;
@ -1629,8 +1734,8 @@ INT WINAPI GetDateFormatW(LCID locale,DWORD flags,
TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",
locale,flags,xtime,debugstr_w(format),date,datelen); locale,flags,xtime,debugstr_w(format),date,datelen);
/* Tests (could be left until OLE_GetFormatW) */ /* Tests */
if (flags && format) if (flags && format) /* if lpFormat is non-null then flags must be zero */
{ {
SetLastError (ERROR_INVALID_FLAGS); SetLastError (ERROR_INVALID_FLAGS);
return 0; return 0;
@ -1640,26 +1745,62 @@ INT WINAPI GetDateFormatW(LCID locale,DWORD flags,
SetLastError (ERROR_INVALID_PARAMETER); SetLastError (ERROR_INVALID_PARAMETER);
return 0; return 0;
} }
if (!locale) { if (!locale)
{
locale = LOCALE_SYSTEM_DEFAULT; locale = LOCALE_SYSTEM_DEFAULT;
}; };
if (locale == LOCALE_SYSTEM_DEFAULT) { if (locale == LOCALE_SYSTEM_DEFAULT)
{
thislocale = GetSystemDefaultLCID(); thislocale = GetSystemDefaultLCID();
} else if (locale == LOCALE_USER_DEFAULT) { } else if (locale == LOCALE_USER_DEFAULT)
{
thislocale = GetUserDefaultLCID(); thislocale = GetUserDefaultLCID();
} else { } else
{
thislocale = locale; thislocale = locale;
}; };
if (xtime == NULL) { /* check for invalid flag combinations */
if((flags & DATE_LTRREADING) && (flags & DATE_RTLREADING))
{
SetLastError (ERROR_INVALID_FLAGS);
return 0;
}
/* DATE_SHORTDATE, DATE_LONGDATE and DATE_YEARMONTH are mutually */
/* exclusive */
if((flags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
&& !((flags & DATE_SHORTDATE) ^ (flags &
DATE_LONGDATE) ^ (flags & DATE_YEARMONTH)))
{
SetLastError (ERROR_INVALID_FLAGS);
return 0;
}
/* if the user didn't pass in a pointer to the current time we read it */
/* here */
if (xtime == NULL)
{
GetSystemTime(&t); GetSystemTime(&t);
} else { } else
{
/* NOTE: check here before we perform the SystemTimeToFileTime conversion */
/* because this conversion will fix invalid time values */
/* check to see if the time/date is valid */
/* set ERROR_INVALID_PARAMETER and return 0 if invalid */
if((xtime->wDay > 31) || (xtime->wDayOfWeek > 6) || (xtime->wMonth > 12))
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
/* Silently correct wDayOfWeek by transforming to FileTime and back again */ /* Silently correct wDayOfWeek by transforming to FileTime and back again */
res=SystemTimeToFileTime(xtime,&ft); res=SystemTimeToFileTime(xtime,&ft);
/* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER on error */ /* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER on error */
/*FIXME: SystemTimeToFileTime doesn't yet do that check */ if(!res)
if(!res) { {
SetLastError(ERROR_INVALID_PARAMETER); SetLastError(ERROR_INVALID_PARAMETER);
return 0; return 0;
} }
@ -1668,13 +1809,15 @@ INT WINAPI GetDateFormatW(LCID locale,DWORD flags,
}; };
thistime = &t; thistime = &t;
if (format == NULL) { if (format == NULL)
{
GetLocaleInfoW(thislocale, ((flags&DATE_LONGDATE) GetLocaleInfoW(thislocale, ((flags&DATE_LONGDATE)
? LOCALE_SLONGDATE ? LOCALE_SLONGDATE
: LOCALE_SSHORTDATE), : LOCALE_SSHORTDATE),
format_buf, sizeof(format_buf)/sizeof(*format_buf)); format_buf, sizeof(format_buf)/sizeof(*format_buf));
thisformat = format_buf; thisformat = format_buf;
} else { } else
{
thisformat = format; thisformat = format;
}; };
@ -2763,6 +2906,8 @@ GetTimeFormatA(LCID locale, /* [in] */
/****************************************************************************** /******************************************************************************
* GetTimeFormatW [KERNEL32.@] * GetTimeFormatW [KERNEL32.@]
* Makes a Unicode string of the time * Makes a Unicode string of the time
*
* NOTE: See OLE_GetFormatW() for further documentation
*/ */
INT WINAPI INT WINAPI
GetTimeFormatW(LCID locale, /* [in] */ GetTimeFormatW(LCID locale, /* [in] */
@ -2784,15 +2929,29 @@ GetTimeFormatW(LCID locale, /* [in] */
thislocale = OLE2NLS_CheckLocale ( locale ); thislocale = OLE2NLS_CheckLocale ( locale );
/* if the user didn't specify a format we use the default */
/* format for this locale */
if (format == NULL) if (format == NULL)
{ if (flags & LOCALE_NOUSEROVERRIDE) /* use system default */ {
{ thislocale = GetSystemDefaultLCID(); if (flags & LOCALE_NOUSEROVERRIDE) /* use system default */
{
thislocale = GetSystemDefaultLCID();
} }
GetLocaleInfoW(thislocale, thisflags, format_buf, 40); GetLocaleInfoW(thislocale, thisflags, format_buf, 40);
thisformat = format_buf; thisformat = format_buf;
} }
else else
{ thisformat = format; {
/* if non-null format and LOCALE_NOUSEROVERRIDE then fail */
/* NOTE: this could be either invalid flags or invalid parameter */
/* windows sets it to invalid flags */
if (flags & LOCALE_NOUSEROVERRIDE)
{
SetLastError(ERROR_INVALID_FLAGS);
return 0;
}
thisformat = format;
} }
if (xtime == NULL) /* NULL means use the current local time */ if (xtime == NULL) /* NULL means use the current local time */
@ -2800,7 +2959,15 @@ GetTimeFormatW(LCID locale, /* [in] */
thistime = &t; thistime = &t;
} }
else else
{ thistime = xtime; {
/* check time values */
if((xtime->wHour > 24) || (xtime->wMinute >= 60) || (xtime->wSecond >= 60))
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
thistime = xtime;
} }
ret = OLE_GetFormatW(thislocale, thisflags, flags, thistime, thisformat, ret = OLE_GetFormatW(thislocale, thisflags, flags, thistime, thisformat,