~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/kernel32/lcformat.c

Version: ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2  * Locale-dependent format handling
  3  *
  4  * Copyright 1995 Martin von Loewis
  5  * Copyright 1998 David Lee Lambert
  6  * Copyright 2000 Julio César Gázquez
  7  * Copyright 2003 Jon Griffiths
  8  * Copyright 2005 Dmitry Timoshkov
  9  *
 10  * This library is free software; you can redistribute it and/or
 11  * modify it under the terms of the GNU Lesser General Public
 12  * License as published by the Free Software Foundation; either
 13  * version 2.1 of the License, or (at your option) any later version.
 14  *
 15  * This library is distributed in the hope that it will be useful,
 16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18  * Lesser General Public License for more details.
 19  *
 20  * You should have received a copy of the GNU Lesser General Public
 21  * License along with this library; if not, write to the Free Software
 22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 23  */
 24 
 25 #include "config.h"
 26 #include "wine/port.h"
 27 
 28 #include <string.h>
 29 #include <stdarg.h>
 30 #include <stdio.h>
 31 #include <stdlib.h>
 32 
 33 #include "windef.h"
 34 #include "winbase.h"
 35 #include "wine/unicode.h"
 36 #include "wine/debug.h"
 37 #include "winternl.h"
 38 
 39 #include "kernel_private.h"
 40 
 41 WINE_DEFAULT_DEBUG_CHANNEL(nls);
 42 
 43 #define DATE_DATEVARSONLY 0x0100  /* only date stuff: yMdg */
 44 #define TIME_TIMEVARSONLY 0x0200  /* only time stuff: hHmst */
 45 
 46 /* Since calculating the formatting data for each locale is time-consuming,
 47  * we get the format data for each locale only once and cache it in memory.
 48  * We cache both the system default and user overridden data, after converting
 49  * them into the formats that the functions here expect. Since these functions
 50  * will typically be called with only a small number of the total locales
 51  * installed, the memory overhead is minimal while the speedup is significant.
 52  *
 53  * Our cache takes the form of a singly linked list, whose node is below:
 54  */
 55 #define NLS_NUM_CACHED_STRINGS 45
 56 
 57 typedef struct _NLS_FORMAT_NODE
 58 {
 59   LCID  lcid;         /* Locale Id */
 60   DWORD dwFlags;      /* 0 or LOCALE_NOUSEROVERRIDE */
 61   DWORD dwCodePage;   /* Default code page (if LOCALE_USE_ANSI_CP not given) */
 62   NUMBERFMTW   fmt;   /* Default format for numbers */
 63   CURRENCYFMTW cyfmt; /* Default format for currencies */
 64   LPWSTR lppszStrings[NLS_NUM_CACHED_STRINGS]; /* Default formats,day/month names */
 65   WCHAR szShortAM[2]; /* Short 'AM' marker */
 66   WCHAR szShortPM[2]; /* Short 'PM' marker */
 67   struct _NLS_FORMAT_NODE *next;
 68 } NLS_FORMAT_NODE;
 69 
 70 /* Macros to get particular data strings from a format node */
 71 #define GetNegative(fmt)  fmt->lppszStrings[0]
 72 #define GetLongDate(fmt)  fmt->lppszStrings[1]
 73 #define GetShortDate(fmt) fmt->lppszStrings[2]
 74 #define GetTime(fmt)      fmt->lppszStrings[3]
 75 #define GetAM(fmt)        fmt->lppszStrings[42]
 76 #define GetPM(fmt)        fmt->lppszStrings[43]
 77 #define GetYearMonth(fmt) fmt->lppszStrings[44]
 78 
 79 #define GetLongDay(fmt,day)    fmt->lppszStrings[4 + day]
 80 #define GetShortDay(fmt,day)   fmt->lppszStrings[11 + day]
 81 #define GetLongMonth(fmt,mth)  fmt->lppszStrings[18 + mth]
 82 #define GetShortMonth(fmt,mth) fmt->lppszStrings[30 + mth]
 83 
 84 /* Write access to the cache is protected by this critical section */
 85 static CRITICAL_SECTION NLS_FormatsCS;
 86 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug =
 87 {
 88     0, 0, &NLS_FormatsCS,
 89     { &NLS_FormatsCS_debug.ProcessLocksList,
 90       &NLS_FormatsCS_debug.ProcessLocksList },
 91       0, 0, { (DWORD_PTR)(__FILE__ ": NLS_Formats") }
 92 };
 93 static CRITICAL_SECTION NLS_FormatsCS = { &NLS_FormatsCS_debug, -1, 0, 0, 0, 0 };
 94 
 95 /**************************************************************************
 96  * NLS_GetLocaleNumber <internal>
 97  *
 98  * Get a numeric locale format value.
 99  */
100 static DWORD NLS_GetLocaleNumber(LCID lcid, DWORD dwFlags)
101 {
102   WCHAR szBuff[80];
103   DWORD dwVal = 0;
104 
105   szBuff[0] = '\0';
106   GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
107 
108   if (szBuff[0] && szBuff[1] == ';' && szBuff[2] != '')
109     dwVal = (szBuff[0] - '') * 10 + (szBuff[2] - '');
110   else
111   {
112     const WCHAR* iter = szBuff;
113     dwVal = 0;
114     while(*iter >= '' && *iter <= '9')
115       dwVal = dwVal * 10 + (*iter++ - '');
116   }
117   return dwVal;
118 }
119 
120 /**************************************************************************
121  * NLS_GetLocaleString <internal>
122  *
123  * Get a string locale format value.
124  */
125 static WCHAR* NLS_GetLocaleString(LCID lcid, DWORD dwFlags)
126 {
127   WCHAR szBuff[80], *str;
128   DWORD dwLen;
129 
130   szBuff[0] = '\0';
131   GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
132   dwLen = strlenW(szBuff) + 1;
133   str = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
134   if (str)
135     memcpy(str, szBuff, dwLen * sizeof(WCHAR));
136   return str;
137 }
138 
139 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
140   TRACE( #type ": %d (%08x)\n", (DWORD)num, (DWORD)num)
141 
142 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
143   TRACE( #type ": %s\n", debugstr_w(str))
144 
145 /**************************************************************************
146  * NLS_GetFormats <internal>
147  *
148  * Calculate (and cache) the number formats for a locale.
149  */
150 static const NLS_FORMAT_NODE *NLS_GetFormats(LCID lcid, DWORD dwFlags)
151 {
152   /* GetLocaleInfo() identifiers for cached formatting strings */
153   static const USHORT NLS_LocaleIndices[] = {
154     LOCALE_SNEGATIVESIGN,
155     LOCALE_SLONGDATE,   LOCALE_SSHORTDATE,
156     LOCALE_STIMEFORMAT,
157     LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
158     LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
159     LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
160     LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
161     LOCALE_SABBREVDAYNAME7,
162     LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
163     LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
164     LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
165     LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
166     LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
167     LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
168     LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
169     LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
170     LOCALE_S1159, LOCALE_S2359,
171     LOCALE_SYEARMONTH
172   };
173   static NLS_FORMAT_NODE *NLS_CachedFormats = NULL;
174   NLS_FORMAT_NODE *node = NLS_CachedFormats;
175 
176   dwFlags &= LOCALE_NOUSEROVERRIDE;
177 
178   TRACE("(0x%04x,0x%08x)\n", lcid, dwFlags);
179 
180   /* See if we have already cached the locales number format */
181   while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
182     node = node->next;
183 
184   if (!node || node->lcid != lcid || node->dwFlags != dwFlags)
185   {
186     NLS_FORMAT_NODE *new_node;
187     DWORD i;
188 
189     TRACE("Creating new cache entry\n");
190 
191     if (!(new_node = HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE))))
192       return NULL;
193 
194     GET_LOCALE_NUMBER(new_node->dwCodePage, LOCALE_IDEFAULTANSICODEPAGE);
195 
196     /* Number Format */
197     new_node->lcid = lcid;
198     new_node->dwFlags = dwFlags;
199     new_node->next = NULL;
200 
201     GET_LOCALE_NUMBER(new_node->fmt.NumDigits, LOCALE_IDIGITS);
202     GET_LOCALE_NUMBER(new_node->fmt.LeadingZero, LOCALE_ILZERO);
203     GET_LOCALE_NUMBER(new_node->fmt.NegativeOrder, LOCALE_INEGNUMBER);
204 
205     GET_LOCALE_NUMBER(new_node->fmt.Grouping, LOCALE_SGROUPING);
206     if (new_node->fmt.Grouping > 9 && new_node->fmt.Grouping != 32)
207     {
208       WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
209            new_node->fmt.Grouping);
210       new_node->fmt.Grouping = 0;
211     }
212 
213     GET_LOCALE_STRING(new_node->fmt.lpDecimalSep, LOCALE_SDECIMAL);
214     GET_LOCALE_STRING(new_node->fmt.lpThousandSep, LOCALE_STHOUSAND);
215 
216     /* Currency Format */
217     new_node->cyfmt.NumDigits = new_node->fmt.NumDigits;
218     new_node->cyfmt.LeadingZero = new_node->fmt.LeadingZero;
219 
220     GET_LOCALE_NUMBER(new_node->cyfmt.Grouping, LOCALE_SGROUPING);
221 
222     if (new_node->cyfmt.Grouping > 9)
223     {
224       WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
225            new_node->cyfmt.Grouping);
226       new_node->cyfmt.Grouping = 0;
227     }
228 
229     GET_LOCALE_NUMBER(new_node->cyfmt.NegativeOrder, LOCALE_INEGCURR);
230     if (new_node->cyfmt.NegativeOrder > 15)
231     {
232       WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
233            new_node->cyfmt.NegativeOrder);
234       new_node->cyfmt.NegativeOrder = 0;
235     }
236     GET_LOCALE_NUMBER(new_node->cyfmt.PositiveOrder, LOCALE_ICURRENCY);
237     if (new_node->cyfmt.PositiveOrder > 3)
238     {
239       WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
240            new_node->cyfmt.PositiveOrder);
241       new_node->cyfmt.PositiveOrder = 0;
242     }
243     GET_LOCALE_STRING(new_node->cyfmt.lpDecimalSep, LOCALE_SMONDECIMALSEP);
244     GET_LOCALE_STRING(new_node->cyfmt.lpThousandSep, LOCALE_SMONTHOUSANDSEP);
245     GET_LOCALE_STRING(new_node->cyfmt.lpCurrencySymbol, LOCALE_SCURRENCY);
246 
247     /* Date/Time Format info, negative character, etc */
248     for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
249     {
250       GET_LOCALE_STRING(new_node->lppszStrings[i], NLS_LocaleIndices[i]);
251     }
252     new_node->szShortAM[0] = GetAM(new_node)[0]; new_node->szShortAM[1] = '\0';
253     new_node->szShortPM[0] = GetPM(new_node)[0]; new_node->szShortPM[1] = '\0';
254 
255     /* Now add the computed format to the cache */
256     RtlEnterCriticalSection(&NLS_FormatsCS);
257 
258     /* Search again: We may have raced to add the node */
259     node = NLS_CachedFormats;
260     while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
261       node = node->next;
262 
263     if (!node)
264     {
265       node = NLS_CachedFormats = new_node; /* Empty list */
266       new_node = NULL;
267     }
268     else if (node->lcid != lcid || node->dwFlags != dwFlags)
269     {
270       node->next = new_node; /* Not in the list, add to end */
271       node = new_node;
272       new_node = NULL;
273     }
274 
275     RtlLeaveCriticalSection(&NLS_FormatsCS);
276 
277     if (new_node)
278     {
279       /* We raced and lost: The node was already added by another thread.
280        * node points to the currently cached node, so free new_node.
281        */
282       for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
283         HeapFree(GetProcessHeap(), 0, new_node->lppszStrings[i]);
284       HeapFree(GetProcessHeap(), 0, new_node->fmt.lpDecimalSep);
285       HeapFree(GetProcessHeap(), 0, new_node->fmt.lpThousandSep);
286       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpDecimalSep);
287       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpThousandSep);
288       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpCurrencySymbol);
289       HeapFree(GetProcessHeap(), 0, new_node);
290     }
291   }
292   return node;
293 }
294 
295 /**************************************************************************
296  * NLS_IsUnicodeOnlyLcid <internal>
297  *
298  * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
299  */
300 BOOL NLS_IsUnicodeOnlyLcid(LCID lcid)
301 {
302   lcid = ConvertDefaultLocale(lcid);
303 
304   switch (PRIMARYLANGID(lcid))
305   {
306   case LANG_ARMENIAN:
307   case LANG_DIVEHI:
308   case LANG_GEORGIAN:
309   case LANG_GUJARATI:
310   case LANG_HINDI:
311   case LANG_KANNADA:
312   case LANG_KONKANI:
313   case LANG_MARATHI:
314   case LANG_PUNJABI:
315   case LANG_SANSKRIT:
316     TRACE("lcid 0x%08x: langid 0x%4x is Unicode Only\n", lcid, PRIMARYLANGID(lcid));
317     return TRUE;
318   default:
319     return FALSE;
320   }
321 }
322 
323 /*
324  * Formatting of dates, times, numbers and currencies.
325  */
326 
327 #define IsLiteralMarker(p) (p == '\'')
328 #define IsDateFmtChar(p)   (p == 'd'||p == 'M'||p == 'y'||p == 'g')
329 #define IsTimeFmtChar(p)   (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
330 
331 /* Only the following flags can be given if a date/time format is specified */
332 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY)
333 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
334                            TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
335                            TIME_NOTIMEMARKER)
336 
337 /******************************************************************************
338  * NLS_GetDateTimeFormatW <internal>
339  *
340  * Performs the formatting for GetDateFormatW/GetTimeFormatW.
341  *
342  * FIXME
343  * DATE_USE_ALT_CALENDAR           - Requires GetCalendarInfo to work first.
344  * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
345  */
346 static INT NLS_GetDateTimeFormatW(LCID lcid, DWORD dwFlags,
347                                   const SYSTEMTIME* lpTime, LPCWSTR lpFormat,
348                                   LPWSTR lpStr, INT cchOut)
349 {
350   const NLS_FORMAT_NODE *node;
351   SYSTEMTIME st;
352   INT cchWritten = 0;
353   INT lastFormatPos = 0;
354   BOOL bSkipping = FALSE; /* Skipping text around marker? */
355 
356   /* Verify our arguments */
357   if ((cchOut && !lpStr) || !(node = NLS_GetFormats(lcid, dwFlags)))
358   {
359 NLS_GetDateTimeFormatW_InvalidParameter:
360     SetLastError(ERROR_INVALID_PARAMETER);
361     return 0;
362   }
363 
364   if (dwFlags & ~(DATE_DATEVARSONLY|TIME_TIMEVARSONLY))
365   {
366     if (lpFormat &&
367         ((dwFlags & DATE_DATEVARSONLY && dwFlags & ~DATE_FORMAT_FLAGS) ||
368          (dwFlags & TIME_TIMEVARSONLY && dwFlags & ~TIME_FORMAT_FLAGS)))
369     {
370 NLS_GetDateTimeFormatW_InvalidFlags:
371       SetLastError(ERROR_INVALID_FLAGS);
372       return 0;
373     }
374 
375     if (dwFlags & DATE_DATEVARSONLY)
376     {
377       if ((dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) == (DATE_LTRREADING|DATE_RTLREADING))
378         goto NLS_GetDateTimeFormatW_InvalidFlags;
379       else if (dwFlags & (DATE_LTRREADING|DATE_RTLREADING))
380         FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
381 
382       switch (dwFlags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
383       {
384       case 0:
385         break;
386       case DATE_SHORTDATE:
387       case DATE_LONGDATE:
388       case DATE_YEARMONTH:
389         if (lpFormat)
390           goto NLS_GetDateTimeFormatW_InvalidFlags;
391         break;
392       default:
393         goto NLS_GetDateTimeFormatW_InvalidFlags;
394       }
395     }
396   }
397 
398   if (!lpFormat)
399   {
400     /* Use the appropriate default format */
401     if (dwFlags & DATE_DATEVARSONLY)
402     {
403       if (dwFlags & DATE_YEARMONTH)
404         lpFormat = GetYearMonth(node);
405       else if (dwFlags & DATE_LONGDATE)
406         lpFormat = GetLongDate(node);
407       else
408         lpFormat = GetShortDate(node);
409     }
410     else
411       lpFormat = GetTime(node);
412   }
413 
414   if (!lpTime)
415   {
416     GetLocalTime(&st); /* Default to current time */
417     lpTime = &st;
418   }
419   else
420   {
421     if (dwFlags & DATE_DATEVARSONLY)
422     {
423       FILETIME ftTmp;
424 
425       /* Verify the date and correct the D.O.W. if needed */
426       memset(&st, 0, sizeof(st));
427       st.wYear = lpTime->wYear;
428       st.wMonth = lpTime->wMonth;
429       st.wDay = lpTime->wDay;
430 
431       if (st.wDay > 31 || st.wMonth > 12 || !SystemTimeToFileTime(&st, &ftTmp))
432         goto NLS_GetDateTimeFormatW_InvalidParameter;
433 
434       FileTimeToSystemTime(&ftTmp, &st);
435       lpTime = &st;
436     }
437 
438     if (dwFlags & TIME_TIMEVARSONLY)
439     {
440       /* Verify the time */
441       if (lpTime->wHour > 24 || lpTime->wMinute > 59 || lpTime->wSecond > 59)
442         goto NLS_GetDateTimeFormatW_InvalidParameter;
443     }
444   }
445 
446   /* Format the output */
447   while (*lpFormat)
448   {
449     if (IsLiteralMarker(*lpFormat))
450     {
451       /* Start of a literal string */
452       lpFormat++;
453 
454       /* Loop until the end of the literal marker or end of the string */
455       while (*lpFormat)
456       {
457         if (IsLiteralMarker(*lpFormat))
458         {
459           lpFormat++;
460           if (!IsLiteralMarker(*lpFormat))
461             break; /* Terminating literal marker */
462         }
463 
464         if (!cchOut)
465           cchWritten++;   /* Count size only */
466         else if (cchWritten >= cchOut)
467           goto NLS_GetDateTimeFormatW_Overrun;
468         else if (!bSkipping)
469         {
470           lpStr[cchWritten] = *lpFormat;
471           cchWritten++;
472         }
473         lpFormat++;
474       }
475     }
476     else if ((dwFlags & DATE_DATEVARSONLY && IsDateFmtChar(*lpFormat)) ||
477              (dwFlags & TIME_TIMEVARSONLY && IsTimeFmtChar(*lpFormat)))
478     {
479       char  buffA[32];
480       WCHAR buff[32], fmtChar;
481       LPCWSTR szAdd = NULL;
482       DWORD dwVal = 0;
483       int   count = 0, dwLen;
484 
485       bSkipping = FALSE;
486 
487       fmtChar = *lpFormat;
488       while (*lpFormat == fmtChar)
489       {
490         count++;
491         lpFormat++;
492       }
493       buff[0] = '\0';
494 
495       switch(fmtChar)
496       {
497       case 'd':
498         if (count >= 4)
499           szAdd = GetLongDay(node, (lpTime->wDayOfWeek + 6) % 7);
500         else if (count == 3)
501           szAdd = GetShortDay(node, (lpTime->wDayOfWeek + 6) % 7);
502         else
503         {
504           dwVal = lpTime->wDay;
505           szAdd = buff;
506         }
507         break;
508 
509       case 'M':
510         if (count >= 4)
511           szAdd = GetLongMonth(node, lpTime->wMonth - 1);
512         else if (count == 3)
513           szAdd = GetShortMonth(node, lpTime->wMonth - 1);
514         else
515         {
516           dwVal = lpTime->wMonth;
517           szAdd = buff;
518         }
519         break;
520 
521       case 'y':
522         if (count >= 4)
523         {
524           count = 4;
525           dwVal = lpTime->wYear;
526         }
527         else
528         {
529           count = count > 2 ? 2 : count;
530           dwVal = lpTime->wYear % 100;
531         }
532         szAdd = buff;
533         break;
534 
535       case 'g':
536         if (count == 2)
537         {
538           /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
539            *        When it is fixed, this string should be cached in 'node'.
540            */
541           FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
542           buff[0] = 'A'; buff[1] = 'D'; buff[2] = '\0';
543         }
544         else
545         {
546           buff[0] = 'g'; buff[1] = '\0'; /* Add a literal 'g' */
547         }
548         szAdd = buff;
549         break;
550 
551       case 'h':
552         if (!(dwFlags & TIME_FORCE24HOURFORMAT))
553         {
554           count = count > 2 ? 2 : count;
555           dwVal = lpTime->wHour == 0 ? 12 : (lpTime->wHour - 1) % 12 + 1;
556           szAdd = buff;
557           break;
558         }
559         /* .. fall through if we are forced to output in 24 hour format */
560 
561       case 'H':
562         count = count > 2 ? 2 : count;
563         dwVal = lpTime->wHour;
564         szAdd = buff;
565         break;
566 
567       case 'm':
568         if (dwFlags & TIME_NOMINUTESORSECONDS)
569         {
570           cchWritten = lastFormatPos; /* Skip */
571           bSkipping = TRUE;
572         }
573         else
574         {
575           count = count > 2 ? 2 : count;
576           dwVal = lpTime->wMinute;
577           szAdd = buff;
578         }
579         break;
580 
581       case 's':
582         if (dwFlags & (TIME_NOSECONDS|TIME_NOMINUTESORSECONDS))
583         {
584           cchWritten = lastFormatPos; /* Skip */
585           bSkipping = TRUE;
586         }
587         else
588         {
589           count = count > 2 ? 2 : count;
590           dwVal = lpTime->wSecond;
591           szAdd = buff;
592         }
593         break;
594 
595       case 't':
596         if (dwFlags & TIME_NOTIMEMARKER)
597         {
598           cchWritten = lastFormatPos; /* Skip */
599           bSkipping = TRUE;
600         }
601         else
602         {
603           if (count == 1)
604             szAdd = lpTime->wHour < 12 ? node->szShortAM : node->szShortPM;
605           else
606             szAdd = lpTime->wHour < 12 ? GetAM(node) : GetPM(node);
607         }
608         break;
609       }
610 
611       if (szAdd == buff && buff[0] == '\0')
612       {
613         /* We have a numeric value to add */
614         sprintf(buffA, "%.*d", count, dwVal);
615         MultiByteToWideChar(CP_ACP, 0, buffA, -1, buff, sizeof(buff)/sizeof(WCHAR));
616       }
617 
618       dwLen = szAdd ? strlenW(szAdd) : 0;
619 
620       if (cchOut && dwLen)
621       {
622         if (cchWritten + dwLen < cchOut)
623           memcpy(lpStr + cchWritten, szAdd, dwLen * sizeof(WCHAR));
624         else
625         {
626           memcpy(lpStr + cchWritten, szAdd, (cchOut - cchWritten) * sizeof(WCHAR));
627           goto NLS_GetDateTimeFormatW_Overrun;
628         }
629       }
630       cchWritten += dwLen;
631       lastFormatPos = cchWritten; /* Save position of last output format text */
632     }
633     else
634     {
635       /* Literal character */
636       if (!cchOut)
637         cchWritten++;   /* Count size only */
638       else if (cchWritten >= cchOut)
639         goto NLS_GetDateTimeFormatW_Overrun;
640       else if (!bSkipping || *lpFormat == ' ')
641       {
642         lpStr[cchWritten] = *lpFormat;
643         cchWritten++;
644       }
645       lpFormat++;
646     }
647   }
648 
649   /* Final string terminator and sanity check */
650   if (cchOut)
651   {
652    if (cchWritten >= cchOut)
653      goto NLS_GetDateTimeFormatW_Overrun;
654    else
655      lpStr[cchWritten] = '\0';
656   }
657   cchWritten++; /* Include terminating NUL */
658 
659   TRACE("returning length=%d, ouput=%s\n", cchWritten, debugstr_w(lpStr));
660   return cchWritten;
661 
662 NLS_GetDateTimeFormatW_Overrun:
663   TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
664   SetLastError(ERROR_INSUFFICIENT_BUFFER);
665   return 0;
666 }
667 
668 /******************************************************************************
669  * NLS_GetDateTimeFormatA <internal>
670  *
671  * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
672  */
673 static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags,
674                                   const SYSTEMTIME* lpTime,
675                                   LPCSTR lpFormat, LPSTR lpStr, INT cchOut)
676 {
677   DWORD cp = CP_ACP;
678   WCHAR szFormat[128], szOut[128];
679   INT iRet;
680 
681   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
682         debugstr_a(lpFormat), lpStr, cchOut);
683 
684   if (NLS_IsUnicodeOnlyLcid(lcid))
685   {
686 GetDateTimeFormatA_InvalidParameter:
687     SetLastError(ERROR_INVALID_PARAMETER);
688     return 0;
689   }
690 
691   if (!(dwFlags & LOCALE_USE_CP_ACP))
692   {
693     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
694     if (!node)
695       goto GetDateTimeFormatA_InvalidParameter;
696     cp = node->dwCodePage;
697   }
698 
699   if (lpFormat)
700     MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, sizeof(szFormat)/sizeof(WCHAR));
701 
702   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
703     cchOut = sizeof(szOut)/sizeof(WCHAR);
704 
705   szOut[0] = '\0';
706 
707   iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL,
708                                 lpStr ? szOut : NULL, cchOut);
709 
710   if (lpStr)
711   {
712     if (szOut[0])
713       WideCharToMultiByte(cp, 0, szOut, iRet ? -1 : cchOut, lpStr, cchOut, 0, 0);
714     else if (cchOut && iRet)
715       *lpStr = '\0';
716   }
717   return iRet;
718 }
719 
720 /******************************************************************************
721  * GetDateFormatA [KERNEL32.@]
722  *
723  * Format a date for a given locale.
724  *
725  * PARAMS
726  *  lcid      [I] Locale to format for
727  *  dwFlags   [I] LOCALE_ and DATE_ flags from "winnls.h"
728  *  lpTime    [I] Date to format
729  *  lpFormat  [I] Format string, or NULL to use the system defaults
730  *  lpDateStr [O] Destination for formatted string
731  *  cchOut    [I] Size of lpDateStr, or 0 to calculate the resulting size
732  *
733  * NOTES
734  *  - If lpFormat is NULL, lpDateStr will be formatted according to the format
735  *    details returned by GetLocaleInfoA() and modified by dwFlags.
736  *  - lpFormat is a string of characters and formatting tokens. Any characters
737  *    in the string are copied verbatim to lpDateStr, with tokens being replaced
738  *    by the date values they represent.
739  *  - The following tokens have special meanings in a date format string:
740  *|  Token  Meaning
741  *|  -----  -------
742  *|  d      Single digit day of the month (no leading 0)
743  *|  dd     Double digit day of the month
744  *|  ddd    Short name for the day of the week
745  *|  dddd   Long name for the day of the week
746  *|  M      Single digit month of the year (no leading 0)
747  *|  MM     Double digit month of the year
748  *|  MMM    Short name for the month of the year
749  *|  MMMM   Long name for the month of the year
750  *|  y      Double digit year number (no leading 0)
751  *|  yy     Double digit year number
752  *|  yyyy   Four digit year number
753  *|  gg     Era string, for example 'AD'.
754  *  - To output any literal character that could be misidentified as a token,
755  *    enclose it in single quotes.
756  *  - The Ascii version of this function fails if lcid is Unicode only.
757  *
758  * RETURNS
759  *  Success: The number of character written to lpDateStr, or that would
760  *           have been written, if cchOut is 0.
761  *  Failure: 0. Use GetLastError() to determine the cause.
762  */
763 INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
764                            LPCSTR lpFormat, LPSTR lpDateStr, INT cchOut)
765 {
766   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
767         debugstr_a(lpFormat), lpDateStr, cchOut);
768 
769   return NLS_GetDateTimeFormatA(lcid, dwFlags | DATE_DATEVARSONLY, lpTime,
770                                 lpFormat, lpDateStr, cchOut);
771 }
772 
773 
774 /******************************************************************************
775  * GetDateFormatW       [KERNEL32.@]
776  *
777  * See GetDateFormatA.
778  */
779 INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
780                           LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut)
781 {
782   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
783         debugstr_w(lpFormat), lpDateStr, cchOut);
784 
785   return NLS_GetDateTimeFormatW(lcid, dwFlags|DATE_DATEVARSONLY, lpTime,
786                                 lpFormat, lpDateStr, cchOut);
787 }
788 
789 /******************************************************************************
790  *              GetTimeFormatA  [KERNEL32.@]
791  *
792  * Format a time for a given locale.
793  *
794  * PARAMS
795  *  lcid      [I] Locale to format for
796  *  dwFlags   [I] LOCALE_ and TIME_ flags from "winnls.h"
797  *  lpTime    [I] Time to format
798  *  lpFormat  [I] Formatting overrides
799  *  lpTimeStr [O] Destination for formatted string
800  *  cchOut    [I] Size of lpTimeStr, or 0 to calculate the resulting size
801  *
802  * NOTES
803  *  - If lpFormat is NULL, lpszValue will be formatted according to the format
804  *    details returned by GetLocaleInfoA() and modified by dwFlags.
805  *  - lpFormat is a string of characters and formatting tokens. Any characters
806  *    in the string are copied verbatim to lpTimeStr, with tokens being replaced
807  *    by the time values they represent.
808  *  - The following tokens have special meanings in a time format string:
809  *|  Token  Meaning
810  *|  -----  -------
811  *|  h      Hours with no leading zero (12-hour clock)
812  *|  hh     Hours with full two digits (12-hour clock)
813  *|  H      Hours with no leading zero (24-hour clock)
814  *|  HH     Hours with full two digits (24-hour clock)
815  *|  m      Minutes with no leading zero
816  *|  mm     Minutes with full two digits
817  *|  s      Seconds with no leading zero
818  *|  ss     Seconds with full two digits
819  *|  t      Short time marker (e.g. "A" or "P")
820  *|  tt     Long time marker (e.g. "AM", "PM")
821  *  - To output any literal character that could be misidentified as a token,
822  *    enclose it in single quotes.
823  *  - The Ascii version of this function fails if lcid is Unicode only.
824  *
825  * RETURNS
826  *  Success: The number of character written to lpTimeStr, or that would
827  *           have been written, if cchOut is 0.
828  *  Failure: 0. Use GetLastError() to determine the cause.
829  */
830 INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
831                           LPCSTR lpFormat, LPSTR lpTimeStr, INT cchOut)
832 {
833   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
834         debugstr_a(lpFormat), lpTimeStr, cchOut);
835 
836   return NLS_GetDateTimeFormatA(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
837                                 lpFormat, lpTimeStr, cchOut);
838 }
839 
840 /******************************************************************************
841  *              GetTimeFormatW  [KERNEL32.@]
842  *
843  * See GetTimeFormatA.
844  */
845 INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
846                           LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut)
847 {
848   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
849         debugstr_w(lpFormat), lpTimeStr, cchOut);
850 
851   return NLS_GetDateTimeFormatW(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
852                                 lpFormat, lpTimeStr, cchOut);
853 }
854 
855 /**************************************************************************
856  *              GetNumberFormatA        (KERNEL32.@)
857  *
858  * Format a number string for a given locale.
859  *
860  * PARAMS
861  *  lcid        [I] Locale to format for
862  *  dwFlags     [I] LOCALE_ flags from "winnls.h"
863  *  lpszValue   [I] String to format
864  *  lpFormat    [I] Formatting overrides
865  *  lpNumberStr [O] Destination for formatted string
866  *  cchOut      [I] Size of lpNumberStr, or 0 to calculate the resulting size
867  *
868  * NOTES
869  *  - lpszValue can contain only '' - '9', '-' and '.'.
870  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
871  *    be formatted according to the format details returned by GetLocaleInfoA().
872  *  - This function rounds the number string if the number of decimals exceeds the
873  *    locales normal number of decimal places.
874  *  - If cchOut is 0, this function does not write to lpNumberStr.
875  *  - The Ascii version of this function fails if lcid is Unicode only.
876  *
877  * RETURNS
878  *  Success: The number of character written to lpNumberStr, or that would
879  *           have been written, if cchOut is 0.
880  *  Failure: 0. Use GetLastError() to determine the cause.
881  */
882 INT WINAPI GetNumberFormatA(LCID lcid, DWORD dwFlags,
883                             LPCSTR lpszValue,  const NUMBERFMTA *lpFormat,
884                             LPSTR lpNumberStr, int cchOut)
885 {
886   DWORD cp = CP_ACP;
887   WCHAR szDec[8], szGrp[8], szIn[128], szOut[128];
888   NUMBERFMTW fmt;
889   const NUMBERFMTW *pfmt = NULL;
890   INT iRet;
891 
892   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
893         lpFormat, lpNumberStr, cchOut);
894 
895   if (NLS_IsUnicodeOnlyLcid(lcid))
896   {
897 GetNumberFormatA_InvalidParameter:
898     SetLastError(ERROR_INVALID_PARAMETER);
899     return 0;
900   }
901 
902   if (!(dwFlags & LOCALE_USE_CP_ACP))
903   {
904     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
905     if (!node)
906       goto GetNumberFormatA_InvalidParameter;
907     cp = node->dwCodePage;
908   }
909 
910   if (lpFormat)
911   {
912     memcpy(&fmt, lpFormat, sizeof(fmt));
913     pfmt = &fmt;
914     if (lpFormat->lpDecimalSep)
915     {
916       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
917       fmt.lpDecimalSep = szDec;