ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

lcformat.c
Go to the documentation of this file.
00001 /*
00002  * Locale-dependent format handling
00003  *
00004  * Copyright 1995 Martin von Loewis
00005  * Copyright 1998 David Lee Lambert
00006  * Copyright 2000 Julio César Gázquez
00007  * Copyright 2003 Jon Griffiths
00008  * Copyright 2005 Dmitry Timoshkov
00009  *
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Lesser General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2.1 of the License, or (at your option) any later version.
00014  *
00015  * This library is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Lesser General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU Lesser General Public
00021  * License along with this library; if not, write to the Free Software
00022  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00023  */
00024 
00025 //#include "config.h"
00026 //#include "wine/port.h"
00027 
00028 #include <string.h>
00029 #include <stdarg.h>
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 
00033 #include "windef.h"
00034 #include "winbase.h"
00035 #include "wine/unicode.h"
00036 #include "wine/debug.h"
00037 #include "winternl.h"
00038 
00039 #define CRITICAL_SECTION RTL_CRITICAL_SECTION
00040 #define CRITICAL_SECTION_DEBUG RTL_CRITICAL_SECTION_DEBUG
00041 #define CALINFO_MAX_YEAR 2029
00042 
00043 #define HeapAlloc RtlAllocateHeap
00044 #define HeapReAlloc RtlReAllocateHeap
00045 #define HeapFree RtlFreeHeap
00046 
00047 WINE_DEFAULT_DEBUG_CHANNEL(nls);
00048 
00049 #define DATE_DATEVARSONLY 0x0100  /* only date stuff: yMdg */
00050 #define TIME_TIMEVARSONLY 0x0200  /* only time stuff: hHmst */
00051 
00052 /* Since calculating the formatting data for each locale is time-consuming,
00053  * we get the format data for each locale only once and cache it in memory.
00054  * We cache both the system default and user overridden data, after converting
00055  * them into the formats that the functions here expect. Since these functions
00056  * will typically be called with only a small number of the total locales
00057  * installed, the memory overhead is minimal while the speedup is significant.
00058  *
00059  * Our cache takes the form of a singly linked list, whose node is below:
00060  */
00061 #define NLS_NUM_CACHED_STRINGS 57
00062 
00063 typedef struct _NLS_FORMAT_NODE
00064 {
00065   LCID  lcid;         /* Locale Id */
00066   DWORD dwFlags;      /* 0 or LOCALE_NOUSEROVERRIDE */
00067   DWORD dwCodePage;   /* Default code page (if LOCALE_USE_ANSI_CP not given) */
00068   NUMBERFMTW   fmt;   /* Default format for numbers */
00069   CURRENCYFMTW cyfmt; /* Default format for currencies */
00070   LPWSTR lppszStrings[NLS_NUM_CACHED_STRINGS]; /* Default formats,day/month names */
00071   WCHAR szShortAM[2]; /* Short 'AM' marker */
00072   WCHAR szShortPM[2]; /* Short 'PM' marker */
00073   struct _NLS_FORMAT_NODE *next;
00074 } NLS_FORMAT_NODE;
00075 
00076 /* Macros to get particular data strings from a format node */
00077 #define GetNegative(fmt)  fmt->lppszStrings[0]
00078 #define GetLongDate(fmt)  fmt->lppszStrings[1]
00079 #define GetShortDate(fmt) fmt->lppszStrings[2]
00080 #define GetTime(fmt)      fmt->lppszStrings[3]
00081 #define GetAM(fmt)        fmt->lppszStrings[54]
00082 #define GetPM(fmt)        fmt->lppszStrings[55]
00083 #define GetYearMonth(fmt) fmt->lppszStrings[56]
00084 
00085 #define GetLongDay(fmt,day)       fmt->lppszStrings[4 + day]
00086 #define GetShortDay(fmt,day)      fmt->lppszStrings[11 + day]
00087 #define GetLongMonth(fmt,mth)     fmt->lppszStrings[18 + mth]
00088 #define GetGenitiveMonth(fmt,mth) fmt->lppszStrings[30 + mth]
00089 #define GetShortMonth(fmt,mth)    fmt->lppszStrings[42 + mth]
00090 
00091 /* Write access to the cache is protected by this critical section */
00092 static CRITICAL_SECTION NLS_FormatsCS;
00093 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug =
00094 {
00095     0, 0, &NLS_FormatsCS,
00096     { &NLS_FormatsCS_debug.ProcessLocksList,
00097       &NLS_FormatsCS_debug.ProcessLocksList },
00098       0, 0, 0
00099 };
00100 static CRITICAL_SECTION NLS_FormatsCS = { &NLS_FormatsCS_debug, -1, 0, 0, 0, 0 };
00101 
00102 /**************************************************************************
00103  * NLS_GetLocaleNumber <internal>
00104  *
00105  * Get a numeric locale format value.
00106  */
00107 static DWORD NLS_GetLocaleNumber(LCID lcid, DWORD dwFlags)
00108 {
00109   WCHAR szBuff[80];
00110   DWORD dwVal = 0;
00111 
00112   szBuff[0] = '\0';
00113   GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
00114 
00115   if (szBuff[0] && szBuff[1] == ';' && szBuff[2] != '0')
00116     dwVal = (szBuff[0] - '0') * 10 + (szBuff[2] - '0');
00117   else
00118   {
00119     const WCHAR* iter = szBuff;
00120     dwVal = 0;
00121     while(*iter >= '0' && *iter <= '9')
00122       dwVal = dwVal * 10 + (*iter++ - '0');
00123   }
00124   return dwVal;
00125 }
00126 
00127 /**************************************************************************
00128  * NLS_GetLocaleString <internal>
00129  *
00130  * Get a string locale format value.
00131  */
00132 static WCHAR* NLS_GetLocaleString(LCID lcid, DWORD dwFlags)
00133 {
00134   WCHAR szBuff[80], *str;
00135   DWORD dwLen;
00136 
00137   szBuff[0] = '\0';
00138   GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
00139   dwLen = strlenW(szBuff) + 1;
00140   str = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
00141   if (str)
00142     memcpy(str, szBuff, dwLen * sizeof(WCHAR));
00143   return str;
00144 }
00145 
00146 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
00147   TRACE( #type ": %d (%08x)\n", (DWORD)num, (DWORD)num)
00148 
00149 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
00150   TRACE( #type ": %s\n", debugstr_w(str))
00151 
00152 /**************************************************************************
00153  * NLS_GetFormats <internal>
00154  *
00155  * Calculate (and cache) the number formats for a locale.
00156  */
00157 static const NLS_FORMAT_NODE *NLS_GetFormats(LCID lcid, DWORD dwFlags)
00158 {
00159   /* GetLocaleInfo() identifiers for cached formatting strings */
00160   static const LCTYPE NLS_LocaleIndices[] = {
00161     LOCALE_SNEGATIVESIGN,
00162     LOCALE_SLONGDATE,   LOCALE_SSHORTDATE,
00163     LOCALE_STIMEFORMAT,
00164     LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
00165     LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
00166     LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
00167     LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
00168     LOCALE_SABBREVDAYNAME7,
00169     LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
00170     LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
00171     LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
00172     LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
00173     LOCALE_SMONTHNAME1  | LOCALE_RETURN_GENITIVE_NAMES,
00174     LOCALE_SMONTHNAME2  | LOCALE_RETURN_GENITIVE_NAMES,
00175     LOCALE_SMONTHNAME3  | LOCALE_RETURN_GENITIVE_NAMES,
00176     LOCALE_SMONTHNAME4  | LOCALE_RETURN_GENITIVE_NAMES,
00177     LOCALE_SMONTHNAME5  | LOCALE_RETURN_GENITIVE_NAMES,
00178     LOCALE_SMONTHNAME6  | LOCALE_RETURN_GENITIVE_NAMES,
00179     LOCALE_SMONTHNAME7  | LOCALE_RETURN_GENITIVE_NAMES,
00180     LOCALE_SMONTHNAME8  | LOCALE_RETURN_GENITIVE_NAMES,
00181     LOCALE_SMONTHNAME9  | LOCALE_RETURN_GENITIVE_NAMES,
00182     LOCALE_SMONTHNAME10 | LOCALE_RETURN_GENITIVE_NAMES,
00183     LOCALE_SMONTHNAME11 | LOCALE_RETURN_GENITIVE_NAMES,
00184     LOCALE_SMONTHNAME12 | LOCALE_RETURN_GENITIVE_NAMES,
00185     LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
00186     LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
00187     LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
00188     LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
00189     LOCALE_S1159, LOCALE_S2359,
00190     LOCALE_SYEARMONTH
00191   };
00192   static NLS_FORMAT_NODE *NLS_CachedFormats = NULL;
00193   NLS_FORMAT_NODE *node = NLS_CachedFormats;
00194 
00195   dwFlags &= LOCALE_NOUSEROVERRIDE;
00196 
00197   TRACE("(0x%04x,0x%08x)\n", lcid, dwFlags);
00198 
00199   /* See if we have already cached the locales number format */
00200   while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
00201     node = node->next;
00202 
00203   if (!node || node->lcid != lcid || node->dwFlags != dwFlags)
00204   {
00205     NLS_FORMAT_NODE *new_node;
00206     DWORD i;
00207 
00208     TRACE("Creating new cache entry\n");
00209 
00210     if (!(new_node = HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE))))
00211       return NULL;
00212 
00213     GET_LOCALE_NUMBER(new_node->dwCodePage, LOCALE_IDEFAULTANSICODEPAGE);
00214 
00215     /* Number Format */
00216     new_node->lcid = lcid;
00217     new_node->dwFlags = dwFlags;
00218     new_node->next = NULL;
00219 
00220     GET_LOCALE_NUMBER(new_node->fmt.NumDigits, LOCALE_IDIGITS);
00221     GET_LOCALE_NUMBER(new_node->fmt.LeadingZero, LOCALE_ILZERO);
00222     GET_LOCALE_NUMBER(new_node->fmt.NegativeOrder, LOCALE_INEGNUMBER);
00223 
00224     GET_LOCALE_NUMBER(new_node->fmt.Grouping, LOCALE_SGROUPING);
00225     if (new_node->fmt.Grouping > 9 && new_node->fmt.Grouping != 32)
00226     {
00227       WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
00228            new_node->fmt.Grouping);
00229       new_node->fmt.Grouping = 0;
00230     }
00231 
00232     GET_LOCALE_STRING(new_node->fmt.lpDecimalSep, LOCALE_SDECIMAL);
00233     GET_LOCALE_STRING(new_node->fmt.lpThousandSep, LOCALE_STHOUSAND);
00234 
00235     /* Currency Format */
00236     new_node->cyfmt.NumDigits = new_node->fmt.NumDigits;
00237     new_node->cyfmt.LeadingZero = new_node->fmt.LeadingZero;
00238 
00239     GET_LOCALE_NUMBER(new_node->cyfmt.Grouping, LOCALE_SGROUPING);
00240 
00241     if (new_node->cyfmt.Grouping > 9)
00242     {
00243       WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
00244            new_node->cyfmt.Grouping);
00245       new_node->cyfmt.Grouping = 0;
00246     }
00247 
00248     GET_LOCALE_NUMBER(new_node->cyfmt.NegativeOrder, LOCALE_INEGCURR);
00249     if (new_node->cyfmt.NegativeOrder > 15)
00250     {
00251       WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
00252            new_node->cyfmt.NegativeOrder);
00253       new_node->cyfmt.NegativeOrder = 0;
00254     }
00255     GET_LOCALE_NUMBER(new_node->cyfmt.PositiveOrder, LOCALE_ICURRENCY);
00256     if (new_node->cyfmt.PositiveOrder > 3)
00257     {
00258       WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
00259            new_node->cyfmt.PositiveOrder);
00260       new_node->cyfmt.PositiveOrder = 0;
00261     }
00262     GET_LOCALE_STRING(new_node->cyfmt.lpDecimalSep, LOCALE_SMONDECIMALSEP);
00263     GET_LOCALE_STRING(new_node->cyfmt.lpThousandSep, LOCALE_SMONTHOUSANDSEP);
00264     GET_LOCALE_STRING(new_node->cyfmt.lpCurrencySymbol, LOCALE_SCURRENCY);
00265 
00266     /* Date/Time Format info, negative character, etc */
00267     for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
00268     {
00269       GET_LOCALE_STRING(new_node->lppszStrings[i], NLS_LocaleIndices[i]);
00270     }
00271     /* Save some memory if month genitive name is the same or not present */
00272     for (i = 0; i < 12; i++)
00273     {
00274       if (strcmpW(GetLongMonth(new_node, i), GetGenitiveMonth(new_node, i)) == 0)
00275       {
00276         HeapFree(GetProcessHeap(), 0, GetGenitiveMonth(new_node, i));
00277         GetGenitiveMonth(new_node, i) = NULL;
00278       }
00279     }
00280 
00281     new_node->szShortAM[0] = GetAM(new_node)[0]; new_node->szShortAM[1] = '\0';
00282     new_node->szShortPM[0] = GetPM(new_node)[0]; new_node->szShortPM[1] = '\0';
00283 
00284     /* Now add the computed format to the cache */
00285     RtlEnterCriticalSection(&NLS_FormatsCS);
00286 
00287     /* Search again: We may have raced to add the node */
00288     node = NLS_CachedFormats;
00289     while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
00290       node = node->next;
00291 
00292     if (!node)
00293     {
00294       node = NLS_CachedFormats = new_node; /* Empty list */
00295       new_node = NULL;
00296     }
00297     else if (node->lcid != lcid || node->dwFlags != dwFlags)
00298     {
00299       node->next = new_node; /* Not in the list, add to end */
00300       node = new_node;
00301       new_node = NULL;
00302     }
00303 
00304     RtlLeaveCriticalSection(&NLS_FormatsCS);
00305 
00306     if (new_node)
00307     {
00308       /* We raced and lost: The node was already added by another thread.
00309        * node points to the currently cached node, so free new_node.
00310        */
00311       for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
00312         HeapFree(GetProcessHeap(), 0, new_node->lppszStrings[i]);
00313       HeapFree(GetProcessHeap(), 0, new_node->fmt.lpDecimalSep);
00314       HeapFree(GetProcessHeap(), 0, new_node->fmt.lpThousandSep);
00315       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpDecimalSep);
00316       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpThousandSep);
00317       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpCurrencySymbol);
00318       HeapFree(GetProcessHeap(), 0, new_node);
00319     }
00320   }
00321   return node;
00322 }
00323 
00324 /**************************************************************************
00325  * NLS_IsUnicodeOnlyLcid <internal>
00326  *
00327  * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
00328  */
00329 BOOL NLS_IsUnicodeOnlyLcid(LCID lcid)
00330 {
00331   lcid = ConvertDefaultLocale(lcid);
00332 
00333   switch (PRIMARYLANGID(lcid))
00334   {
00335   case LANG_ARMENIAN:
00336   case LANG_DIVEHI:
00337   case LANG_GEORGIAN:
00338   case LANG_GUJARATI:
00339   case LANG_HINDI:
00340   case LANG_KANNADA:
00341   case LANG_KONKANI:
00342   case LANG_MARATHI:
00343   case LANG_PUNJABI:
00344   case LANG_SANSKRIT:
00345     TRACE("lcid 0x%08x: langid 0x%4x is Unicode Only\n", lcid, PRIMARYLANGID(lcid));
00346     return TRUE;
00347   default:
00348     return FALSE;
00349   }
00350 }
00351 
00352 /*
00353  * Formatting of dates, times, numbers and currencies.
00354  */
00355 
00356 #define IsLiteralMarker(p) (p == '\'')
00357 #define IsDateFmtChar(p)   (p == 'd'||p == 'M'||p == 'y'||p == 'g')
00358 #define IsTimeFmtChar(p)   (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
00359 
00360 /* Only the following flags can be given if a date/time format is specified */
00361 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY)
00362 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
00363                            TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
00364                            TIME_NOTIMEMARKER)
00365 
00366 /******************************************************************************
00367  * NLS_GetDateTimeFormatW <internal>
00368  *
00369  * Performs the formatting for GetDateFormatW/GetTimeFormatW.
00370  *
00371  * FIXME
00372  * DATE_USE_ALT_CALENDAR           - Requires GetCalendarInfo to work first.
00373  * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
00374  */
00375 static INT NLS_GetDateTimeFormatW(LCID lcid, DWORD dwFlags,
00376                                   const SYSTEMTIME* lpTime, LPCWSTR lpFormat,
00377                                   LPWSTR lpStr, INT cchOut)
00378 {
00379   const NLS_FORMAT_NODE *node;
00380   SYSTEMTIME st;
00381   INT cchWritten = 0;
00382   INT lastFormatPos = 0;
00383   BOOL bSkipping = FALSE; /* Skipping text around marker? */
00384   BOOL d_dd_formatted = FALSE; /* previous formatted part was for d or dd */
00385 
00386   /* Verify our arguments */
00387   if ((cchOut && !lpStr) || !(node = NLS_GetFormats(lcid, dwFlags)))
00388     goto invalid_parameter;
00389 
00390   if (dwFlags & ~(DATE_DATEVARSONLY|TIME_TIMEVARSONLY))
00391   {
00392     if (lpFormat &&
00393         ((dwFlags & DATE_DATEVARSONLY && dwFlags & ~DATE_FORMAT_FLAGS) ||
00394          (dwFlags & TIME_TIMEVARSONLY && dwFlags & ~TIME_FORMAT_FLAGS)))
00395     {
00396       goto invalid_flags;
00397     }
00398 
00399     if (dwFlags & DATE_DATEVARSONLY)
00400     {
00401       if ((dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) == (DATE_LTRREADING|DATE_RTLREADING))
00402         goto invalid_flags;
00403       else if (dwFlags & (DATE_LTRREADING|DATE_RTLREADING))
00404         FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
00405 
00406       switch (dwFlags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
00407       {
00408       case 0:
00409         break;
00410       case DATE_SHORTDATE:
00411       case DATE_LONGDATE:
00412       case DATE_YEARMONTH:
00413         if (lpFormat)
00414           goto invalid_flags;
00415         break;
00416       default:
00417         goto invalid_flags;
00418       }
00419     }
00420   }
00421 
00422   if (!lpFormat)
00423   {
00424     /* Use the appropriate default format */
00425     if (dwFlags & DATE_DATEVARSONLY)
00426     {
00427       if (dwFlags & DATE_YEARMONTH)
00428         lpFormat = GetYearMonth(node);
00429       else if (dwFlags & DATE_LONGDATE)
00430         lpFormat = GetLongDate(node);
00431       else
00432         lpFormat = GetShortDate(node);
00433     }
00434     else
00435       lpFormat = GetTime(node);
00436   }
00437 
00438   if (!lpTime)
00439   {
00440     GetLocalTime(&st); /* Default to current time */
00441     lpTime = &st;
00442   }
00443   else
00444   {
00445     if (dwFlags & DATE_DATEVARSONLY)
00446     {
00447       FILETIME ftTmp;
00448 
00449       /* Verify the date and correct the D.O.W. if needed */
00450       memset(&st, 0, sizeof(st));
00451       st.wYear = lpTime->wYear;
00452       st.wMonth = lpTime->wMonth;
00453       st.wDay = lpTime->wDay;
00454 
00455       if (st.wDay > 31 || st.wMonth > 12 || !SystemTimeToFileTime(&st, &ftTmp))
00456         goto invalid_parameter;
00457 
00458       FileTimeToSystemTime(&ftTmp, &st);
00459       lpTime = &st;
00460     }
00461 
00462     if (dwFlags & TIME_TIMEVARSONLY)
00463     {
00464       /* Verify the time */
00465       if (lpTime->wHour > 24 || lpTime->wMinute > 59 || lpTime->wSecond > 59)
00466         goto invalid_parameter;
00467     }
00468   }
00469 
00470   /* Format the output */
00471   while (*lpFormat)
00472   {
00473     if (IsLiteralMarker(*lpFormat))
00474     {
00475       /* Start of a literal string */
00476       lpFormat++;
00477 
00478       /* Loop until the end of the literal marker or end of the string */
00479       while (*lpFormat)
00480       {
00481         if (IsLiteralMarker(*lpFormat))
00482         {
00483           lpFormat++;
00484           if (!IsLiteralMarker(*lpFormat))
00485             break; /* Terminating literal marker */
00486         }
00487 
00488         if (!cchOut)
00489           cchWritten++;   /* Count size only */
00490         else if (cchWritten >= cchOut)
00491           goto overrun;
00492         else if (!bSkipping)
00493         {
00494           lpStr[cchWritten] = *lpFormat;
00495           cchWritten++;
00496         }
00497         lpFormat++;
00498       }
00499     }
00500     else if ((dwFlags & DATE_DATEVARSONLY && IsDateFmtChar(*lpFormat)) ||
00501              (dwFlags & TIME_TIMEVARSONLY && IsTimeFmtChar(*lpFormat)))
00502     {
00503       WCHAR buff[32], fmtChar;
00504       LPCWSTR szAdd = NULL;
00505       DWORD dwVal = 0;
00506       int   count = 0, dwLen;
00507 
00508       bSkipping = FALSE;
00509 
00510       fmtChar = *lpFormat;
00511       while (*lpFormat == fmtChar)
00512       {
00513         count++;
00514         lpFormat++;
00515       }
00516       buff[0] = '\0';
00517 
00518       if (fmtChar != 'M') d_dd_formatted = FALSE;
00519       switch(fmtChar)
00520       {
00521       case 'd':
00522         if (count >= 4)
00523           szAdd = GetLongDay(node, (lpTime->wDayOfWeek + 6) % 7);
00524         else if (count == 3)
00525           szAdd = GetShortDay(node, (lpTime->wDayOfWeek + 6) % 7);
00526         else
00527         {
00528           dwVal = lpTime->wDay;
00529           szAdd = buff;
00530           d_dd_formatted = TRUE;
00531         }
00532         break;
00533 
00534       case 'M':
00535         if (count >= 4)
00536         {
00537           LPCWSTR genitive = GetGenitiveMonth(node, lpTime->wMonth - 1);
00538           if (genitive)
00539           {
00540             if (d_dd_formatted)
00541             {
00542               szAdd = genitive;
00543               break;
00544             }
00545             else
00546             {
00547               LPCWSTR format = lpFormat;
00548               /* Look forward now, if next format pattern is for day genitive
00549                  name should be used */
00550               while (*format)
00551               {
00552                 /* Skip parts within markers */
00553                 if (IsLiteralMarker(*format))
00554                 {
00555                   ++format;
00556                   while (*format)
00557                   {
00558                     if (IsLiteralMarker(*format))
00559                     {
00560                       ++format;
00561                       if (!IsLiteralMarker(*format)) break;
00562                     }
00563                   }
00564                 }
00565                 if (*format != ' ') break;
00566                 ++format;
00567               }
00568               /* Only numeric day form matters */
00569               if (*format && *format == 'd')
00570               {
00571                 INT dcount = 1;
00572                 while (*++format == 'd') dcount++;
00573                 if (dcount < 3)
00574                 {
00575                   szAdd = genitive;
00576                   break;
00577                 }
00578               }
00579             }
00580           }
00581           szAdd = GetLongMonth(node, lpTime->wMonth - 1);
00582         }
00583         else if (count == 3)
00584           szAdd = GetShortMonth(node, lpTime->wMonth - 1);
00585         else
00586         {
00587           dwVal = lpTime->wMonth;
00588           szAdd = buff;
00589         }
00590         break;
00591 
00592       case 'y':
00593         if (count >= 4)
00594         {
00595           count = 4;
00596           dwVal = lpTime->wYear;
00597         }
00598         else
00599         {
00600           count = count > 2 ? 2 : count;
00601           dwVal = lpTime->wYear % 100;
00602         }
00603         szAdd = buff;
00604         break;
00605 
00606       case 'g':
00607         if (count == 2)
00608         {
00609           /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
00610            *        When it is fixed, this string should be cached in 'node'.
00611            */
00612           FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
00613           buff[0] = 'A'; buff[1] = 'D'; buff[2] = '\0';
00614         }
00615         else
00616         {
00617           buff[0] = 'g'; buff[1] = '\0'; /* Add a literal 'g' */
00618         }
00619         szAdd = buff;
00620         break;
00621 
00622       case 'h':
00623         if (!(dwFlags & TIME_FORCE24HOURFORMAT))
00624         {
00625           count = count > 2 ? 2 : count;
00626           dwVal = lpTime->wHour == 0 ? 12 : (lpTime->wHour - 1) % 12 + 1;
00627           szAdd = buff;
00628           break;
00629         }
00630         /* .. fall through if we are forced to output in 24 hour format */
00631 
00632       case 'H':
00633         count = count > 2 ? 2 : count;
00634         dwVal = lpTime->wHour;
00635         szAdd = buff;
00636         break;
00637 
00638       case 'm':
00639         if (dwFlags & TIME_NOMINUTESORSECONDS)
00640         {
00641           cchWritten = lastFormatPos; /* Skip */
00642           bSkipping = TRUE;
00643         }
00644         else
00645         {
00646           count = count > 2 ? 2 : count;
00647           dwVal = lpTime->wMinute;
00648           szAdd = buff;
00649         }
00650         break;
00651 
00652       case 's':
00653         if (dwFlags & (TIME_NOSECONDS|TIME_NOMINUTESORSECONDS))
00654         {
00655           cchWritten = lastFormatPos; /* Skip */
00656           bSkipping = TRUE;
00657         }
00658         else
00659         {
00660           count = count > 2 ? 2 : count;
00661           dwVal = lpTime->wSecond;
00662           szAdd = buff;
00663         }
00664         break;
00665 
00666       case 't':
00667         if (dwFlags & TIME_NOTIMEMARKER)
00668         {
00669           cchWritten = lastFormatPos; /* Skip */
00670           bSkipping = TRUE;
00671         }
00672         else
00673         {
00674           if (count == 1)
00675             szAdd = lpTime->wHour < 12 ? node->szShortAM : node->szShortPM;
00676           else
00677             szAdd = lpTime->wHour < 12 ? GetAM(node) : GetPM(node);
00678         }
00679         break;
00680       }
00681 
00682       if (szAdd == buff && buff[0] == '\0')
00683       {
00684         static const WCHAR fmtW[] = {'%','.','*','d',0};
00685         /* We have a numeric value to add */
00686         snprintfW(buff, sizeof(buff)/sizeof(WCHAR), fmtW, count, dwVal);
00687       }
00688 
00689       dwLen = szAdd ? strlenW(szAdd) : 0;
00690 
00691       if (cchOut && dwLen)
00692       {
00693         if (cchWritten + dwLen < cchOut)
00694           memcpy(lpStr + cchWritten, szAdd, dwLen * sizeof(WCHAR));
00695         else
00696         {
00697           memcpy(lpStr + cchWritten, szAdd, (cchOut - cchWritten) * sizeof(WCHAR));
00698           goto overrun;
00699         }
00700       }
00701       cchWritten += dwLen;
00702       lastFormatPos = cchWritten; /* Save position of last output format text */
00703     }
00704     else
00705     {
00706       /* Literal character */
00707       if (!cchOut)
00708         cchWritten++;   /* Count size only */
00709       else if (cchWritten >= cchOut)
00710         goto overrun;
00711       else if (!bSkipping || *lpFormat == ' ')
00712       {
00713         lpStr[cchWritten] = *lpFormat;
00714         cchWritten++;
00715       }
00716       lpFormat++;
00717     }
00718   }
00719 
00720   /* Final string terminator and sanity check */
00721   if (cchOut)
00722   {
00723    if (cchWritten >= cchOut)
00724      goto overrun;
00725    else
00726      lpStr[cchWritten] = '\0';
00727   }
00728   cchWritten++; /* Include terminating NUL */
00729 
00730   TRACE("returning length=%d, ouput=%s\n", cchWritten, debugstr_w(lpStr));
00731   return cchWritten;
00732 
00733 overrun:
00734   TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
00735   SetLastError(ERROR_INSUFFICIENT_BUFFER);
00736   return 0;
00737 
00738 invalid_parameter:
00739   SetLastError(ERROR_INVALID_PARAMETER);
00740   return 0;
00741 
00742 invalid_flags:
00743   SetLastError(ERROR_INVALID_FLAGS);
00744   return 0;
00745 }
00746 
00747 /******************************************************************************
00748  * NLS_GetDateTimeFormatA <internal>
00749  *
00750  * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
00751  */
00752 static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags,
00753                                   const SYSTEMTIME* lpTime,
00754                                   LPCSTR lpFormat, LPSTR lpStr, INT cchOut)
00755 {
00756   DWORD cp = CP_ACP;
00757   WCHAR szFormat[128], szOut[128];
00758   INT iRet;
00759 
00760   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
00761         debugstr_a(lpFormat), lpStr, cchOut);
00762 
00763   if (NLS_IsUnicodeOnlyLcid(lcid))
00764   {
00765     SetLastError(ERROR_INVALID_PARAMETER);
00766     return 0;
00767   }
00768 
00769   if (!(dwFlags & LOCALE_USE_CP_ACP))
00770   {
00771     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
00772     if (!node)
00773     {
00774       SetLastError(ERROR_INVALID_PARAMETER);
00775       return 0;
00776     }
00777 
00778     cp = node->dwCodePage;
00779   }
00780 
00781   if (lpFormat)
00782     MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, sizeof(szFormat)/sizeof(WCHAR));
00783 
00784   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
00785     cchOut = sizeof(szOut)/sizeof(WCHAR);
00786 
00787   szOut[0] = '\0';
00788 
00789   iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL,
00790                                 lpStr ? szOut : NULL, cchOut);
00791 
00792   if (lpStr)
00793   {
00794     if (szOut[0])
00795       WideCharToMultiByte(cp, 0, szOut, iRet ? -1 : cchOut, lpStr, cchOut, 0, 0);
00796     else if (cchOut && iRet)
00797       *lpStr = '\0';
00798   }
00799   return iRet;
00800 }
00801 
00802 /******************************************************************************
00803  * GetDateFormatA [KERNEL32.@]
00804  *
00805  * Format a date for a given locale.
00806  *
00807  * PARAMS
00808  *  lcid      [I] Locale to format for
00809  *  dwFlags   [I] LOCALE_ and DATE_ flags from "winnls.h"
00810  *  lpTime    [I] Date to format
00811  *  lpFormat  [I] Format string, or NULL to use the system defaults
00812  *  lpDateStr [O] Destination for formatted string
00813  *  cchOut    [I] Size of lpDateStr, or 0 to calculate the resulting size
00814  *
00815  * NOTES
00816  *  - If lpFormat is NULL, lpDateStr will be formatted according to the format
00817  *    details returned by GetLocaleInfoA() and modified by dwFlags.
00818  *  - lpFormat is a string of characters and formatting tokens. Any characters
00819  *    in the string are copied verbatim to lpDateStr, with tokens being replaced
00820  *    by the date values they represent.
00821  *  - The following tokens have special meanings in a date format string:
00822  *|  Token  Meaning
00823  *|  -----  -------
00824  *|  d      Single digit day of the month (no leading 0)
00825  *|  dd     Double digit day of the month
00826  *|  ddd    Short name for the day of the week
00827  *|  dddd   Long name for the day of the week
00828  *|  M      Single digit month of the year (no leading 0)
00829  *|  MM     Double digit month of the year
00830  *|  MMM    Short name for the month of the year
00831  *|  MMMM   Long name for the month of the year
00832  *|  y      Double digit year number (no leading 0)
00833  *|  yy     Double digit year number
00834  *|  yyyy   Four digit year number
00835  *|  gg     Era string, for example 'AD'.
00836  *  - To output any literal character that could be misidentified as a token,
00837  *    enclose it in single quotes.
00838  *  - The Ascii version of this function fails if lcid is Unicode only.
00839  *
00840  * RETURNS
00841  *  Success: The number of character written to lpDateStr, or that would
00842  *           have been written, if cchOut is 0.
00843  *  Failure: 0. Use GetLastError() to determine the cause.
00844  */
00845 INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
00846                            LPCSTR lpFormat, LPSTR lpDateStr, INT cchOut)
00847 {
00848   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
00849         debugstr_a(lpFormat), lpDateStr, cchOut);
00850 
00851   return NLS_GetDateTimeFormatA(lcid, dwFlags | DATE_DATEVARSONLY, lpTime,
00852                                 lpFormat, lpDateStr, cchOut);
00853 }
00854 
00855 
00856 /******************************************************************************
00857  * GetDateFormatW   [KERNEL32.@]
00858  *
00859  * See GetDateFormatA.
00860  */
00861 INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
00862                           LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut)
00863 {
00864   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
00865         debugstr_w(lpFormat), lpDateStr, cchOut);
00866 
00867   return NLS_GetDateTimeFormatW(lcid, dwFlags|DATE_DATEVARSONLY, lpTime,
00868                                 lpFormat, lpDateStr, cchOut);
00869 }
00870 
00871 /******************************************************************************
00872  *      GetTimeFormatA  [KERNEL32.@]
00873  *
00874  * Format a time for a given locale.
00875  *
00876  * PARAMS
00877  *  lcid      [I] Locale to format for
00878  *  dwFlags   [I] LOCALE_ and TIME_ flags from "winnls.h"
00879  *  lpTime    [I] Time to format
00880  *  lpFormat  [I] Formatting overrides
00881  *  lpTimeStr [O] Destination for formatted string
00882  *  cchOut    [I] Size of lpTimeStr, or 0 to calculate the resulting size
00883  *
00884  * NOTES
00885  *  - If lpFormat is NULL, lpszValue will be formatted according to the format
00886  *    details returned by GetLocaleInfoA() and modified by dwFlags.
00887  *  - lpFormat is a string of characters and formatting tokens. Any characters
00888  *    in the string are copied verbatim to lpTimeStr, with tokens being replaced
00889  *    by the time values they represent.
00890  *  - The following tokens have special meanings in a time format string:
00891  *|  Token  Meaning
00892  *|  -----  -------
00893  *|  h      Hours with no leading zero (12-hour clock)
00894  *|  hh     Hours with full two digits (12-hour clock)
00895  *|  H      Hours with no leading zero (24-hour clock)
00896  *|  HH     Hours with full two digits (24-hour clock)
00897  *|  m      Minutes with no leading zero
00898  *|  mm     Minutes with full two digits
00899  *|  s      Seconds with no leading zero
00900  *|  ss     Seconds with full two digits
00901  *|  t      Short time marker (e.g. "A" or "P")
00902  *|  tt     Long time marker (e.g. "AM", "PM")
00903  *  - To output any literal character that could be misidentified as a token,
00904  *    enclose it in single quotes.
00905  *  - The Ascii version of this function fails if lcid is Unicode only.
00906  *
00907  * RETURNS
00908  *  Success: The number of character written to lpTimeStr, or that would
00909  *           have been written, if cchOut is 0.
00910  *  Failure: 0. Use GetLastError() to determine the cause.
00911  */
00912 INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
00913                           LPCSTR lpFormat, LPSTR lpTimeStr, INT cchOut)
00914 {
00915   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
00916         debugstr_a(lpFormat), lpTimeStr, cchOut);
00917 
00918   return NLS_GetDateTimeFormatA(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
00919                                 lpFormat, lpTimeStr, cchOut);
00920 }
00921 
00922 /******************************************************************************
00923  *      GetTimeFormatW  [KERNEL32.@]
00924  *
00925  * See GetTimeFormatA.
00926  */
00927 INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
00928                           LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut)
00929 {
00930   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
00931         debugstr_w(lpFormat), lpTimeStr, cchOut);
00932 
00933   return NLS_GetDateTimeFormatW(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
00934                                 lpFormat, lpTimeStr, cchOut);
00935 }
00936 
00937 /**************************************************************************
00938  *              GetNumberFormatA    (KERNEL32.@)
00939  *
00940  * Format a number string for a given locale.
00941  *
00942  * PARAMS
00943  *  lcid        [I] Locale to format for
00944  *  dwFlags     [I] LOCALE_ flags from "winnls.h"
00945  *  lpszValue   [I] String to format
00946  *  lpFormat    [I] Formatting overrides
00947  *  lpNumberStr [O] Destination for formatted string
00948  *  cchOut      [I] Size of lpNumberStr, or 0 to calculate the resulting size
00949  *
00950  * NOTES
00951  *  - lpszValue can contain only '0' - '9', '-' and '.'.
00952  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
00953  *    be formatted according to the format details returned by GetLocaleInfoA().
00954  *  - This function rounds the number string if the number of decimals exceeds the
00955  *    locales normal number of decimal places.
00956  *  - If cchOut is 0, this function does not write to lpNumberStr.
00957  *  - The Ascii version of this function fails if lcid is Unicode only.
00958  *
00959  * RETURNS
00960  *  Success: The number of character written to lpNumberStr, or that would
00961  *           have been written, if cchOut is 0.
00962  *  Failure: 0. Use GetLastError() to determine the cause.
00963  */
00964 INT WINAPI GetNumberFormatA(LCID lcid, DWORD dwFlags,
00965                             LPCSTR lpszValue,  const NUMBERFMTA *lpFormat,
00966                             LPSTR lpNumberStr, int cchOut)
00967 {
00968   DWORD cp = CP_ACP;
00969   WCHAR szDec[8], szGrp[8], szIn[128], szOut[128];
00970   NUMBERFMTW fmt;
00971   const NUMBERFMTW *pfmt = NULL;
00972   INT iRet;
00973 
00974   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
00975         lpFormat, lpNumberStr, cchOut);
00976 
00977   if (NLS_IsUnicodeOnlyLcid(lcid))
00978   {
00979     SetLastError(ERROR_INVALID_PARAMETER);
00980     return 0;
00981   }
00982 
00983   if (!(dwFlags & LOCALE_USE_CP_ACP))
00984   {
00985     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
00986     if (!node)
00987     {
00988       SetLastError(ERROR_INVALID_PARAMETER);
00989       return 0;
00990     }
00991 
00992     cp = node->dwCodePage;
00993   }
00994 
00995   if (lpFormat)
00996   {
00997     memcpy(&fmt, lpFormat, sizeof(fmt));
00998     pfmt = &fmt;
00999     if (lpFormat->lpDecimalSep)
01000     {
01001       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
01002       fmt.lpDecimalSep = szDec;
01003     }
01004     if (lpFormat->lpThousandSep)
01005     {
01006       MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
01007       fmt.lpThousandSep = szGrp;
01008     }
01009   }
01010 
01011   if (lpszValue)
01012     MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
01013 
01014   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
01015     cchOut = sizeof(szOut)/sizeof(WCHAR);
01016 
01017   szOut[0] = '\0';
01018 
01019   iRet = GetNumberFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
01020                           lpNumberStr ? szOut : NULL, cchOut);
01021 
01022   if (szOut[0] && lpNumberStr)
01023     WideCharToMultiByte(cp, 0, szOut, -1, lpNumberStr, cchOut, 0, 0);
01024   return iRet;
01025 }
01026 
01027 /* Number parsing state flags */
01028 #define NF_ISNEGATIVE 0x1  /* '-' found */
01029 #define NF_ISREAL     0x2  /* '.' found */
01030 #define NF_DIGITS     0x4  /* '0'-'9' found */
01031 #define NF_DIGITS_OUT 0x8  /* Digits before the '.' found */
01032 #define NF_ROUND      0x10 /* Number needs to be rounded */
01033 
01034 /* Formatting options for Numbers */
01035 #define NLS_NEG_PARENS      0 /* "(1.1)" */
01036 #define NLS_NEG_LEFT        1 /* "-1.1"  */
01037 #define NLS_NEG_LEFT_SPACE  2 /* "- 1.1" */
01038 #define NLS_NEG_RIGHT       3 /* "1.1-"  */
01039 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
01040 
01041 /**************************************************************************
01042  *              GetNumberFormatW    (KERNEL32.@)
01043  *
01044  * See GetNumberFormatA.
01045  */
01046 INT WINAPI GetNumberFormatW(LCID lcid, DWORD dwFlags,
01047                             LPCWSTR lpszValue,  const NUMBERFMTW *lpFormat,
01048                             LPWSTR lpNumberStr, int cchOut)
01049 {
01050   WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
01051   WCHAR szNegBuff[8];
01052   const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc;
01053   DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0;
01054   INT iRet;
01055 
01056   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
01057         lpFormat, lpNumberStr, cchOut);
01058 
01059   if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpNumberStr) ||
01060       !IsValidLocale(lcid, 0) ||
01061       (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep)))
01062   {
01063     goto error;
01064   }
01065 
01066   if (!lpFormat)
01067   {
01068     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
01069 
01070     if (!node)
01071       goto error;
01072     lpFormat = &node->fmt;
01073     lpszNegStart = lpszNeg = GetNegative(node);
01074   }
01075   else
01076   {
01077     GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
01078                    szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
01079     lpszNegStart = lpszNeg = szNegBuff;
01080   }
01081   lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
01082 
01083   dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
01084 
01085   /* Format the number backwards into a temporary buffer */
01086 
01087   szSrc = lpszValue;
01088   *szOut-- = '\0';
01089 
01090   /* Check the number for validity */
01091   while (*szSrc)
01092   {
01093     if (*szSrc >= '0' && *szSrc <= '9')
01094     {
01095       dwState |= NF_DIGITS;
01096       if (dwState & NF_ISREAL)
01097         dwDecimals++;
01098     }
01099     else if (*szSrc == '-')
01100     {
01101       if (dwState)
01102         goto error; /* '-' not first character */
01103       dwState |= NF_ISNEGATIVE;
01104     }
01105     else if (*szSrc == '.')
01106     {
01107       if (dwState & NF_ISREAL)
01108         goto error; /* More than one '.' */
01109       dwState |= NF_ISREAL;
01110     }
01111     else
01112       goto error; /* Invalid char */
01113     szSrc++;
01114   }
01115   szSrc--; /* Point to last character */
01116 
01117   if (!(dwState & NF_DIGITS))
01118     goto error; /* No digits */
01119 
01120   /* Add any trailing negative sign */
01121   if (dwState & NF_ISNEGATIVE)
01122   {
01123     switch (lpFormat->NegativeOrder)
01124     {
01125     case NLS_NEG_PARENS:
01126       *szOut-- = ')';
01127       break;
01128     case NLS_NEG_RIGHT:
01129     case NLS_NEG_RIGHT_SPACE:
01130       while (lpszNeg >= lpszNegStart)
01131         *szOut-- = *lpszNeg--;
01132      if (lpFormat->NegativeOrder == NLS_NEG_RIGHT_SPACE)
01133        *szOut-- = ' ';
01134       break;
01135     }
01136   }
01137 
01138   /* Copy all digits up to the decimal point */
01139   if (!lpFormat->NumDigits)
01140   {
01141     if (dwState & NF_ISREAL)
01142     {
01143       while (*szSrc != '.') /* Don't write any decimals or a separator */
01144       {
01145         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
01146           dwState |= NF_ROUND;
01147         else
01148           dwState &= ~NF_ROUND;
01149         szSrc--;
01150       }
01151       szSrc--;
01152     }
01153   }
01154   else
01155   {
01156     LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
01157 
01158     if (dwDecimals <= lpFormat->NumDigits)
01159     {
01160       dwDecimals = lpFormat->NumDigits - dwDecimals;
01161       while (dwDecimals--)
01162         *szOut-- = '0'; /* Pad to correct number of dp */
01163     }
01164     else
01165     {
01166       dwDecimals -= lpFormat->NumDigits;
01167       /* Skip excess decimals, and determine if we have to round the number */
01168       while (dwDecimals--)
01169       {
01170         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
01171           dwState |= NF_ROUND;
01172         else
01173           dwState &= ~NF_ROUND;
01174         szSrc--;
01175       }
01176     }
01177 
01178     if (dwState & NF_ISREAL)
01179     {
01180       while (*szSrc != '.')
01181       {
01182         if (dwState & NF_ROUND)
01183         {
01184           if (*szSrc == '9')
01185             *szOut-- = '0'; /* continue rounding */
01186           else
01187           {
01188             dwState &= ~NF_ROUND;
01189             *szOut-- = (*szSrc)+1;
01190           }
01191           szSrc--;
01192         }
01193         else
01194           *szOut-- = *szSrc--; /* Write existing decimals */
01195       }
01196       szSrc--; /* Skip '.' */
01197     }
01198 
01199     while (lpszDec >= lpFormat->lpDecimalSep)
01200       *szOut-- = *lpszDec--; /* Write decimal separator */
01201   }
01202 
01203   dwGroupCount = lpFormat->Grouping == 32 ? 3 : lpFormat->Grouping;
01204 
01205   /* Write the remaining whole number digits, including grouping chars */
01206   while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
01207   {
01208     if (dwState & NF_ROUND)
01209     {
01210       if (*szSrc == '9')
01211         *szOut-- = '0'; /* continue rounding */
01212       else
01213       {
01214         dwState &= ~NF_ROUND;
01215         *szOut-- = (*szSrc)+1;
01216       }
01217       szSrc--;
01218     }
01219     else
01220       *szOut-- = *szSrc--;
01221 
01222     dwState |= NF_DIGITS_OUT;
01223     dwCurrentGroupCount++;
01224     if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
01225     {
01226       LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
01227 
01228       while (lpszGrp >= lpFormat->lpThousandSep)
01229         *szOut-- = *lpszGrp--; /* Write grouping char */
01230 
01231       dwCurrentGroupCount = 0;
01232       if (lpFormat->Grouping == 32)
01233         dwGroupCount = 2; /* Indic grouping: 3 then 2 */
01234     }
01235   }
01236   if (dwState & NF_ROUND)
01237   {
01238     *szOut-- = '1'; /* e.g. .6 > 1.0 */
01239   }
01240   else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
01241     *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
01242 
01243   /* Add any leading negative sign */
01244   if (dwState & NF_ISNEGATIVE)
01245   {
01246     switch (lpFormat->NegativeOrder)
01247     {
01248     case NLS_NEG_PARENS:
01249       *szOut-- = '(';
01250       break;
01251     case NLS_NEG_LEFT_SPACE:
01252       *szOut-- = ' ';
01253       /* Fall through */
01254     case NLS_NEG_LEFT:
01255       while (lpszNeg >= lpszNegStart)
01256         *szOut-- = *lpszNeg--;
01257       break;
01258     }
01259   }
01260   szOut++;
01261 
01262   iRet = strlenW(szOut) + 1;
01263   if (cchOut)
01264   {
01265     if (iRet <= cchOut)
01266       memcpy(lpNumberStr, szOut, iRet * sizeof(WCHAR));
01267     else
01268     {
01269       memcpy(lpNumberStr, szOut, cchOut * sizeof(WCHAR));
01270       lpNumberStr[cchOut - 1] = '\0';
01271       SetLastError(ERROR_INSUFFICIENT_BUFFER);
01272       iRet = 0;
01273     }
01274   }
01275   return iRet;
01276 
01277 error:
01278   SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
01279   return 0;
01280 }
01281 
01282 /**************************************************************************
01283  *              GetCurrencyFormatA  (KERNEL32.@)
01284  *
01285  * Format a currency string for a given locale.
01286  *
01287  * PARAMS
01288  *  lcid          [I] Locale to format for
01289  *  dwFlags       [I] LOCALE_ flags from "winnls.h"
01290  *  lpszValue     [I] String to format
01291  *  lpFormat      [I] Formatting overrides
01292  *  lpCurrencyStr [O] Destination for formatted string
01293  *  cchOut        [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
01294  *
01295  * NOTES
01296  *  - lpszValue can contain only '0' - '9', '-' and '.'.
01297  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
01298  *    be formatted according to the format details returned by GetLocaleInfoA().
01299  *  - This function rounds the currency if the number of decimals exceeds the
01300  *    locales number of currency decimal places.
01301  *  - If cchOut is 0, this function does not write to lpCurrencyStr.
01302  *  - The Ascii version of this function fails if lcid is Unicode only.
01303  *
01304  * RETURNS
01305  *  Success: The number of character written to lpNumberStr, or that would
01306  *           have been written, if cchOut is 0.
01307  *  Failure: 0. Use GetLastError() to determine the cause.
01308  */
01309 INT WINAPI GetCurrencyFormatA(LCID lcid, DWORD dwFlags,
01310                               LPCSTR lpszValue,  const CURRENCYFMTA *lpFormat,
01311                               LPSTR lpCurrencyStr, int cchOut)
01312 {
01313   DWORD cp = CP_ACP;
01314   WCHAR szDec[8], szGrp[8], szCy[8], szIn[128], szOut[128];
01315   CURRENCYFMTW fmt;
01316   const CURRENCYFMTW *pfmt = NULL;
01317   INT iRet;
01318 
01319   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
01320         lpFormat, lpCurrencyStr, cchOut);
01321 
01322   if (NLS_IsUnicodeOnlyLcid(lcid))
01323   {
01324     SetLastError(ERROR_INVALID_PARAMETER);
01325     return 0;
01326   }
01327 
01328   if (!(dwFlags & LOCALE_USE_CP_ACP))
01329   {
01330     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
01331     if (!node)
01332     {
01333       SetLastError(ERROR_INVALID_PARAMETER);
01334       return 0;
01335     }
01336 
01337     cp = node->dwCodePage;
01338   }
01339 
01340   if (lpFormat)
01341   {
01342     memcpy(&fmt, lpFormat, sizeof(fmt));
01343     pfmt = &fmt;
01344     if (lpFormat->lpDecimalSep)
01345     {
01346       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
01347       fmt.lpDecimalSep = szDec;
01348     }
01349     if (lpFormat->lpThousandSep)
01350     {
01351       MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
01352       fmt.lpThousandSep = szGrp;
01353     }
01354     if (lpFormat->lpCurrencySymbol)
01355     {
01356       MultiByteToWideChar(cp, 0, lpFormat->lpCurrencySymbol, -1, szCy, sizeof(szCy)/sizeof(WCHAR));
01357       fmt.lpCurrencySymbol = szCy;
01358     }
01359   }
01360 
01361   if (lpszValue)
01362     MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
01363 
01364   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
01365     cchOut = sizeof(szOut)/sizeof(WCHAR);
01366 
01367   szOut[0] = '\0';
01368 
01369   iRet = GetCurrencyFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
01370                             lpCurrencyStr ? szOut : NULL, cchOut);
01371 
01372   if (szOut[0] && lpCurrencyStr)
01373     WideCharToMultiByte(cp, 0, szOut, -1, lpCurrencyStr, cchOut, 0, 0);
01374   return iRet;
01375 }
01376 
01377 /* Formatting states for Currencies. We use flags to avoid code duplication. */
01378 #define CF_PARENS       0x1  /* Parentheses      */
01379 #define CF_MINUS_LEFT   0x2  /* '-' to the left  */
01380 #define CF_MINUS_RIGHT  0x4  /* '-' to the right */
01381 #define CF_MINUS_BEFORE 0x8  /* '-' before '$'   */
01382 #define CF_CY_LEFT      0x10 /* '$' to the left  */
01383 #define CF_CY_RIGHT     0x20 /* '$' to the right */
01384 #define CF_CY_SPACE     0x40 /* ' ' by '$'       */
01385 
01386 /**************************************************************************
01387  *              GetCurrencyFormatW  (KERNEL32.@)
01388  *
01389  * See GetCurrencyFormatA.
01390  */
01391 INT WINAPI GetCurrencyFormatW(LCID lcid, DWORD dwFlags,
01392                               LPCWSTR lpszValue,  const CURRENCYFMTW *lpFormat,
01393                               LPWSTR lpCurrencyStr, int cchOut)
01394 {
01395   static const BYTE NLS_NegCyFormats[16] =
01396   {
01397     CF_PARENS|CF_CY_LEFT,                       /* ($1.1) */
01398     CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT,   /* -$1.1  */
01399     CF_MINUS_LEFT|CF_CY_LEFT,                   /* $-1.1  */
01400     CF_MINUS_RIGHT|CF_CY_LEFT,                  /* $1.1-  */
01401     CF_PARENS|CF_CY_RIGHT,                      /* (1.1$) */
01402     CF_MINUS_LEFT|CF_CY_RIGHT,                  /* -1.1$  */
01403     CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT, /* 1.1-$  */
01404     CF_MINUS_RIGHT|CF_CY_RIGHT,                 /* 1.1$-  */
01405     CF_MINUS_LEFT|CF_CY_RIGHT|CF_CY_SPACE,      /* -1.1 $ */
01406     CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT|CF_CY_SPACE,   /* -$ 1.1 */
01407     CF_MINUS_RIGHT|CF_CY_RIGHT|CF_CY_SPACE,     /* 1.1 $-  */
01408     CF_MINUS_RIGHT|CF_CY_LEFT|CF_CY_SPACE,      /* $ 1.1-  */
01409     CF_MINUS_LEFT|CF_CY_LEFT|CF_CY_SPACE,       /* $ -1.1  */
01410     CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1- $ */
01411     CF_PARENS|CF_CY_LEFT|CF_CY_SPACE,           /* ($ 1.1) */
01412     CF_PARENS|CF_CY_RIGHT|CF_CY_SPACE,          /* (1.1 $) */
01413   };
01414   static const BYTE NLS_PosCyFormats[4] =
01415   {
01416     CF_CY_LEFT,              /* $1.1  */
01417     CF_CY_RIGHT,             /* 1.1$  */
01418     CF_CY_LEFT|CF_CY_SPACE,  /* $ 1.1 */
01419     CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $ */
01420   };
01421   WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
01422   WCHAR szNegBuff[8];
01423   const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc, *lpszCy, *lpszCyStart;
01424   DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0, dwFmt;
01425   INT iRet;
01426 
01427   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
01428         lpFormat, lpCurrencyStr, cchOut);
01429 
01430   if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpCurrencyStr) ||
01431       !IsValidLocale(lcid, 0) ||
01432       (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep ||
01433       !lpFormat->lpCurrencySymbol || lpFormat->NegativeOrder > 15 ||
01434       lpFormat->PositiveOrder > 3)))
01435   {
01436     goto error;
01437   }
01438 
01439   if (!lpFormat)
01440   {
01441     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
01442 
01443     if (!node)
01444       goto error;
01445 
01446     lpFormat = &node->cyfmt;
01447     lpszNegStart = lpszNeg = GetNegative(node);
01448   }
01449   else
01450   {
01451     GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
01452                    szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
01453     lpszNegStart = lpszNeg = szNegBuff;
01454   }
01455   dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
01456 
01457   lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
01458   lpszCyStart = lpFormat->lpCurrencySymbol;
01459   lpszCy = lpszCyStart + strlenW(lpszCyStart) - 1;
01460 
01461   /* Format the currency backwards into a temporary buffer */
01462 
01463   szSrc = lpszValue;
01464   *szOut-- = '\0';
01465 
01466   /* Check the number for validity */
01467   while (*szSrc)
01468   {
01469     if (*szSrc >= '0' && *szSrc <= '9')
01470     {
01471       dwState |= NF_DIGITS;
01472       if (dwState & NF_ISREAL)
01473         dwDecimals++;
01474     }
01475     else if (*szSrc == '-')
01476     {
01477       if (dwState)
01478         goto error; /* '-' not first character */
01479       dwState |= NF_ISNEGATIVE;
01480     }
01481     else if (*szSrc == '.')
01482     {
01483       if (dwState & NF_ISREAL)
01484         goto error; /* More than one '.' */
01485       dwState |= NF_ISREAL;
01486     }
01487     else
01488       goto error; /* Invalid char */
01489     szSrc++;
01490   }
01491   szSrc--; /* Point to last character */
01492 
01493   if (!(dwState & NF_DIGITS))
01494     goto error; /* No digits */
01495 
01496   if (dwState & NF_ISNEGATIVE)
01497     dwFmt = NLS_NegCyFormats[lpFormat->NegativeOrder];
01498   else
01499     dwFmt = NLS_PosCyFormats[lpFormat->PositiveOrder];
01500 
01501   /* Add any trailing negative or currency signs */
01502   if (dwFmt & CF_PARENS)
01503     *szOut-- = ')';
01504 
01505   while (dwFmt & (CF_MINUS_RIGHT|CF_CY_RIGHT))
01506   {
01507     switch (dwFmt & (CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT))
01508     {
01509     case CF_MINUS_RIGHT:
01510     case CF_MINUS_RIGHT|CF_CY_RIGHT:
01511       while (lpszNeg >= lpszNegStart)
01512         *szOut-- = *lpszNeg--;
01513       dwFmt &= ~CF_MINUS_RIGHT;
01514       break;
01515 
01516     case CF_CY_RIGHT:
01517     case CF_MINUS_BEFORE|CF_CY_RIGHT:
01518     case CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT:
01519       while (lpszCy >= lpszCyStart)
01520         *szOut-- = *lpszCy--;
01521       if (dwFmt & CF_CY_SPACE)
01522         *szOut-- = ' ';
01523       dwFmt &= ~(CF_CY_RIGHT|CF_MINUS_BEFORE);
01524       break;
01525     }
01526   }
01527 
01528   /* Copy all digits up to the decimal point */
01529   if (!lpFormat->NumDigits)
01530   {
01531     if (dwState & NF_ISREAL)
01532     {
01533       while (*szSrc != '.') /* Don't write any decimals or a separator */
01534       {
01535         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
01536           dwState |= NF_ROUND;
01537         else
01538           dwState &= ~NF_ROUND;
01539         szSrc--;
01540       }
01541       szSrc--;
01542     }
01543   }
01544   else
01545   {
01546     LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
01547 
01548     if (dwDecimals <= lpFormat->NumDigits)
01549     {
01550       dwDecimals = lpFormat->NumDigits - dwDecimals;
01551       while (dwDecimals--)
01552         *szOut-- = '0'; /* Pad to correct number of dp */
01553     }
01554     else
01555     {
01556       dwDecimals -= lpFormat->NumDigits;
01557       /* Skip excess decimals, and determine if we have to round the number */
01558       while (dwDecimals--)
01559       {
01560         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
01561           dwState |= NF_ROUND;
01562         else
01563           dwState &= ~NF_ROUND;
01564         szSrc--;
01565       }
01566     }
01567 
01568     if (dwState & NF_ISREAL)
01569     {
01570       while (*szSrc != '.')
01571       {
01572         if (dwState & NF_ROUND)
01573         {
01574           if (*szSrc == '9')
01575             *szOut-- = '0'; /* continue rounding */
01576           else
01577           {
01578             dwState &= ~NF_ROUND;
01579             *szOut-- = (*szSrc)+1;
01580           }
01581           szSrc--;
01582         }
01583         else
01584           *szOut-- = *szSrc--; /* Write existing decimals */
01585       }
01586       szSrc--; /* Skip '.' */
01587     }
01588     while (lpszDec >= lpFormat->lpDecimalSep)
01589       *szOut-- = *lpszDec--; /* Write decimal separator */
01590   }
01591 
01592   dwGroupCount = lpFormat->Grouping;
01593 
01594   /* Write the remaining whole number digits, including grouping chars */
01595   while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
01596   {
01597     if (dwState & NF_ROUND)
01598     {
01599       if (*szSrc == '9')
01600         *szOut-- = '0'; /* continue rounding */
01601       else
01602       {
01603         dwState &= ~NF_ROUND;
01604         *szOut-- = (*szSrc)+1;
01605       }
01606       szSrc--;
01607     }
01608     else
01609       *szOut-- = *szSrc--;
01610 
01611     dwState |= NF_DIGITS_OUT;
01612     dwCurrentGroupCount++;
01613     if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
01614     {
01615       LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
01616 
01617       while (lpszGrp >= lpFormat->lpThousandSep)
01618         *szOut-- = *lpszGrp--; /* Write grouping char */
01619 
01620       dwCurrentGroupCount = 0;
01621     }
01622   }
01623   if (dwState & NF_ROUND)
01624     *szOut-- = '1'; /* e.g. .6 > 1.0 */
01625   else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
01626     *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
01627 
01628   /* Add any leading negative or currency sign */
01629   while (dwFmt & (CF_MINUS_LEFT|CF_CY_LEFT))
01630   {
01631     switch (dwFmt & (CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT))
01632     {
01633     case CF_MINUS_LEFT:
01634     case CF_MINUS_LEFT|CF_CY_LEFT:
01635       while (lpszNeg >= lpszNegStart)
01636         *szOut-- = *lpszNeg--;
01637       dwFmt &= ~CF_MINUS_LEFT;
01638       break;
01639 
01640     case CF_CY_LEFT:
01641     case CF_CY_LEFT|CF_MINUS_BEFORE:
01642     case CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT:
01643       if (dwFmt & CF_CY_SPACE)
01644         *szOut-- = ' ';
01645       while (lpszCy >= lpszCyStart)
01646         *szOut-- = *lpszCy--;
01647       dwFmt &= ~(CF_CY_LEFT|CF_MINUS_BEFORE);
01648       break;
01649     }
01650   }
01651   if (dwFmt & CF_PARENS)
01652     *szOut-- = '(';
01653   szOut++;
01654 
01655   iRet = strlenW(szOut) + 1;
01656   if (cchOut)
01657   {
01658     if (iRet <= cchOut)
01659       memcpy(lpCurrencyStr, szOut, iRet * sizeof(WCHAR));
01660     else
01661     {
01662       memcpy(lpCurrencyStr, szOut, cchOut * sizeof(WCHAR));
01663       lpCurrencyStr[cchOut - 1] = '\0';
01664       SetLastError(ERROR_INSUFFICIENT_BUFFER);
01665       iRet = 0;
01666     }
01667   }
01668   return iRet;
01669 
01670 error:
01671   SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
01672   return 0;
01673 }
01674 
01675 /* FIXME: Everything below here needs to move somewhere else along with the
01676  *        other EnumXXX functions, when a method for storing resources for
01677  *        alternate calendars is determined.
01678  */
01679 
01680 /**************************************************************************
01681  *              EnumDateFormatsExA    (KERNEL32.@)
01682  *
01683  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
01684  * LOCALE_NOUSEROVERRIDE here as well?
01685  */
01686 BOOL WINAPI EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc, LCID lcid, DWORD flags)
01687 {
01688     CALID cal_id;
01689     char buf[256];
01690 
01691     if (!proc)
01692     {
01693         SetLastError(ERROR_INVALID_PARAMETER);
01694         return FALSE;
01695     }
01696 
01697     if (!GetLocaleInfoW(lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR)))
01698         return FALSE;
01699 
01700     switch (flags & ~LOCALE_USE_CP_ACP)
01701     {
01702     case 0:
01703     case DATE_SHORTDATE:
01704         if (GetLocaleInfoA(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
01705             proc(buf, cal_id);
01706         break;
01707 
01708     case DATE_LONGDATE:
01709         if (GetLocaleInfoA(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
01710             proc(buf, cal_id);
01711         break;
01712 
01713     case DATE_YEARMONTH:
01714         if (GetLocaleInfoA(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
01715             proc(buf, cal_id);
01716         break;
01717 
01718     default:
01719         FIXME("Unknown date format (%d)\n", flags);
01720         SetLastError(ERROR_INVALID_PARAMETER);
01721         return FALSE;
01722     }
01723     return TRUE;
01724 }
01725 
01726 /**************************************************************************
01727  *              EnumDateFormatsExW    (KERNEL32.@)
01728  */
01729 BOOL WINAPI EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags)
01730 {
01731     CALID cal_id;
01732     WCHAR buf[256];
01733 
01734     if (!proc)
01735     {
01736         SetLastError(ERROR_INVALID_PARAMETER);
01737         return FALSE;
01738     }
01739 
01740     if (!GetLocaleInfoW(lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR)))
01741         return FALSE;
01742 
01743     switch (flags & ~LOCALE_USE_CP_ACP)
01744     {
01745     case 0:
01746     case DATE_SHORTDATE:
01747         if (GetLocaleInfoW(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
01748             proc(buf, cal_id);
01749         break;
01750 
01751     case DATE_LONGDATE:
01752         if (GetLocaleInfoW(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
01753             proc(buf, cal_id);
01754         break;
01755 
01756     case DATE_YEARMONTH:
01757         if (GetLocaleInfoW(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
01758             proc(buf, cal_id);
01759         break;
01760 
01761     default:
01762         FIXME("Unknown date format (%d)\n", flags);
01763         SetLastError(ERROR_INVALID_PARAMETER);
01764         return FALSE;
01765     }
01766     return TRUE;
01767 }
01768 
01769 /**************************************************************************
01770  *              EnumDateFormatsA    (KERNEL32.@)
01771  *
01772  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
01773  * LOCALE_NOUSEROVERRIDE here as well?
01774  */
01775 BOOL WINAPI EnumDateFormatsA(DATEFMT_ENUMPROCA proc, LCID lcid, DWORD flags)
01776 {
01777     char buf[256];
01778 
01779     if (!proc)
01780     {
01781         SetLastError(ERROR_INVALID_PARAMETER);
01782         return FALSE;
01783     }
01784 
01785     switch (flags & ~LOCALE_USE_CP_ACP)
01786     {
01787     case 0:
01788     case DATE_SHORTDATE:
01789         if (GetLocaleInfoA(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
01790             proc(buf);
01791         break;
01792 
01793     case DATE_LONGDATE:
01794         if (GetLocaleInfoA(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
01795             proc(buf);
01796         break;
01797 
01798     case DATE_YEARMONTH:
01799         if (GetLocaleInfoA(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
01800             proc(buf);
01801         break;
01802 
01803     default:
01804         FIXME("Unknown date format (%d)\n", flags);
01805         SetLastError(ERROR_INVALID_PARAMETER);
01806         return FALSE;
01807     }
01808     return TRUE;
01809 }
01810 
01811 /**************************************************************************
01812  *              EnumDateFormatsW    (KERNEL32.@)
01813  */
01814 BOOL WINAPI EnumDateFormatsW(DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags)
01815 {
01816     WCHAR buf[256];
01817 
01818     if (!proc)
01819     {
01820         SetLastError(ERROR_INVALID_PARAMETER);
01821         return FALSE;
01822     }
01823 
01824     switch (flags & ~LOCALE_USE_CP_ACP)
01825     {
01826     case 0:
01827     case DATE_SHORTDATE:
01828         if (GetLocaleInfoW(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
01829             proc(buf);
01830         break;
01831 
01832     case DATE_LONGDATE:
01833         if (GetLocaleInfoW(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
01834             proc(buf);
01835         break;
01836 
01837     case DATE_YEARMONTH:
01838         if (GetLocaleInfoW(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
01839             proc(buf);
01840         break;
01841 
01842     default:
01843         FIXME("Unknown date format (%d)\n", flags);
01844         SetLastError(ERROR_INVALID_PARAMETER);
01845         return FALSE;
01846     }
01847     return TRUE;
01848 }
01849 
01850 /**************************************************************************
01851  *              EnumTimeFormatsA    (KERNEL32.@)
01852  *
01853  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
01854  * LOCALE_NOUSEROVERRIDE here as well?
01855  */
01856 BOOL WINAPI EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc, LCID lcid, DWORD flags)
01857 {
01858     char buf[256];
01859 
01860     if (!proc)
01861     {
01862         SetLastError(ERROR_INVALID_PARAMETER);
01863         return FALSE;
01864     }
01865 
01866     switch (flags & ~LOCALE_USE_CP_ACP)
01867     {
01868     case 0:
01869         if (GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT | (flags & LOCALE_USE_CP_ACP), buf, 256))
01870             proc(buf);
01871         break;
01872 
01873     default:
01874         FIXME("Unknown time format (%d)\n", flags);
01875         SetLastError(ERROR_INVALID_PARAMETER);
01876         return FALSE;
01877     }
01878     return TRUE;
01879 }
01880 
01881 /**************************************************************************
01882  *              EnumTimeFormatsW    (KERNEL32.@)
01883  */
01884 BOOL WINAPI EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags)
01885 {
01886     WCHAR buf[256];
01887 
01888     if (!proc)
01889     {
01890         SetLastError(ERROR_INVALID_PARAMETER);
01891         return FALSE;
01892     }
01893 
01894     switch (flags & ~LOCALE_USE_CP_ACP)
01895     {
01896     case 0:
01897         if (GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT | (flags & LOCALE_USE_CP_ACP), buf, 256))
01898             proc(buf);
01899         break;
01900 
01901     default:
01902         FIXME("Unknown time format (%d)\n", flags);
01903         SetLastError(ERROR_INVALID_PARAMETER);
01904         return FALSE;
01905     }
01906     return TRUE;
01907 }
01908 
01909 /******************************************************************************
01910  * NLS_EnumCalendarInfoAW <internal>
01911  * Enumerates calendar information for a specified locale.
01912  *
01913  * PARAMS
01914  *    calinfoproc [I] Pointer to the callback
01915  *    locale      [I] The locale for which to retrieve calendar information.
01916  *                    This parameter can be a locale identifier created by the
01917  *                    MAKELCID macro, or one of the following values:
01918  *                        LOCALE_SYSTEM_DEFAULT
01919  *                            Use the default system locale.
01920  *                        LOCALE_USER_DEFAULT
01921  *                            Use the default user locale.
01922  *    calendar    [I] The calendar for which information is requested, or
01923  *                    ENUM_ALL_CALENDARS.
01924  *    caltype     [I] The type of calendar information to be returned. Note
01925  *                    that only one CALTYPE value can be specified per call
01926  *                    of this function, except where noted.
01927  *    unicode     [I] Specifies if the callback expects a unicode string.
01928  *    ex          [I] Specifies if the callback needs the calendar identifier.
01929  *
01930  * RETURNS
01931  *    Success: TRUE.
01932  *    Failure: FALSE. Use GetLastError() to determine the cause.
01933  *
01934  * NOTES
01935  *    When the ANSI version of this function is used with a Unicode-only LCID,
01936  *    the call can succeed because the system uses the system code page.
01937  *    However, characters that are undefined in the system code page appear
01938  *    in the string as a question mark (?).
01939  *
01940  * TODO
01941  *    The above note should be respected by GetCalendarInfoA.
01942  */
01943 static BOOL NLS_EnumCalendarInfoAW(void *calinfoproc, LCID locale,
01944                   CALID calendar, CALTYPE caltype, BOOL unicode, BOOL ex )
01945 {
01946   WCHAR *buf, *opt = NULL, *iter = NULL;
01947   BOOL ret = FALSE;
01948   int bufSz = 200;      /* the size of the buffer */
01949 
01950   if (calinfoproc == NULL)
01951   {
01952     SetLastError(ERROR_INVALID_PARAMETER);
01953     return FALSE;
01954   }
01955 
01956   buf = HeapAlloc(GetProcessHeap(), 0, bufSz);
01957   if (buf == NULL)
01958   {
01959     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
01960     return FALSE;
01961   }
01962 
01963   if (calendar == ENUM_ALL_CALENDARS)
01964   {
01965     int optSz = GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, NULL, 0);
01966     if (optSz > 1)
01967     {
01968       opt = HeapAlloc(GetProcessHeap(), 0, optSz * sizeof(WCHAR));
01969       if (opt == NULL)
01970       {
01971         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
01972         goto cleanup;
01973       }
01974       if (GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, opt, optSz))
01975         iter = opt;
01976     }
01977     calendar = NLS_GetLocaleNumber(locale, LOCALE_ICALENDARTYPE);
01978   }
01979 
01980   while (TRUE)          /* loop through calendars */
01981   {
01982     do              /* loop until there's no error */
01983     {
01984       if (unicode)
01985         ret = GetCalendarInfoW(locale, calendar, caltype, buf, bufSz / sizeof(WCHAR), NULL);
01986       else ret = GetCalendarInfoA(locale, calendar, caltype, (CHAR*)buf, bufSz / sizeof(CHAR), NULL);
01987 
01988       if (!ret)
01989       {
01990         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
01991         {               /* so resize it */
01992           int newSz;
01993           if (unicode)
01994             newSz = GetCalendarInfoW(locale, calendar, caltype, NULL, 0, NULL) * sizeof(WCHAR);
01995           else newSz = GetCalendarInfoA(locale, calendar, caltype, NULL, 0, NULL) * sizeof(CHAR);
01996           if (bufSz >= newSz)
01997           {
01998             ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz, newSz);
01999             goto cleanup;
02000           }
02001           bufSz = newSz;
02002           WARN("Buffer too small; resizing to %d bytes.\n", bufSz);
02003           buf = HeapReAlloc(GetProcessHeap(), 0, buf, bufSz);
02004           if (buf == NULL)
02005             goto cleanup;
02006         } else goto cleanup;
02007       }
02008     } while (!ret);
02009 
02010     /* Here we are. We pass the buffer to the correct version of
02011      * the callback. Because it's not the same number of params,
02012      * we must check for Ex, but we don't care about Unicode
02013      * because the buffer is already in the correct format.
02014      */
02015     if (ex) {
02016       ret = ((CALINFO_ENUMPROCEXW)calinfoproc)(buf, calendar);
02017     } else
02018       ret = ((CALINFO_ENUMPROCW)calinfoproc)(buf);
02019 
02020     if (!ret) {         /* the callback told to stop */
02021       ret = TRUE;
02022       break;
02023     }
02024 
02025     if ((iter == NULL) || (*iter == 0)) /* no more calendars */
02026       break;
02027 
02028     calendar = 0;
02029     while ((*iter >= '0') && (*iter <= '9'))
02030       calendar = calendar * 10 + *iter++ - '0';
02031 
02032     if (*iter++ != 0)
02033     {
02034       SetLastError(ERROR_BADDB);
02035       ret = FALSE;
02036       break;
02037     }
02038   }
02039 
02040 cleanup:
02041   HeapFree(GetProcessHeap(), 0, opt);
02042   HeapFree(GetProcessHeap(), 0, buf);
02043   return ret;
02044 }
02045 
02046 /******************************************************************************
02047  *      EnumCalendarInfoA   [KERNEL32.@]
02048  *
02049  * See EnumCalendarInfoAW.
02050  */
02051 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
02052                                CALID calendar,CALTYPE caltype )
02053 {
02054   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
02055   return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, FALSE);
02056 }
02057 
02058 /******************************************************************************
02059  *      EnumCalendarInfoW   [KERNEL32.@]
02060  *
02061  * See EnumCalendarInfoAW.
02062  */
02063 BOOL WINAPI EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc,LCID locale,
02064                                CALID calendar,CALTYPE caltype )
02065 {
02066   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
02067   return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, FALSE);
02068 }
02069 
02070 /******************************************************************************
02071  *      EnumCalendarInfoExA [KERNEL32.@]
02072  *
02073  * See EnumCalendarInfoAW.
02074  */
02075 BOOL WINAPI EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc,LCID locale,
02076                                  CALID calendar,CALTYPE caltype )
02077 {
02078   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
02079   return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, TRUE);
02080 }
02081 
02082 /******************************************************************************
02083  *      EnumCalendarInfoExW [KERNEL32.@]
02084  *
02085  * See EnumCalendarInfoAW.
02086  */
02087 BOOL WINAPI EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc,LCID locale,
02088                                  CALID calendar,CALTYPE caltype )
02089 {
02090   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
02091   return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, TRUE);
02092 }
02093 
02094 /*********************************************************************
02095  *  GetCalendarInfoA                (KERNEL32.@)
02096  *
02097  */
02098 int WINAPI GetCalendarInfoA(LCID lcid, CALID Calendar, CALTYPE CalType,
02099                 LPSTR lpCalData, int cchData, LPDWORD lpValue)
02100 {
02101     int ret;
02102     LPWSTR lpCalDataW = NULL;
02103 
02104     if (NLS_IsUnicodeOnlyLcid(lcid))
02105     {
02106       SetLastError(ERROR_INVALID_PARAMETER);
02107       return 0;
02108     }
02109 
02110     if (cchData &&
02111         !(lpCalDataW = HeapAlloc(GetProcessHeap(), 0, cchData*sizeof(WCHAR))))
02112       return 0;
02113 
02114     ret = GetCalendarInfoW(lcid, Calendar, CalType, lpCalDataW, cchData, lpValue);
02115     if(ret && lpCalDataW && lpCalData)
02116       WideCharToMultiByte(CP_ACP, 0, lpCalDataW, cchData, lpCalData, cchData, NULL, NULL);
02117     else if (CalType & CAL_RETURN_NUMBER)
02118         ret *= sizeof(WCHAR);
02119     HeapFree(GetProcessHeap(), 0, lpCalDataW);
02120 
02121     return ret;
02122 }
02123 
02124 /*********************************************************************
02125  *  GetCalendarInfoW                (KERNEL32.@)
02126  *
02127  */
02128 int WINAPI GetCalendarInfoW(LCID Locale, CALID Calendar, CALTYPE CalType,
02129                 LPWSTR lpCalData, int cchData, LPDWORD lpValue)
02130 {
02131     if (CalType & CAL_NOUSEROVERRIDE)
02132     FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
02133     if (CalType & CAL_USE_CP_ACP)
02134     FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
02135 
02136     if (CalType & CAL_RETURN_NUMBER) {
02137         if (!lpValue)
02138         {
02139             SetLastError( ERROR_INVALID_PARAMETER );
02140             return 0;
02141         }
02142     if (lpCalData != NULL)
02143         WARN("lpCalData not NULL (%p) when it should!\n", lpCalData);
02144     if (cchData != 0)
02145         WARN("cchData not 0 (%d) when it should!\n", cchData);
02146     } else {
02147     if (lpValue != NULL)
02148         WARN("lpValue not NULL (%p) when it should!\n", lpValue);
02149     }
02150 
02151     /* FIXME: No verification is made yet wrt Locale
02152      * for the CALTYPES not requiring GetLocaleInfoA */
02153     switch (CalType & ~(CAL_NOUSEROVERRIDE|CAL_RETURN_NUMBER|CAL_USE_CP_ACP)) {
02154     case CAL_ICALINTVALUE:
02155             FIXME("Unimplemented caltype %d\n", CalType & 0xffff);
02156         return 0;
02157     case CAL_SCALNAME:
02158             FIXME("Unimplemented caltype %d\n", CalType & 0xffff);
02159         return 0;
02160     case CAL_IYEAROFFSETRANGE:
02161             FIXME("Unimplemented caltype %d\n", CalType & 0xffff);
02162         return 0;
02163     case CAL_SERASTRING:
02164             FIXME("Unimplemented caltype %d\n", CalType & 0xffff);
02165         return 0;
02166     case CAL_SSHORTDATE:
02167         return GetLocaleInfoW(Locale, LOCALE_SSHORTDATE, lpCalData, cchData);
02168     case CAL_SLONGDATE:
02169         return GetLocaleInfoW(Locale, LOCALE_SLONGDATE, lpCalData, cchData);
02170     case CAL_SDAYNAME1:
02171         return GetLocaleInfoW(Locale, LOCALE_SDAYNAME1, lpCalData, cchData);
02172     case CAL_SDAYNAME2:
02173         return GetLocaleInfoW(Locale, LOCALE_SDAYNAME2, lpCalData, cchData);
02174     case CAL_SDAYNAME3:
02175         return GetLocaleInfoW(Locale, LOCALE_SDAYNAME3, lpCalData, cchData);
02176     case CAL_SDAYNAME4:
02177         return GetLocaleInfoW(Locale, LOCALE_SDAYNAME4, lpCalData, cchData);
02178     case CAL_SDAYNAME5:
02179         return GetLocaleInfoW(Locale, LOCALE_SDAYNAME5, lpCalData, cchData);
02180     case CAL_SDAYNAME6:
02181         return GetLocaleInfoW(Locale, LOCALE_SDAYNAME6, lpCalData, cchData);
02182     case CAL_SDAYNAME7:
02183         return GetLocaleInfoW(Locale, LOCALE_SDAYNAME7, lpCalData, cchData);
02184     case CAL_SABBREVDAYNAME1:
02185         return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME1, lpCalData, cchData);
02186     case CAL_SABBREVDAYNAME2:
02187         return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME2, lpCalData, cchData);
02188     case CAL_SABBREVDAYNAME3:
02189         return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME3, lpCalData, cchData);
02190     case CAL_SABBREVDAYNAME4:
02191         return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME4, lpCalData, cchData);
02192     case CAL_SABBREVDAYNAME5:
02193         return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME5, lpCalData, cchData);
02194     case CAL_SABBREVDAYNAME6:
02195         return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME6, lpCalData, cchData);
02196     case CAL_SABBREVDAYNAME7:
02197         return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME7, lpCalData, cchData);
02198     case CAL_SMONTHNAME1:
02199         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME1, lpCalData, cchData);
02200     case CAL_SMONTHNAME2:
02201         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME2, lpCalData, cchData);
02202     case CAL_SMONTHNAME3:
02203         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME3, lpCalData, cchData);
02204     case CAL_SMONTHNAME4:
02205         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME4, lpCalData, cchData);
02206     case CAL_SMONTHNAME5:
02207         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME5, lpCalData, cchData);
02208     case CAL_SMONTHNAME6:
02209         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME6, lpCalData, cchData);
02210     case CAL_SMONTHNAME7:
02211         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME7, lpCalData, cchData);
02212     case CAL_SMONTHNAME8:
02213         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME8, lpCalData, cchData);
02214     case CAL_SMONTHNAME9:
02215         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME9, lpCalData, cchData);
02216     case CAL_SMONTHNAME10:
02217         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME10, lpCalData, cchData);
02218     case CAL_SMONTHNAME11:
02219         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME11, lpCalData, cchData);
02220     case CAL_SMONTHNAME12:
02221         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME12, lpCalData, cchData);
02222     case CAL_SMONTHNAME13:
02223         return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME13, lpCalData, cchData);
02224     case CAL_SABBREVMONTHNAME1:
02225         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME1, lpCalData, cchData);
02226     case CAL_SABBREVMONTHNAME2:
02227         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME2, lpCalData, cchData);
02228     case CAL_SABBREVMONTHNAME3:
02229         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME3, lpCalData, cchData);
02230     case CAL_SABBREVMONTHNAME4:
02231         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME4, lpCalData, cchData);
02232     case CAL_SABBREVMONTHNAME5:
02233         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME5, lpCalData, cchData);
02234     case CAL_SABBREVMONTHNAME6:
02235         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME6, lpCalData, cchData);
02236     case CAL_SABBREVMONTHNAME7:
02237         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME7, lpCalData, cchData);
02238     case CAL_SABBREVMONTHNAME8:
02239         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME8, lpCalData, cchData);
02240     case CAL_SABBREVMONTHNAME9:
02241         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME9, lpCalData, cchData);
02242     case CAL_SABBREVMONTHNAME10:
02243         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME10, lpCalData, cchData);
02244     case CAL_SABBREVMONTHNAME11:
02245         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME11, lpCalData, cchData);
02246     case CAL_SABBREVMONTHNAME12:
02247         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME12, lpCalData, cchData);
02248     case CAL_SABBREVMONTHNAME13:
02249         return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME13, lpCalData, cchData);
02250     case CAL_SYEARMONTH:
02251         return GetLocaleInfoW(Locale, LOCALE_SYEARMONTH, lpCalData, cchData);
02252     case CAL_ITWODIGITYEARMAX:
02253             if (CalType & CAL_RETURN_NUMBER)
02254             {
02255                 *lpValue = CALINFO_MAX_YEAR;
02256                 return sizeof(DWORD) / sizeof(WCHAR);
02257             }
02258             else
02259             {
02260                 static const WCHAR fmtW[] = {'%','u',0};
02261                 WCHAR buffer[10];
02262                 int ret = snprintfW( buffer, 10, fmtW, CALINFO_MAX_YEAR  ) + 1;
02263                 if (!lpCalData) return ret;
02264                 if (ret <= cchData)
02265                 {
02266                     strcpyW( lpCalData, buffer );
02267                     return ret;
02268                 }
02269                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
02270                 return 0;
02271             }
02272         break;
02273     default:
02274             FIXME("Unknown caltype %d\n",CalType & 0xffff);
02275             SetLastError(ERROR_INVALID_FLAGS);
02276             return 0;
02277     }
02278     return 0;
02279 }
02280 
02281 /*********************************************************************
02282  *  SetCalendarInfoA                (KERNEL32.@)
02283  *
02284  */
02285 int WINAPI  SetCalendarInfoA(LCID Locale, CALID Calendar, CALTYPE CalType, LPCSTR lpCalData)
02286 {
02287     FIXME("(%08x,%08x,%08x,%s): stub\n",
02288       Locale, Calendar, CalType, debugstr_a(lpCalData));
02289     return 0;
02290 }
02291 
02292 /*********************************************************************
02293  *  SetCalendarInfoW                (KERNEL32.@)
02294  *
02295  *
02296  */
02297 int WINAPI  SetCalendarInfoW(LCID Locale, CALID Calendar, CALTYPE CalType, LPCWSTR lpCalData)
02298 {
02299     FIXME("(%08x,%08x,%08x,%s): stub\n",
02300       Locale, Calendar, CalType, debugstr_w(lpCalData));
02301     return 0;
02302 }

Generated on Sat May 26 2012 04:23:08 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.