Fixed definitions of TTTOOLINFOA/W_V1_SIZE and
[wine] / dlls / kernel / lcformat.c
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  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wine/unicode.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(nls);
37
38
39 /******************************************************************************
40  *              get_date_time_formatW   [Internal]
41  *
42  * dateformat is set TRUE if being called for a date, false for a time
43  *
44  * This function implements stuff for GetDateFormat() and
45  * GetTimeFormat().
46  *
47  *  d    single-digit (no leading zero) day (of month)
48  *  dd   two-digit day (of month)
49  *  ddd  short day-of-week name
50  *  dddd long day-of-week name
51  *  M    single-digit month
52  *  MM   two-digit month
53  *  MMM  short month name
54  *  MMMM full month name
55  *  y    two-digit year, no leading 0
56  *  yy   two-digit year
57  *  yyyy four-digit year
58  *  gg   era string
59  *  h    hours with no leading zero (12-hour)
60  *  hh   hours with full two digits
61  *  H    hours with no leading zero (24-hour)
62  *  HH   hours with full two digits
63  *  m    minutes with no leading zero
64  *  mm   minutes with full two digits
65  *  s    seconds with no leading zero
66  *  ss   seconds with full two digits
67  *  t    time marker (A or P)
68  *  tt   time marker (AM, PM)
69  *  ''   used to quote literal characters
70  *  ''   (within a quoted string) indicates a literal '
71  *
72  * If TIME_NOMINUTESORSECONDS or TIME_NOSECONDS is specified, the function
73  *  removes the separator(s) preceding the minutes and/or seconds element(s).
74  *
75  * If TIME_NOTIMEMARKER is specified, the function removes the separator(s)
76  *  preceding and following the time marker.
77  *
78  * If TIME_FORCE24HOURFORMAT is specified, the function displays any existing
79  *  time marker, unless the TIME_NOTIMEMARKER flag is also set.
80  *
81  * These functions REQUIRE valid locale, date,  and format.
82  *
83  * If the time or date is invalid, return 0 and set ERROR_INVALID_PARAMETER
84  *
85  * Return value is the number of characters written, or if outlen is zero
86  *      it is the number of characters required for the output including
87  *      the terminating null.
88  */
89 static INT get_date_time_formatW(LCID locale, DWORD flags, DWORD tflags,
90                                  const SYSTEMTIME* xtime, LPCWSTR format,
91                                  LPWSTR output, INT outlen, int dateformat)
92 {
93    INT     outpos;
94    INT     lastFormatPos; /* the position in the output buffer of */
95                           /* the end of the output from the last formatting */
96                           /* character */
97    BOOL    dropUntilNextFormattingChar = FALSE; /* TIME_NOTIMEMARKER drops
98                                 all of the text around the dropped marker,
99                                 eg. "h@!t@!m" becomes "hm" */
100
101    /* make a debug report */
102    TRACE("args: 0x%lx, 0x%lx, 0x%lx, time(d=%d,h=%d,m=%d,s=%d), fmt:%s (at %p), "
103               "%p with max len %d\n",
104          locale, flags, tflags,
105          xtime->wDay, xtime->wHour, xtime->wMinute, xtime->wSecond,
106          debugstr_w(format), format, output, outlen);
107
108    /* initialize state variables */
109    outpos = 0;
110    lastFormatPos = 0;
111
112    while (*format) {
113       /* Literal string: Maybe terminated early by a \0 */
114       if (*format == (WCHAR) '\'')
115       {
116          format++;
117
118          /* We loop while we haven't reached the end of the format string */
119          /* and until we haven't found another "'" character */
120          while (*format)
121          {
122             /* we found what might be the close single quote mark */
123             /* we need to make sure there isn't another single quote */
124             /* after it, if there is we need to skip over this quote mark */
125             /* as the user is trying to put a single quote mark in their output */
126             if (*format == (WCHAR) '\'')
127             {
128                format++;
129                if (*format != '\'')
130                {
131                   break; /* It was a terminating quote */
132                }
133             }
134
135             /* if outlen is zero then we are couting the number of */
136             /* characters of space we need to output this text, don't */
137             /* modify the output buffer */
138             if (!outlen)
139             {
140                outpos++;   /* We are counting */;
141             }  else if (outpos >= outlen)
142             {
143                goto too_short;
144             } else
145             {
146                /* even drop literal strings */
147                if(!dropUntilNextFormattingChar)
148                {
149                  output[outpos] = *format;
150                  outpos++;
151                }
152             }
153             format++;
154          }
155       } else if ( (dateformat &&  (*format=='d' ||
156                                    *format=='M' ||
157                                    *format=='y' ||
158                                    *format=='g')  ) ||
159                   (!dateformat && (*format=='H' ||
160                                    *format=='h' ||
161                                    *format=='m' ||
162                                    *format=='s' ||
163                                    *format=='t') )    )
164      /* if processing a date and we have a date formatting character, OR */
165      /* if we are processing a time and we have a time formatting character */
166      {
167          int type, count;
168          char    tmp[16];
169          WCHAR   buf[40];
170          int     buflen=0;
171          type = *format;
172          format++;
173
174          /* clear out the drop text flag if we are in here */
175          dropUntilNextFormattingChar = FALSE;
176
177          /* count up the number of the same letter values in a row that */
178          /* we get, this lets us distinguish between "s" and "ss" and it */
179          /* eliminates the duplicate to simplify the below case statement */
180          for (count = 1; *format == type; format++)
181             count++;
182
183          buf[0] = 0; /* always null terminate the buffer */
184
185          switch(type)
186          {
187           case 'd':
188             if        (count >= 4) {
189                GetLocaleInfoW(locale,
190                              LOCALE_SDAYNAME1 + (xtime->wDayOfWeek +6)%7,
191                              buf, sizeof(buf)/sizeof(WCHAR) );
192             } else if (count == 3) {
193                GetLocaleInfoW(locale,
194                                 LOCALE_SABBREVDAYNAME1 +
195                                 (xtime->wDayOfWeek +6)%7,
196                                 buf, sizeof(buf)/sizeof(WCHAR) );
197             } else {
198                 sprintf( tmp, "%.*d", count, xtime->wDay );
199                 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
200             }
201             break;
202
203           case 'M':
204             if        (count >= 4) {
205                GetLocaleInfoW(locale,  LOCALE_SMONTHNAME1 +
206                                 xtime->wMonth -1, buf,
207                                 sizeof(buf)/sizeof(WCHAR) );
208             } else if (count == 3) {
209                GetLocaleInfoW(locale,  LOCALE_SABBREVMONTHNAME1 +
210                                 xtime->wMonth -1, buf,
211                                 sizeof(buf)/sizeof(WCHAR) );
212             } else {
213                 sprintf( tmp, "%.*d", count, xtime->wMonth );
214                 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
215             }
216             break;
217           case 'y':
218             if        (count >= 4) {
219                 sprintf( tmp, "%d", xtime->wYear );
220             } else {
221                 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wYear % 100 );
222             }
223             MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
224             break;
225
226           case 'g':
227             if        (count == 2) {
228                FIXME("LOCALE_ICALENDARTYPE unimplemented\n");
229                strcpy( tmp, "AD" );
230             } else {
231                /* Win API sez we copy it verbatim */
232                 strcpy( tmp, "g" );
233             }
234             MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
235             break;
236
237           case 'h':
238               /* fallthrough if we are forced to output in 24 hour format */
239               if(!(tflags & TIME_FORCE24HOURFORMAT))
240               {
241                 /* hours 1:00-12:00 --- is this right? */
242                 /* NOTE: 0000 hours is also 12 midnight */
243                 sprintf( tmp, "%.*d", count > 2 ? 2 : count,
244                         xtime->wHour == 0 ? 12 : (xtime->wHour-1)%12 +1);
245                 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
246                 break;
247               }
248           case 'H':
249               sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wHour );
250               MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
251               break;
252
253           case 'm':
254               /* if TIME_NOMINUTESORSECONDS don't display minutes */
255               if(!(tflags & TIME_NOMINUTESORSECONDS))
256               {
257                 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wMinute );
258                 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
259               } else
260               {
261                 outpos = lastFormatPos;
262               }
263               break;
264
265           case 's':
266               /* if we have a TIME_NOSECONDS or TIME_NOMINUTESORSECONDS
267                         flag then don't display seconds */
268               if(!(tflags & TIME_NOSECONDS) && !(tflags &
269                         TIME_NOMINUTESORSECONDS))
270               {
271                 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wSecond );
272                 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
273               } else
274               {
275                 outpos = lastFormatPos;
276               }
277               break;
278
279           case 't':
280             if(!(tflags & TIME_NOTIMEMARKER))
281             {
282               GetLocaleInfoW(locale, (xtime->wHour < 12) ?
283                              LOCALE_S1159 : LOCALE_S2359,
284                              buf, sizeof(buf) );
285               if (count == 1)
286               {
287                  buf[1] = 0;
288               }
289             } else
290             {
291                 outpos = lastFormatPos; /* remove any prior text up until
292                         the output due to formatting characters */
293                 dropUntilNextFormattingChar = TRUE; /* drop everything
294                         until we hit the next formatting character */
295             }
296             break;
297          }
298
299          /* cat buf onto the output */
300          buflen = strlenW(buf);
301
302          /* we are counting how many characters we need for output */
303          /* don't modify the output buffer... */
304          if (!outlen)
305             /* We are counting */;
306          else if (outpos + buflen < outlen) {
307             strcpyW( output + outpos, buf );
308          } else {
309             lstrcpynW( output + outpos, buf, outlen - outpos );
310             /* Is this an undocumented feature we are supporting? */
311             goto too_short;
312          }
313          outpos += buflen;
314          lastFormatPos = outpos; /* record the end of the formatting text we just output */
315       } else /* we are processing a NON formatting character */
316       {
317          /* a literal character */
318          if (!outlen)
319          {
320             outpos++;   /* We are counting */;
321          }
322          else if (outpos >= outlen)
323          {
324             goto too_short;
325          }
326          else /* just copy the character into the output buffer */
327          {
328             /* unless we are dropping characters */
329             if(!dropUntilNextFormattingChar)
330             {
331                output[outpos] = *format;
332                outpos++;
333             }
334          }
335          format++;
336       }
337    }
338
339    /* final string terminator and sanity check */
340    if (!outlen)
341       /* We are counting */;
342    else if (outpos >= outlen)
343       goto too_short;
344    else
345       output[outpos] = '\0';
346
347    outpos++; /* add one for the terminating null character */
348
349    TRACE(" returning %d %s\n", outpos, debugstr_w(output));
350    return outpos;
351
352 too_short:
353    SetLastError(ERROR_INSUFFICIENT_BUFFER);
354    WARN(" buffer overflow\n");
355    return 0;
356 }
357
358
359 /******************************************************************************
360  *              GetDateFormatA  [KERNEL32.@]
361  * Makes an ASCII string of the date
362  *
363  * Acts the same as GetDateFormatW(),  except that it's ASCII.
364  */
365 INT WINAPI GetDateFormatA( LCID locale, DWORD flags, const SYSTEMTIME* xtime,
366                            LPCSTR format, LPSTR date, INT datelen )
367 {
368   INT ret;
369   LPWSTR wformat = NULL;
370   LPWSTR wdate = NULL;
371
372   if (format)
373   {
374     wformat = HeapAlloc(GetProcessHeap(), 0,
375                         (strlen(format) + 1) * sizeof(wchar_t));
376
377     MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
378   }
379
380   if (date && datelen)
381   {
382     wdate = HeapAlloc(GetProcessHeap(), 0,
383                       (datelen + 1) * sizeof(wchar_t));
384   }
385
386   ret = GetDateFormatW(locale, flags, xtime, wformat, wdate, datelen);
387
388   if (wdate)
389   {
390     WideCharToMultiByte(CP_ACP, 0, wdate, ret, date, datelen, NULL, NULL);
391     HeapFree(GetProcessHeap(), 0, wdate);
392   }
393
394   if (wformat)
395   {
396     HeapFree(GetProcessHeap(), 0, wformat);
397   }
398
399   return ret;
400 }
401
402
403 /******************************************************************************
404  *              GetDateFormatW  [KERNEL32.@]
405  * Makes a Unicode string of the date
406  *
407  * This function uses format to format the date,  or,  if format
408  * is NULL, uses the default for the locale.  format is a string
409  * of literal fields and characters as follows:
410  *
411  * - d    single-digit (no leading zero) day (of month)
412  * - dd   two-digit day (of month)
413  * - ddd  short day-of-week name
414  * - dddd long day-of-week name
415  * - M    single-digit month
416  * - MM   two-digit month
417  * - MMM  short month name
418  * - MMMM full month name
419  * - y    two-digit year, no leading 0
420  * - yy   two-digit year
421  * - yyyy four-digit year
422  * - gg   era string
423  *
424  * Accepts & returns sizes as counts of Unicode characters.
425  */
426 INT WINAPI GetDateFormatW( LCID locale, DWORD flags, const SYSTEMTIME* xtime,
427                            LPCWSTR format, LPWSTR date, INT datelen)
428 {
429     WCHAR format_buf[40];
430     LPCWSTR thisformat;
431     SYSTEMTIME t;
432     LCID thislocale;
433     INT ret;
434     FILETIME ft;
435     BOOL res;
436
437     TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",
438           locale,flags,xtime,debugstr_w(format),date,datelen);
439
440     /* Tests */
441     if (flags && format) /* if lpFormat is non-null then flags must be zero */
442     {
443         SetLastError (ERROR_INVALID_FLAGS);
444         return 0;
445     }
446     if (datelen && !date)
447     {
448         SetLastError (ERROR_INVALID_PARAMETER);
449         return 0;
450     }
451     if (!locale)
452     {
453         locale = LOCALE_SYSTEM_DEFAULT;
454     }
455
456     if (locale == LOCALE_SYSTEM_DEFAULT)
457     {
458         thislocale = GetSystemDefaultLCID();
459     } else if (locale == LOCALE_USER_DEFAULT)
460     {
461         thislocale = GetUserDefaultLCID();
462     } else
463     {
464         thislocale = locale;
465     }
466
467     /* check for invalid flag combinations */
468     if((flags & DATE_LTRREADING) && (flags & DATE_RTLREADING))
469     {
470         SetLastError (ERROR_INVALID_FLAGS);
471         return 0;
472     }
473
474     /* DATE_SHORTDATE, DATE_LONGDATE and DATE_YEARMONTH are mutually */
475     /* exclusive */
476     if((flags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
477          && !((flags & DATE_SHORTDATE) ^ (flags &
478         DATE_LONGDATE) ^ (flags & DATE_YEARMONTH)))
479     {
480         SetLastError (ERROR_INVALID_FLAGS);
481         return 0;
482     }
483
484     /* if the user didn't pass in a pointer to the current time we read it */
485     /* here */
486     if (xtime == NULL)
487     {
488         GetSystemTime(&t);
489     } else
490     {
491         /* NOTE: check here before we perform the SystemTimeToFileTime conversion */
492         /*  because this conversion will fix invalid time values */
493         /* check to see if the time/date is valid */
494         /* set ERROR_INVALID_PARAMETER and return 0 if invalid */
495         if((xtime->wDay > 31) || (xtime->wMonth > 12))
496         {
497           SetLastError(ERROR_INVALID_PARAMETER);
498           return 0;
499         }
500         /* For all we know the day of week and the time may be absolute
501          * rubbish.  Therefore if we are going to use conversion through
502          * FileTime we had better use a clean time (and hopefully we won't
503          * fall over any timezone complications).
504          * If we go with an alternative method of correcting the day of week
505          * (e.g. Zeller's congruence) then we won't need to, but we will need
506          * to check the date.
507          */
508         memset (&t, 0, sizeof(t));
509         t.wYear = xtime->wYear;
510         t.wMonth = xtime->wMonth;
511         t.wDay = xtime->wDay;
512
513         /* Silently correct wDayOfWeek by transforming to FileTime and back again */
514         res=SystemTimeToFileTime(&t,&ft);
515
516         /* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER  on error */
517         if(!res)
518         {
519             SetLastError(ERROR_INVALID_PARAMETER);
520             return 0;
521         }
522         FileTimeToSystemTime(&ft,&t);
523     }
524
525     if (format == NULL)
526     {
527         GetLocaleInfoW(thislocale, ((flags&DATE_LONGDATE)
528                                     ? LOCALE_SLONGDATE
529                                     : LOCALE_SSHORTDATE),
530                        format_buf, sizeof(format_buf)/sizeof(*format_buf));
531         thisformat = format_buf;
532     } else
533     {
534         thisformat = format;
535     }
536
537     ret = get_date_time_formatW(thislocale, flags, 0, &t, thisformat, date, datelen, 1);
538
539     TRACE("GetDateFormatW() returning %d, with data=%s\n",
540           ret, debugstr_w(date));
541     return ret;
542 }
543
544
545 /**************************************************************************
546  *              EnumDateFormatsA        (KERNEL32.@)
547  */
548 BOOL WINAPI EnumDateFormatsA( DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale,  DWORD dwFlags)
549 {
550   LCID Loc = GetUserDefaultLCID();
551   if(!lpDateFmtEnumProc)
552     {
553       SetLastError(ERROR_INVALID_PARAMETER);
554       return FALSE;
555     }
556
557   switch( Loc )
558  {
559
560    case 0x00000407:  /* (Loc,"de_DE") */
561    {
562     switch(dwFlags)
563     {
564       case DATE_SHORTDATE:
565         if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
566         if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
567         if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
568         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
569         return TRUE;
570       case DATE_LONGDATE:
571         if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
572         if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
573         if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
574         return TRUE;
575       default:
576         FIXME("Unknown date format (%ld)\n", dwFlags);
577         SetLastError(ERROR_INVALID_PARAMETER);
578         return FALSE;
579      }
580    }
581
582    case 0x0000040c:  /* (Loc,"fr_FR") */
583    {
584     switch(dwFlags)
585     {
586       case DATE_SHORTDATE:
587         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
588         if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
589         if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
590         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
591         return TRUE;
592       case DATE_LONGDATE:
593         if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
594         if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
595         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
596         return TRUE;
597       default:
598         FIXME("Unknown date format (%ld)\n", dwFlags);
599         SetLastError(ERROR_INVALID_PARAMETER);
600         return FALSE;
601      }
602    }
603
604    case 0x00000c0c:  /* (Loc,"fr_CA") */
605    {
606     switch(dwFlags)
607     {
608       case DATE_SHORTDATE:
609         if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
610         if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
611         if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
612         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
613         return TRUE;
614       case DATE_LONGDATE:
615         if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
616         if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
617         return TRUE;
618       default:
619         FIXME("Unknown date format (%ld)\n", dwFlags);
620         SetLastError(ERROR_INVALID_PARAMETER);
621         return FALSE;
622      }
623    }
624
625    case 0x00000809:  /* (Loc,"en_UK") */
626   {
627    switch(dwFlags)
628     {
629       case DATE_SHORTDATE:
630         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
631         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
632         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
633         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
634         return TRUE;
635       case DATE_LONGDATE:
636         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
637         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
638         return TRUE;
639       default:
640         FIXME("Unknown date format (%ld)\n", dwFlags);
641         SetLastError(ERROR_INVALID_PARAMETER);
642         return FALSE;
643     }
644   }
645
646    case 0x00000c09:  /* (Loc,"en_AU") */
647   {
648    switch(dwFlags)
649     {
650       case DATE_SHORTDATE:
651         if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
652         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
653         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
654         return TRUE;
655       case DATE_LONGDATE:
656         if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
657         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
658         return TRUE;
659       default:
660         FIXME("Unknown date format (%ld)\n", dwFlags);
661         SetLastError(ERROR_INVALID_PARAMETER);
662         return FALSE;
663     }
664   }
665
666    case 0x00001009:  /* (Loc,"en_CA") */
667   {
668    switch(dwFlags)
669     {
670       case DATE_SHORTDATE:
671         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
672         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
673         if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
674         if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
675         return TRUE;
676       case DATE_LONGDATE:
677         if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
678         if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
679         return TRUE;
680       default:
681         FIXME("Unknown date format (%ld)\n", dwFlags);
682         SetLastError(ERROR_INVALID_PARAMETER);
683         return FALSE;
684     }
685   }
686
687    case 0x00001409:  /* (Loc,"en_NZ") */
688   {
689    switch(dwFlags)
690     {
691       case DATE_SHORTDATE:
692         if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
693         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
694         if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
695         return TRUE;
696       case DATE_LONGDATE:
697         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
698         if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
699         return TRUE;
700       default:
701         FIXME("Unknown date format (%ld)\n", dwFlags);
702         SetLastError(ERROR_INVALID_PARAMETER);
703         return FALSE;
704     }
705   }
706
707    case 0x00001809:  /* (Loc,"en_IE") */
708   {
709    switch(dwFlags)
710     {
711       case DATE_SHORTDATE:
712         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
713         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
714         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
715         return TRUE;
716       case DATE_LONGDATE:
717         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
718         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
719         return TRUE;
720       default:
721         FIXME("Unknown date format (%ld)\n", dwFlags);
722         SetLastError(ERROR_INVALID_PARAMETER);
723         return FALSE;
724     }
725   }
726
727    case 0x00001c09:  /* (Loc,"en_ZA") */
728   {
729    switch(dwFlags)
730     {
731       case DATE_SHORTDATE:
732         if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
733         return TRUE;
734       case DATE_LONGDATE:
735         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
736         return TRUE;
737       default:
738         FIXME("Unknown date format (%ld)\n", dwFlags);
739         SetLastError(ERROR_INVALID_PARAMETER);
740         return FALSE;
741     }
742   }
743
744    case 0x00002009:  /* (Loc,"en_JM") */
745   {
746    switch(dwFlags)
747     {
748       case DATE_SHORTDATE:
749         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
750         return TRUE;
751       case DATE_LONGDATE:
752         if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
753         if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
754         if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
755         if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
756         return TRUE;
757       default:
758         FIXME("Unknown date format (%ld)\n", dwFlags);
759         SetLastError(ERROR_INVALID_PARAMETER);
760         return FALSE;
761     }
762   }
763
764    case 0x00002809:  /* (Loc,"en_BZ") */
765    case 0x00002c09:  /* (Loc,"en_TT") */
766   {
767    switch(dwFlags)
768     {
769       case DATE_SHORTDATE:
770         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
771         return TRUE;
772       case DATE_LONGDATE:
773         if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
774         return TRUE;
775       default:
776         FIXME("Unknown date format (%ld)\n", dwFlags);
777         SetLastError(ERROR_INVALID_PARAMETER);
778         return FALSE;
779     }
780   }
781
782    default:  /* default to US English "en_US" */
783   {
784    switch(dwFlags)
785     {
786       case DATE_SHORTDATE:
787         if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
788         if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
789         if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
790         if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
791         if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
792         if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
793         return TRUE;
794       case DATE_LONGDATE:
795         if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
796         if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
797         if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
798         if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
799         return TRUE;
800       default:
801         FIXME("Unknown date format (%ld)\n", dwFlags);
802         SetLastError(ERROR_INVALID_PARAMETER);
803         return FALSE;
804     }
805   }
806  }
807 }
808
809 /**************************************************************************
810  *              EnumDateFormatsW        (KERNEL32.@)
811  */
812 BOOL WINAPI EnumDateFormatsW( DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags )
813 {
814   FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
815   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
816   return FALSE;
817 }
818
819 /**************************************************************************
820  *              EnumTimeFormatsA        (KERNEL32.@)
821  */
822 BOOL WINAPI EnumTimeFormatsA( TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
823 {
824   LCID Loc = GetUserDefaultLCID();
825   if(!lpTimeFmtEnumProc)
826     {
827       SetLastError(ERROR_INVALID_PARAMETER);
828       return FALSE;
829     }
830   if(dwFlags)
831     {
832       FIXME("Unknown time format (%ld)\n", dwFlags);
833     }
834
835   switch( Loc )
836  {
837    case 0x00000407:  /* (Loc,"de_DE") */
838    {
839     if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
840     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
841     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
842     if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
843     if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
844     return TRUE;
845    }
846
847    case 0x0000040c:  /* (Loc,"fr_FR") */
848    case 0x00000c0c:  /* (Loc,"fr_CA") */
849    {
850     if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
851     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
852     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
853     if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
854     if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
855     return TRUE;
856    }
857
858    case 0x00000809:  /* (Loc,"en_UK") */
859    case 0x00000c09:  /* (Loc,"en_AU") */
860    case 0x00001409:  /* (Loc,"en_NZ") */
861    case 0x00001809:  /* (Loc,"en_IE") */
862    {
863     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
864     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
865     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
866     return TRUE;
867    }
868
869    case 0x00001c09:  /* (Loc,"en_ZA") */
870    case 0x00002809:  /* (Loc,"en_BZ") */
871    case 0x00002c09:  /* (Loc,"en_TT") */
872    {
873     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
874     if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
875     return TRUE;
876    }
877
878    default:  /* default to US style "en_US" */
879    {
880     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
881     if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
882     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
883     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
884     return TRUE;
885    }
886  }
887 }
888
889 /**************************************************************************
890  *              EnumTimeFormatsW        (KERNEL32.@)
891  */
892 BOOL WINAPI EnumTimeFormatsW( TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
893 {
894   FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
895   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
896   return FALSE;
897 }
898
899 /**************************************************************************
900  *           This function is used just locally !
901  *  Description: Inverts a string.
902  */
903 static void invert_string(char* string)
904 {
905     int i, j;
906
907     for (i = 0, j = strlen(string) - 1; i < j; i++, j--)
908     {
909         char ch = string[i];
910         string[i] = string[j];
911         string[j] = ch;
912     }
913 }
914
915 /***************************************************************************************
916  *           This function is used just locally !
917  *  Description: Test if the given string (psNumber) is valid or not.
918  *               The valid characters are the following:
919  *               - Characters '0' through '9'.
920  *               - One decimal point (dot) if the number is a floating-point value.
921  *               - A minus sign in the first character position if the number is
922  *                 a negative value.
923  *              If the function succeeds, psBefore/psAfter will point to the string
924  *              on the right/left of the decimal symbol. pbNegative indicates if the
925  *              number is negative.
926  */
927 static INT get_number_components(char* pInput, char* psBefore, char* psAfter, BOOL* pbNegative)
928 {
929     char sNumberSet[] = "0123456789";
930     BOOL bInDecimal = FALSE;
931
932         /* Test if we do have a minus sign */
933         if ( *pInput == '-' )
934         {
935                 *pbNegative = TRUE;
936                 pInput++; /* Jump to the next character. */
937         }
938
939         while(*pInput != '\0')
940         {
941                 /* Do we have a valid numeric character */
942                 if ( strchr(sNumberSet, *pInput) != NULL )
943                 {
944                         if (bInDecimal == TRUE)
945                 *psAfter++ = *pInput;
946                         else
947                 *psBefore++ = *pInput;
948                 }
949                 else
950                 {
951                         /* Is this a decimal point (dot) */
952                         if ( *pInput == '.' )
953                         {
954                                 /* Is it the first time we find it */
955                                 if ((bInDecimal == FALSE))
956                                         bInDecimal = TRUE;
957                                 else
958                                         return -1; /* ERROR: Invalid parameter */
959                         }
960                         else
961                         {
962                                 /* It's neither a numeric character, nor a decimal point.
963                                  * Thus, return an error.
964                  */
965                                 return -1;
966                         }
967                 }
968         pInput++;
969         }
970
971         /* Add an End of Line character to the output buffers */
972         *psBefore = '\0';
973         *psAfter = '\0';
974
975         return 0;
976 }
977
978 /**************************************************************************
979  *           This function is used just locally !
980  *  Description: A number could be formatted using different numbers
981  *               of "digits in group" (example: 4;3;2;0).
982  *               The first parameter of this function is an array
983  *               containing the rule to be used. Its format is the following:
984  *               |NDG|DG1|DG2|...|0|
985  *               where NDG is the number of used "digits in group" and DG1, DG2,
986  *               are the corresponding "digits in group".
987  *               Thus, this function returns the grouping value in the array
988  *               pointed by the second parameter.
989  */
990 static INT get_grouping(char* sRule, INT index)
991 {
992     char    sData[2], sRuleSize[2];
993     INT     nData, nRuleSize;
994
995     memcpy(sRuleSize, sRule, 1);
996     memcpy(sRuleSize+1, "\0", 1);
997     nRuleSize = atoi(sRuleSize);
998
999     if (index > 0 && index < nRuleSize)
1000     {
1001         memcpy(sData, sRule+index, 1);
1002         memcpy(sData+1, "\0", 1);
1003         nData = atoi(sData);
1004     }
1005
1006     else
1007     {
1008         memcpy(sData, sRule+nRuleSize-1, 1);
1009         memcpy(sData+1, "\0", 1);
1010         nData = atoi(sData);
1011     }
1012
1013     return nData;
1014 }
1015
1016 /**************************************************************************
1017  *              GetNumberFormatA        (KERNEL32.@)
1018  */
1019 INT WINAPI GetNumberFormatA(LCID locale, DWORD dwflags,
1020                                LPCSTR lpvalue,   const NUMBERFMTA * lpFormat,
1021                                LPSTR lpNumberStr, int cchNumber)
1022 {
1023     char   sNumberDigits[3], sDecimalSymbol[5], sDigitsInGroup[11], sDigitGroupSymbol[5], sILZero[2];
1024     INT    nNumberDigits, nNumberDecimal, i, j, nCounter, nStep, nRuleIndex, nGrouping, nDigits, retVal, nLZ;
1025     char   sNumber[128], sDestination[128], sDigitsAfterDecimal[10], sDigitsBeforeDecimal[128];
1026     char   sRule[10], sSemiColumn[]=";", sBuffer[5], sNegNumber[2];
1027     char   *pStr = NULL, *pTmpStr = NULL;
1028     LCID   systemDefaultLCID;
1029     BOOL   bNegative = FALSE;
1030     enum   Operations
1031     {
1032         USE_PARAMETER,
1033         USE_LOCALEINFO,
1034         USE_SYSTEMDEFAULT,
1035         RETURN_ERROR
1036     } used_operation;
1037
1038     strncpy(sNumber, lpvalue, 128);
1039     sNumber[127] = '\0';
1040
1041     /* Make sure we have a valid input string, get the number
1042      * of digits before and after the decimal symbol, and check
1043      * if this is a negative number.
1044      */
1045     if (get_number_components(sNumber, sDigitsBeforeDecimal, sDigitsAfterDecimal, &bNegative) != -1)
1046     {
1047         nNumberDecimal = strlen(sDigitsBeforeDecimal);
1048         nDigits = strlen(sDigitsAfterDecimal);
1049     }
1050     else
1051     {
1052         SetLastError(ERROR_INVALID_PARAMETER);
1053         return 0;
1054     }
1055
1056     /* Which source will we use to format the string */
1057     used_operation = RETURN_ERROR;
1058     if (lpFormat != NULL)
1059     {
1060         if (dwflags == 0)
1061             used_operation = USE_PARAMETER;
1062     }
1063     else
1064     {
1065         if (dwflags & LOCALE_NOUSEROVERRIDE)
1066             used_operation = USE_SYSTEMDEFAULT;
1067         else
1068             used_operation = USE_LOCALEINFO;
1069     }
1070
1071     /* Load the fields we need */
1072     switch(used_operation)
1073     {
1074     case USE_LOCALEINFO:
1075         GetLocaleInfoA(locale, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
1076         GetLocaleInfoA(locale, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
1077         GetLocaleInfoA(locale, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
1078         GetLocaleInfoA(locale, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
1079         GetLocaleInfoA(locale, LOCALE_ILZERO, sILZero, sizeof(sILZero));
1080         GetLocaleInfoA(locale, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
1081         break;
1082     case USE_PARAMETER:
1083         sprintf(sNumberDigits, "%d",lpFormat->NumDigits);
1084         strcpy(sDecimalSymbol, lpFormat->lpDecimalSep);
1085         sprintf(sDigitsInGroup, "%d;0",lpFormat->Grouping);
1086         /* Win95-WinME only allow 0-9 for grouping, no matter what MSDN says. */
1087         strcpy(sDigitGroupSymbol, lpFormat->lpThousandSep);
1088         sprintf(sILZero, "%d",lpFormat->LeadingZero);
1089         sprintf(sNegNumber, "%d",lpFormat->NegativeOrder);
1090         break;
1091     case USE_SYSTEMDEFAULT:
1092         systemDefaultLCID = GetSystemDefaultLCID();
1093         GetLocaleInfoA(systemDefaultLCID, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
1094         GetLocaleInfoA(systemDefaultLCID, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
1095         GetLocaleInfoA(systemDefaultLCID, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
1096         GetLocaleInfoA(systemDefaultLCID, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
1097         GetLocaleInfoA(systemDefaultLCID, LOCALE_ILZERO, sILZero, sizeof(sILZero));
1098         GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
1099         break;
1100     default:
1101         SetLastError(ERROR_INVALID_PARAMETER);
1102         return 0;
1103     }
1104
1105     nNumberDigits = atoi(sNumberDigits);
1106
1107     /* Remove the ";" */
1108     i=0;
1109     j = 1;
1110     for (nCounter=0; nCounter<strlen(sDigitsInGroup); nCounter++)
1111     {
1112         if ( memcmp(sDigitsInGroup + nCounter, sSemiColumn, 1) != 0 )
1113         {
1114             memcpy(sRule + j, sDigitsInGroup + nCounter, 1);
1115             i++;
1116             j++;
1117         }
1118     }
1119     sprintf(sBuffer, "%d", i);
1120     memcpy(sRule, sBuffer, 1); /* Number of digits in the groups ( used by get_grouping() ) */
1121     memcpy(sRule + j, "\0", 1);
1122
1123     /* First, format the digits before the decimal. */
1124     if ((nNumberDecimal>0) && (atoi(sDigitsBeforeDecimal) != 0))
1125     {
1126         /* Working on an inverted string is easier ! */
1127         invert_string(sDigitsBeforeDecimal);
1128
1129         nStep = nCounter = i = j = 0;
1130         nRuleIndex = 1;
1131         nGrouping = get_grouping(sRule, nRuleIndex);
1132         if (nGrouping == 0) /* If the first grouping is zero */
1133             nGrouping = nNumberDecimal; /* Don't do grouping */
1134
1135         /* Here, we will loop until we reach the end of the string.
1136          * An internal counter (j) is used in order to know when to
1137          * insert the "digit group symbol".
1138          */
1139         while (nNumberDecimal > 0)
1140         {
1141             i = nCounter + nStep;
1142             memcpy(sDestination + i, sDigitsBeforeDecimal + nCounter, 1);
1143             nCounter++;
1144             j++;
1145             if (j >= nGrouping)
1146             {
1147                 j = 0;
1148                 if (nRuleIndex < sRule[0])
1149                     nRuleIndex++;
1150                 nGrouping = get_grouping(sRule, nRuleIndex);
1151                 memcpy(sDestination + i+1, sDigitGroupSymbol, strlen(sDigitGroupSymbol));
1152                 nStep+= strlen(sDigitGroupSymbol);
1153             }
1154
1155             nNumberDecimal--;
1156         }
1157
1158         memcpy(sDestination + i+1, "\0", 1);
1159         /* Get the string in the right order ! */
1160         invert_string(sDestination);
1161      }
1162      else
1163      {
1164         nLZ = atoi(sILZero);
1165         if (nLZ != 0)
1166         {
1167             /* Use 0.xxx instead of .xxx */
1168             memcpy(sDestination, "0", 1);
1169             memcpy(sDestination+1, "\0", 1);
1170         }
1171         else
1172             memcpy(sDestination, "\0", 1);
1173
1174      }
1175
1176     /* Second, format the digits after the decimal. */
1177     j = 0;
1178     nCounter = nNumberDigits;
1179     if ( (nDigits>0) && (pStr = strstr (sNumber, ".")) )
1180     {
1181         i = strlen(sNumber) - strlen(pStr) + 1;
1182         strncpy ( sDigitsAfterDecimal, sNumber + i, nNumberDigits);
1183         j = strlen(sDigitsAfterDecimal);
1184         if (j < nNumberDigits)
1185             nCounter = nNumberDigits-j;
1186     }
1187     for (i=0;i<nCounter;i++)
1188          memcpy(sDigitsAfterDecimal+i+j, "0", 1);
1189     memcpy(sDigitsAfterDecimal + nNumberDigits, "\0", 1);
1190
1191     i = strlen(sDestination);
1192     j = strlen(sDigitsAfterDecimal);
1193     /* Finally, construct the resulting formatted string. */
1194
1195     for (nCounter=0; nCounter<i; nCounter++)
1196         memcpy(sNumber + nCounter, sDestination + nCounter, 1);
1197
1198     memcpy(sNumber + nCounter, sDecimalSymbol, strlen(sDecimalSymbol));
1199
1200     for (i=0; i<j; i++)
1201         memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), sDigitsAfterDecimal + i, 1);
1202     memcpy(sNumber + nCounter+i+ (i ? strlen(sDecimalSymbol) : 0), "\0", 1);
1203
1204     /* Is it a negative number */
1205     if (bNegative == TRUE)
1206     {
1207         i = atoi(sNegNumber);
1208         pStr = sDestination;
1209         pTmpStr = sNumber;
1210         switch (i)
1211         {
1212         case 0:
1213             *pStr++ = '(';
1214             while (*sNumber != '\0')
1215                 *pStr++ =  *pTmpStr++;
1216             *pStr++ = ')';
1217             break;
1218         case 1:
1219             *pStr++ = '-';
1220             while (*pTmpStr != '\0')
1221                 *pStr++ =  *pTmpStr++;
1222             break;
1223         case 2:
1224             *pStr++ = '-';
1225             *pStr++ = ' ';
1226             while (*pTmpStr != '\0')
1227                 *pStr++ =  *pTmpStr++;
1228             break;
1229         case 3:
1230             while (*pTmpStr != '\0')
1231                 *pStr++ =  *pTmpStr++;
1232             *pStr++ = '-';
1233             break;
1234         case 4:
1235             while (*pTmpStr != '\0')
1236                 *pStr++ =  *pTmpStr++;
1237             *pStr++ = ' ';
1238             *pStr++ = '-';
1239             break;
1240         default:
1241             while (*pTmpStr != '\0')
1242                 *pStr++ =  *pTmpStr++;
1243             break;
1244         }
1245     }
1246     else
1247         strcpy(sDestination, sNumber);
1248
1249     /* If cchNumber is zero, then returns the number of bytes or characters
1250      * required to hold the formatted number string
1251      */
1252     retVal = strlen(sDestination) + 1;
1253     if (cchNumber!=0)
1254     {
1255         memcpy( lpNumberStr, sDestination, min(cchNumber, retVal) );
1256         if (cchNumber < retVal) {
1257             retVal = 0;
1258             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1259         }
1260     }
1261     return retVal;
1262 }
1263
1264 /**************************************************************************
1265  *              GetNumberFormatW        (KERNEL32.@)
1266  */
1267 INT WINAPI GetNumberFormatW(LCID locale, DWORD dwflags,
1268                             LPCWSTR lpvalue,  const NUMBERFMTW * lpFormat,
1269                             LPWSTR lpNumberStr, int cchNumber)
1270 {
1271  FIXME("%s: stub, no reformatting done\n",debugstr_w(lpvalue));
1272
1273  lstrcpynW( lpNumberStr, lpvalue, cchNumber );
1274  return cchNumber? strlenW( lpNumberStr ) : 0;
1275 }
1276
1277 /**************************************************************************
1278  *              GetCurrencyFormatA      (KERNEL32.@)
1279  */
1280 INT WINAPI GetCurrencyFormatA(LCID locale, DWORD dwflags,
1281                               LPCSTR lpvalue,   const CURRENCYFMTA * lpFormat,
1282                               LPSTR lpCurrencyStr, int cchCurrency)
1283 {
1284     UINT   nPosOrder, nNegOrder;
1285     INT    retVal;
1286     char   sDestination[128], sNegOrder[8], sPosOrder[8], sCurrencySymbol[8];
1287     char   *pDestination = sDestination;
1288     char   sNumberFormated[128];
1289     char   *pNumberFormated = sNumberFormated;
1290     LCID   systemDefaultLCID;
1291     BOOL   bIsPositive = FALSE, bValidFormat = FALSE;
1292     enum   Operations
1293     {
1294         USE_PARAMETER,
1295         USE_LOCALEINFO,
1296         USE_SYSTEMDEFAULT,
1297         RETURN_ERROR
1298     } used_operation;
1299
1300     NUMBERFMTA  numberFmt;
1301
1302     /* Which source will we use to format the string */
1303     used_operation = RETURN_ERROR;
1304     if (lpFormat != NULL)
1305     {
1306         if (dwflags == 0)
1307             used_operation = USE_PARAMETER;
1308     }
1309     else
1310     {
1311         if (dwflags & LOCALE_NOUSEROVERRIDE)
1312             used_operation = USE_SYSTEMDEFAULT;
1313         else
1314             used_operation = USE_LOCALEINFO;
1315     }
1316
1317     /* Load the fields we need */
1318     switch(used_operation)
1319     {
1320     case USE_LOCALEINFO:
1321         /* Specific to CURRENCYFMTA */
1322         GetLocaleInfoA(locale, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
1323         GetLocaleInfoA(locale, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
1324         GetLocaleInfoA(locale, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
1325
1326         nPosOrder = atoi(sPosOrder);
1327         nNegOrder = atoi(sNegOrder);
1328         break;
1329     case USE_PARAMETER:
1330         /* Specific to CURRENCYFMTA */
1331         nNegOrder = lpFormat->NegativeOrder;
1332         nPosOrder = lpFormat->PositiveOrder;
1333         strcpy(sCurrencySymbol, lpFormat->lpCurrencySymbol);
1334         break;
1335     case USE_SYSTEMDEFAULT:
1336         systemDefaultLCID = GetSystemDefaultLCID();
1337         /* Specific to CURRENCYFMTA */
1338         GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
1339         GetLocaleInfoA(systemDefaultLCID, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
1340         GetLocaleInfoA(systemDefaultLCID, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
1341
1342         nPosOrder = atoi(sPosOrder);
1343         nNegOrder = atoi(sNegOrder);
1344         break;
1345     default:
1346         SetLastError(ERROR_INVALID_PARAMETER);
1347         return 0;
1348     }
1349
1350     /* Construct a temporary number format structure */
1351     if (lpFormat != NULL)
1352     {
1353         numberFmt.NumDigits     = lpFormat->NumDigits;
1354         numberFmt.LeadingZero   = lpFormat->LeadingZero;
1355         numberFmt.Grouping      = lpFormat->Grouping;
1356         numberFmt.NegativeOrder = 0;
1357         numberFmt.lpDecimalSep = lpFormat->lpDecimalSep;
1358         numberFmt.lpThousandSep = lpFormat->lpThousandSep;
1359         bValidFormat = TRUE;
1360     }
1361
1362     /* Make a call to GetNumberFormatA() */
1363     if (*lpvalue == '-')
1364     {
1365         bIsPositive = FALSE;
1366         retVal = GetNumberFormatA(locale,0,lpvalue+1,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
1367     }
1368     else
1369     {
1370         bIsPositive = TRUE;
1371         retVal = GetNumberFormatA(locale,0,lpvalue,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
1372     }
1373
1374     if (retVal == 0)
1375         return 0;
1376
1377     /* construct the formatted string */
1378     if (bIsPositive)
1379     {
1380         switch (nPosOrder)
1381         {
1382         case 0:   /* Prefix, no separation */
1383             strcpy (pDestination, sCurrencySymbol);
1384             strcat (pDestination, pNumberFormated);
1385             break;
1386         case 1:   /* Suffix, no separation */
1387             strcpy (pDestination, pNumberFormated);
1388             strcat (pDestination, sCurrencySymbol);
1389             break;
1390         case 2:   /* Prefix, 1 char separation */
1391             strcpy (pDestination, sCurrencySymbol);
1392             strcat (pDestination, " ");
1393             strcat (pDestination, pNumberFormated);
1394             break;
1395         case 3:   /* Suffix, 1 char separation */
1396             strcpy (pDestination, pNumberFormated);
1397             strcat (pDestination, " ");
1398             strcat (pDestination, sCurrencySymbol);
1399             break;
1400         default:
1401             SetLastError(ERROR_INVALID_PARAMETER);
1402             return 0;
1403         }
1404     }
1405     else  /* negative number */
1406     {
1407         switch (nNegOrder)
1408         {
1409         case 0:   /* format: ($1.1) */
1410             strcpy (pDestination, "(");
1411             strcat (pDestination, sCurrencySymbol);
1412             strcat (pDestination, pNumberFormated);
1413             strcat (pDestination, ")");
1414             break;
1415         case 1:   /* format: -$1.1 */
1416             strcpy (pDestination, "-");
1417             strcat (pDestination, sCurrencySymbol);
1418             strcat (pDestination, pNumberFormated);
1419             break;
1420         case 2:   /* format: $-1.1 */
1421             strcpy (pDestination, sCurrencySymbol);
1422             strcat (pDestination, "-");
1423             strcat (pDestination, pNumberFormated);
1424             break;
1425         case 3:   /* format: $1.1- */
1426             strcpy (pDestination, sCurrencySymbol);
1427             strcat (pDestination, pNumberFormated);
1428             strcat (pDestination, "-");
1429             break;
1430         case 4:   /* format: (1.1$) */
1431             strcpy (pDestination, "(");
1432             strcat (pDestination, pNumberFormated);
1433             strcat (pDestination, sCurrencySymbol);
1434             strcat (pDestination, ")");
1435             break;
1436         case 5:   /* format: -1.1$ */
1437             strcpy (pDestination, "-");
1438             strcat (pDestination, pNumberFormated);
1439             strcat (pDestination, sCurrencySymbol);
1440             break;
1441         case 6:   /* format: 1.1-$ */
1442             strcpy (pDestination, pNumberFormated);
1443             strcat (pDestination, "-");
1444             strcat (pDestination, sCurrencySymbol);
1445             break;
1446         case 7:   /* format: 1.1$- */
1447             strcpy (pDestination, pNumberFormated);
1448             strcat (pDestination, sCurrencySymbol);
1449             strcat (pDestination, "-");
1450             break;
1451         case 8:   /* format: -1.1 $ */
1452             strcpy (pDestination, "-");
1453             strcat (pDestination, pNumberFormated);
1454             strcat (pDestination, " ");
1455             strcat (pDestination, sCurrencySymbol);
1456             break;
1457         case 9:   /* format: -$ 1.1 */
1458             strcpy (pDestination, "-");
1459             strcat (pDestination, sCurrencySymbol);
1460             strcat (pDestination, " ");
1461             strcat (pDestination, pNumberFormated);
1462             break;
1463         case 10:   /* format: 1.1 $- */
1464             strcpy (pDestination, pNumberFormated);
1465             strcat (pDestination, " ");
1466             strcat (pDestination, sCurrencySymbol);
1467             strcat (pDestination, "-");
1468             break;
1469         case 11:   /* format: $ 1.1- */
1470             strcpy (pDestination, sCurrencySymbol);
1471             strcat (pDestination, " ");
1472             strcat (pDestination, pNumberFormated);
1473             strcat (pDestination, "-");
1474             break;
1475         case 12:   /* format: $ -1.1 */
1476             strcpy (pDestination, sCurrencySymbol);
1477             strcat (pDestination, " ");
1478             strcat (pDestination, "-");
1479             strcat (pDestination, pNumberFormated);
1480             break;
1481         case 13:   /* format: 1.1- $ */
1482             strcpy (pDestination, pNumberFormated);
1483             strcat (pDestination, "-");
1484             strcat (pDestination, " ");
1485             strcat (pDestination, sCurrencySymbol);
1486             break;
1487         case 14:   /* format: ($ 1.1) */
1488             strcpy (pDestination, "(");
1489             strcat (pDestination, sCurrencySymbol);
1490             strcat (pDestination, " ");
1491             strcat (pDestination, pNumberFormated);
1492             strcat (pDestination, ")");
1493             break;
1494         case 15:   /* format: (1.1 $) */
1495             strcpy (pDestination, "(");
1496             strcat (pDestination, pNumberFormated);
1497             strcat (pDestination, " ");
1498             strcat (pDestination, sCurrencySymbol);
1499             strcat (pDestination, ")");
1500             break;
1501         default:
1502             SetLastError(ERROR_INVALID_PARAMETER);
1503             return 0;
1504         }
1505     }
1506
1507     retVal = strlen(pDestination) + 1;
1508
1509     if (cchCurrency)
1510     {
1511         memcpy( lpCurrencyStr, pDestination, min(cchCurrency, retVal) );
1512         if (cchCurrency < retVal) {
1513             retVal = 0;
1514             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1515         }
1516     }
1517     return retVal;
1518 }
1519
1520 /**************************************************************************
1521  *              GetCurrencyFormatW      (KERNEL32.@)
1522  */
1523 INT WINAPI GetCurrencyFormatW(LCID locale, DWORD dwflags,
1524                               LPCWSTR lpvalue,   const CURRENCYFMTW * lpFormat,
1525                               LPWSTR lpCurrencyStr, int cchCurrency)
1526 {
1527     FIXME("This API function is NOT implemented !\n");
1528     return 0;
1529 }
1530
1531
1532 /******************************************************************************
1533  *              GetTimeFormatA  [KERNEL32.@]
1534  * Makes an ASCII string of the time
1535  *
1536  * Formats date according to format,  or locale default if format is
1537  * NULL. The format consists of literal characters and fields as follows:
1538  *
1539  * h  hours with no leading zero (12-hour)
1540  * hh hours with full two digits
1541  * H  hours with no leading zero (24-hour)
1542  * HH hours with full two digits
1543  * m  minutes with no leading zero
1544  * mm minutes with full two digits
1545  * s  seconds with no leading zero
1546  * ss seconds with full two digits
1547  * t  time marker (A or P)
1548  * tt time marker (AM, PM)
1549  *
1550  */
1551 INT WINAPI
1552 GetTimeFormatA(LCID locale,        /* [in]  */
1553                DWORD flags,        /* [in]  */
1554                const SYSTEMTIME* xtime, /* [in]  */
1555                LPCSTR format,      /* [in]  */
1556                LPSTR timestr,      /* [out] */
1557                INT timelen         /* [in]  */)
1558 {
1559   INT ret;
1560   LPWSTR wformat = NULL;
1561   LPWSTR wtime = NULL;
1562
1563   if (format)
1564   {
1565     wformat = HeapAlloc(GetProcessHeap(), 0,
1566                         (strlen(format) + 1) * sizeof(wchar_t));
1567     MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
1568   }
1569
1570   if (timestr && timelen)
1571   {
1572     wtime = HeapAlloc(GetProcessHeap(), 0,
1573                       (timelen + 1) * sizeof(wchar_t));
1574   }
1575
1576   ret = GetTimeFormatW(locale, flags, xtime, wformat, wtime, timelen);
1577
1578   if (wtime)
1579   {
1580     WideCharToMultiByte(CP_ACP, 0, wtime, ret, timestr, timelen, NULL, NULL);
1581     HeapFree(GetProcessHeap(), 0, wtime);
1582   }
1583
1584   if (wformat)
1585   {
1586     HeapFree(GetProcessHeap(), 0, wformat);
1587   }
1588
1589   return ret;
1590 }
1591
1592
1593 /******************************************************************************
1594  *              GetTimeFormatW  [KERNEL32.@]
1595  * Makes a Unicode string of the time
1596  *
1597  * NOTE: See get_date_time_formatW() for further documentation
1598  */
1599 INT WINAPI
1600 GetTimeFormatW(LCID locale,        /* [in]  */
1601                DWORD flags,        /* [in]  */
1602                const SYSTEMTIME* xtime, /* [in]  */
1603                LPCWSTR format,     /* [in]  */
1604                LPWSTR timestr,     /* [out] */
1605                INT timelen         /* [in]  */)
1606 {       WCHAR format_buf[40];
1607         LPCWSTR thisformat;
1608         SYSTEMTIME t;
1609         const SYSTEMTIME* thistime;
1610         DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
1611         INT ret;
1612
1613         TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,
1614         xtime,debugstr_w(format),timestr,timelen);
1615
1616         if (!locale) locale = LOCALE_SYSTEM_DEFAULT;
1617         locale = ConvertDefaultLocale( locale );
1618
1619         /* if the user didn't specify a format we use the default */
1620         /* format for this locale */
1621         if (format == NULL)
1622         {
1623           if (flags & LOCALE_NOUSEROVERRIDE)  /* use system default */
1624           {
1625             locale = GetSystemDefaultLCID();
1626           }
1627           GetLocaleInfoW(locale, thisflags, format_buf, 40);
1628           thisformat = format_buf;
1629         }
1630         else
1631         {
1632           /* if non-null format and LOCALE_NOUSEROVERRIDE then fail */
1633           /* NOTE: this could be either invalid flags or invalid parameter */
1634           /*  windows sets it to invalid flags */
1635           if (flags & LOCALE_NOUSEROVERRIDE)
1636           {
1637             SetLastError(ERROR_INVALID_FLAGS);
1638             return 0;
1639           }
1640
1641           thisformat = format;
1642         }
1643
1644         if (xtime == NULL) /* NULL means use the current local time */
1645         { GetLocalTime(&t);
1646           thistime = &t;
1647         }
1648         else
1649         {
1650           /* check time values */
1651           if((xtime->wHour > 24) || (xtime->wMinute >= 60) || (xtime->wSecond >= 60))
1652           {
1653             SetLastError(ERROR_INVALID_PARAMETER);
1654             return 0;
1655           }
1656
1657           thistime = xtime;
1658         }
1659
1660         ret = get_date_time_formatW(locale, thisflags, flags, thistime, thisformat,
1661                                     timestr, timelen, 0);
1662         return ret;
1663 }
1664
1665 /******************************************************************************
1666  *              EnumCalendarInfoA       [KERNEL32.@]
1667  */
1668 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
1669                                CALID calendar,CALTYPE caltype )
1670 {
1671     FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc,locale,calendar,caltype);
1672     return FALSE;
1673 }