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