Windows: fix checked UNICODE build of talocale
[openafs.git] / src / WINNT / talocale / tal_string.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 }
14
15 #include <windows.h>
16 #include <windowsx.h>
17 #include <WINNT/talocale.h>
18 #include <stdlib.h>
19
20
21 /*
22  * DEFINITIONS ________________________________________________________________
23  *
24  */
25
26 #ifndef _MAX_DRIVE
27 #define _MAX_DRIVE    3
28 #define _MAX_DIR    260
29 #define _MAX_EXT      5
30 #define _MAX_FNAME  260
31 #endif
32
33 #define _ttoupper(_ch)    (TCHAR)CharUpper((LPTSTR)_ch)
34
35 #define isfmtgarbage(_ch) (isdigit(_ch) || (_ch==TEXT('.')) || (_ch==TEXT(',')) || (_ch==TEXT('-')))
36
37
38 /*
39  * PROTOTYPES _________________________________________________________________
40  *
41  */
42
43 LPTSTR cdecl vFormatString (LONG, LPCTSTR, va_list);
44
45 BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage);
46 BOOL TranslateError (LPTSTR pszText, ULONG status);
47
48 LPTSTR FixFormatString (LPTSTR pszFormat);
49
50
51 /*
52  * ROUTINES ___________________________________________________________________
53  *
54  */
55
56
57 /*** GetString - Loads one or more consecutive strings from a resource file
58  *
59  */
60
61
62 void GetString (LPTSTR psz, int ids, int cchMax)
63 {
64    *psz = TEXT('\0');
65
66    for (;;)
67       {
68       LPCSTRINGTEMPLATE pst;
69       if ((pst = TaLocale_GetStringResource (ids)) == NULL)
70          break;
71
72       CopyUnicodeToString (psz, (LPWSTR)pst->achString, min(cchMax,pst->cchString+1));
73       if (psz[ lstrlen(psz)-1 ] != TEXT('+'))
74          break;
75       psz[ lstrlen(psz)-1 ] = TEXT('\0');
76
77       cchMax -= lstrlen(psz);
78       psz += lstrlen(psz);
79       ++ids;
80       }
81 }
82
83
84 /*** GetStringLength() - Returns the number of chars in a string resource
85  *
86  */
87
88 size_t GetStringLength (int ids)
89 {
90    size_t cch = 0;
91
92    for (;;)
93       {
94       LPCSTRINGTEMPLATE pst;
95       if ((pst = TaLocale_GetStringResource (ids)) == NULL)
96          break;
97
98       char szAnsi[ cchRESOURCE * 2 + 1 ];
99       CopyUnicodeToAnsi (szAnsi, pst->achString, pst->cchString+1);
100       cch += 1+ lstrlenA(szAnsi);
101
102       if ((!pst->cchString) || (pst->achString[ pst->cchString-1 ] != L'+'))
103          break;
104
105       ++ids;
106       }
107
108    return cch;
109 }
110
111
112
113 /*** SearchMultiString() - scans multistrings for a given string
114  *
115  */
116
117 BOOL SearchMultiString (LPCTSTR pmsz, LPCTSTR pszString, BOOL fCaseSensitive)
118 {
119    if (pmsz)
120       {
121       for (LPCTSTR psz = pmsz; *psz; psz += 1+lstrlen(psz))
122          {
123          if (!*pszString && !lstrlen(psz))
124             return TRUE;
125          else if (pszString && fCaseSensitive && !lstrcmp (psz, pszString))
126             return TRUE;
127          else if (pszString && !fCaseSensitive && !lstrcmpi (psz, pszString))
128             return TRUE;
129          }
130       }
131
132    return FALSE;
133 }
134
135
136 /*** FormatMultiString() - manipulates multistrings ("EXA\0MP\0LE\0\0")
137  *
138  */
139
140 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LONG ids, LPCTSTR pszFormat, va_list arg)
141 {
142    static const TCHAR szMultiStringNULL[] = cszMultiStringNULL;
143    LPTSTR pszNew = vFormatString (ids, pszFormat, arg);
144
145    if (!pszNew || !*pszNew)
146       {
147       FreeString (pszNew);
148       pszNew = (LPTSTR)szMultiStringNULL;
149       }
150
151    size_t cchOld = 1; // for the second NUL-terminator
152    for (LPTSTR psz = *ppszTarget; psz && *psz; psz += 1+lstrlen(psz))
153       cchOld += lstrlen(psz) +1;
154
155    LPTSTR pszFinal;
156    if ((pszFinal = AllocateString (cchOld +lstrlen(pszNew) +1)) != NULL)
157       {
158       if (!*ppszTarget)
159          {
160          lstrcpy (pszFinal, pszNew);
161          }
162       else if (fAddHead)
163          {
164          lstrcpy (pszFinal, pszNew);
165          memcpy (&pszFinal[ lstrlen(pszNew)+1 ], *ppszTarget, sizeof(TCHAR) * cchOld);
166          }
167       else // (*ppszTarget && !fAddHead)
168          {
169          memcpy (pszFinal, *ppszTarget, sizeof(TCHAR) * cchOld);
170          lstrcpy (&pszFinal[ cchOld-1 ], pszNew);
171          }
172       pszFinal[ cchOld +lstrlen(pszNew) ] = TEXT('\0');
173
174       FreeString (*ppszTarget);
175       *ppszTarget = pszFinal;
176       }
177
178    if (pszNew != (LPTSTR)szMultiStringNULL)
179       FreeString (pszNew);
180 }
181
182
183 void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, ...)
184 {
185    va_list arg;
186    //if (pszFormat != NULL)
187       va_start (arg, pszFormat);
188    vFormatMultiString (ppszTarget, fAddHead, PtrToLong(pszTemplate), pszFormat, arg);
189 }
190
191 void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, ...)
192 {
193    va_list arg;
194    //if (pszFormat != NULL)
195       va_start (arg, pszFormat);
196    vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg);
197 }
198
199 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, va_list arg)
200 {
201    vFormatMultiString (ppszTarget, fAddHead, PtrToLong(pszTemplate), pszFormat, arg);
202 }
203
204 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, va_list arg)
205 {
206    vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg);
207 }
208
209
210 /*** FormatString() - replacable-parameter version of wsprintf()
211  *
212  */
213
214 LPTSTR cdecl FormatString (LPCTSTR psz, LPCTSTR pszFmt, ...)
215 {
216    va_list arg;
217    //if (pszFmt != NULL)
218       va_start (arg, pszFmt);
219    return vFormatString (PtrToLong(psz), pszFmt, arg);
220 }
221
222 LPTSTR cdecl FormatString (int ids, LPCTSTR pszFmt, ...)
223 {
224    va_list  arg;
225    //if (pszFmt != NULL)
226       va_start (arg, pszFmt);
227    return vFormatString ((LONG)ids, pszFmt, arg);
228 }
229
230 LPTSTR cdecl vFormatString (LPCTSTR psz, LPCTSTR pszFmt, va_list arg)
231 {
232    return vFormatString (PtrToLong(psz), pszFmt, arg);
233 }
234
235 LPTSTR cdecl vFormatString (int ids, LPCTSTR pszFmt, va_list arg)
236 {
237    return vFormatString ((LONG)ids, pszFmt, arg);
238 }
239
240
241 typedef enum    // vartype
242    {
243    vtINVALID,
244    vtWORD,
245    vtDWORD,
246    vtFLOAT,
247    vtDOUBLE,
248    vtSTRINGW,
249    vtSTRINGA,
250    vtMESSAGE,
251    vtBYTES_DOUBLE,
252    vtBYTES,
253    vtSYSTEMTIME,
254    vtELAPSEDTIME,
255    vtERROR,
256    vtADDRESS,
257    vtLARGEINT,
258    } vartype;
259
260 #ifdef UNICODE
261 #define vtSTRING  vtSTRINGW     // %s gives vtSTRING
262 #else
263 #define vtSTRING  vtSTRINGA
264 #endif
265
266 #define cchMaxNUMBER  40
267 #define cszNULL       TEXT("(null)")
268
269 LPTSTR cdecl vFormatString (LONG pszSource, LPCTSTR pszFmt, va_list arg)
270 {
271    LPTSTR  *apszArgs;
272    LPTSTR   psz;
273    LPTSTR   pszOut = NULL;
274    LPTSTR   pchOut;
275    LPTSTR   pszTemplate;
276    size_t   cch;
277    int      nArgs;
278    int      argno;
279    TCHAR    szFmt[ cchRESOURCE ];
280
281    if (pszSource == 0)
282       return NULL;
283
284    if (HIWORD(pszSource) != 0)  // It's a string
285       {
286       pszTemplate = (LPTSTR)LongToPtr(pszSource);
287       }
288    else // It's a message
289       {
290       cch = GetStringLength((INT)pszSource);
291
292       if ((pszTemplate = AllocateString (1+cch)) == NULL)
293          return NULL;
294
295       GetString (pszTemplate, (int)pszSource, (INT)cch);
296       }
297
298             //
299             // First off, count the number of arguments given in {pszFmt}.
300             //
301
302    nArgs = 0;
303
304    if (pszFmt != NULL)
305       {
306       for (psz = (LPTSTR)pszFmt; *psz; psz++)
307          {
308          if (*psz == TEXT('%'))
309             nArgs++;
310          }
311       }
312
313             //
314             // If there are no arguments, then we're just expanding {pszSource}.
315             //
316
317    if (nArgs == 0)
318       {
319       if (HIWORD(pszSource) == 0)       // It's a message
320          {
321          psz = pszTemplate;
322          }
323       else // (HIWORD(pszSource) != 0)  // It's a string
324          {
325          psz = CloneString (pszTemplate);
326          }
327       return psz;
328       }
329
330             //
331             // Otherwise, allocate an array of LPTSTR's, one for each
332             // argument.
333             //
334
335    apszArgs = (LPTSTR*)Allocate (sizeof(LPTSTR) * nArgs);
336
337    if (apszArgs == NULL)
338       {
339       if (pszSource != PtrToLong(pszTemplate))
340          FreeString (pszTemplate);
341       return NULL;
342       }
343
344    memset (apszArgs, 0x00, sizeof(LPTSTR) * nArgs);
345
346             //
347             // Extract each argument in turn, using {pszFmt} and {arg}.
348             //
349
350    for (argno = 0; pszFmt && *pszFmt; argno++)
351       {
352       size_t   cchMin;
353       vartype  vt;
354
355       double   arg_f;
356       LONG     arg_d;
357       LPTSTR   arg_psz;
358       SYSTEMTIME *arg_t;
359       SOCKADDR_IN *arg_a;
360       LARGE_INTEGER *arg_ldw;
361
362                //
363                // At this point, {pstFmt} points to the start of an argument's
364                // decription within the user-supplied format string
365                // (which may look like, say, "%-12.30ls%40lu").  Parse
366                // the "%-12.30" part of each argument first...
367                //
368
369       if ((*pszFmt != TEXT('%')) || (*(1+pszFmt) == TEXT('\0')))
370          break;
371
372       lstrcpy (szFmt, pszFmt);
373       if ((psz = (LPTSTR)lstrchr (&szFmt[1], TEXT('%'))) != NULL)
374          *psz = 0;
375
376       int ii;
377       for (ii = 1; szFmt[ii] == TEXT('-') || szFmt[ii] == TEXT(','); ii++)
378          ;
379       cchMin = _ttol (&szFmt[ii]);
380
381       for (pszFmt++; *pszFmt && isfmtgarbage(*pszFmt); ++pszFmt)
382          {
383          if (*pszFmt == TEXT('%'))
384             break;
385          }
386
387       if (!pszFmt || !*pszFmt)
388          break;
389       if (*pszFmt == TEXT('%'))
390          continue;
391
392                //
393                // Yuck.  At this point,
394                //    cchMin is either 0 or whatever appeared as X in "%X.##s"
395                //    pszFmt points to the first character after "%##.##"
396                //
397                // Step through the format string's definition ("c", "lu", etc)
398                // to determine what kind of argument this one is, setting {vt}
399                // accordingly.
400                //
401
402       for (vt = vtINVALID; *pszFmt && *pszFmt != TEXT('%'); pszFmt++)
403          {
404          switch (*pszFmt)
405             {
406             case TEXT('c'):
407                vt = vtWORD;
408                break;
409
410             case TEXT('x'):
411             case TEXT('X'):
412             case TEXT('h'):
413             case TEXT('u'):
414             case TEXT('d'):
415                if (vt != vtDWORD)
416                   vt = vtWORD;
417                break;
418
419             case TEXT('l'):
420                if (vt == vtFLOAT)
421                   vt = vtDOUBLE;
422                else
423                   vt = vtDWORD;
424                break;
425
426             case TEXT('i'):     // "%i"==int, "%li"==large_integer
427                if (vt != vtDWORD)
428                   vt = vtWORD;
429                else
430                   vt = vtLARGEINT;
431                break;
432
433             case TEXT('g'):
434             case TEXT('f'):
435                if (vt == vtDWORD)
436                   vt = vtDOUBLE;
437                else
438                   vt = vtFLOAT;
439                break;
440
441             case TEXT('s'):
442                if (vt == vtDWORD)
443                   vt = vtSTRINGW;
444                else if (vt == vtWORD)
445                   vt = vtSTRINGA;
446                else
447                   vt = vtSTRING;
448                break;
449
450             case TEXT('m'):
451                vt = vtMESSAGE;
452                *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
453                break;
454
455             case TEXT('B'):     // %B = double cb
456                vt = vtBYTES_DOUBLE;
457                *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
458                break;
459
460             case TEXT('b'):     // %b = ULONG cb
461                vt = vtBYTES;
462                *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
463                break;
464
465             case TEXT('t'):
466                if (vt == vtERROR)       // %et == systemtime* (shown as elapsed)
467                   vt = vtELAPSEDTIME;
468                else // (vt != vtERROR)  // %t == systemtime* (shown as elapsed)
469                   vt = vtSYSTEMTIME;
470                break;
471
472             case TEXT('e'):     // %e == error
473                vt = vtERROR;
474                break;
475
476             case TEXT('a'):     // %a = LPSOCKADDR_IN
477                vt = vtADDRESS;
478                *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
479                break;
480             }
481          }
482
483       if (vt == vtINVALID)
484          continue;
485
486             //
487             // Now the fun part.  Pop the appropriate argument off the
488             // stack; note that we had to know how big the argument is,
489             // and in what format, in order to do this.
490             //
491
492       switch (vt)
493          {
494          case vtWORD:
495             arg_d = (LONG)(va_arg (arg, short));
496             cch = cchMaxNUMBER;
497             break;
498
499          case vtDWORD:
500             arg_d = va_arg (arg, LONG);
501             cch = cchMaxNUMBER;
502             break;
503
504          case vtFLOAT:
505             arg_f = (double)( va_arg (arg, float) );
506             cch = cchMaxNUMBER;
507             break;
508
509          case vtDOUBLE:
510             arg_f = va_arg (arg, double);
511             cch = cchMaxNUMBER;
512             break;
513
514          case vtSTRINGA:   
515             {
516             LPSTR arg_pszA = va_arg (arg, LPSTR);
517
518             if (arg_pszA == NULL)
519                arg_psz = CloneString (cszNULL);
520             else
521                arg_psz = CloneAnsi (arg_pszA);
522
523             cch = lstrlen(arg_psz);
524             break;
525             }
526
527          case vtSTRINGW:   
528             {
529             LPWSTR arg_pszW = va_arg (arg, LPWSTR);
530
531             if (arg_pszW == NULL)
532                arg_psz = CloneString (cszNULL);
533             else
534                arg_psz = CloneUnicode (arg_pszW);
535
536             cch = lstrlen(arg_psz);
537             break;
538             }
539
540          case vtMESSAGE:
541             {
542             int arg_ids = va_arg (arg, int);
543
544             if ((cch = GetStringLength (arg_ids)) == 0)
545                arg_psz = CloneString (cszNULL);
546             else
547                {
548                if ((arg_psz = AllocateString (cch)) == NULL)
549                   goto lblDONE;
550                GetString (arg_psz, arg_ids, (INT)cch);
551                }
552
553             cch = lstrlen(arg_psz);
554             break;
555             }
556
557          case vtBYTES_DOUBLE:
558             arg_f = va_arg (arg, double);
559             cch = cchMaxNUMBER;
560             break;
561
562          case vtBYTES:
563             arg_f = (double)va_arg (arg, LONG);
564             cch = cchMaxNUMBER;
565             break;
566
567          case vtELAPSEDTIME:
568          case vtSYSTEMTIME:
569             arg_t = va_arg (arg, SYSTEMTIME*);
570             cch = cchRESOURCE;
571             break;
572
573          case vtERROR:
574             arg_d = va_arg (arg, DWORD);
575             cch = cchRESOURCE;
576             break;
577
578          case vtADDRESS:
579             arg_a = va_arg (arg, SOCKADDR_IN*);
580             cch = cchRESOURCE;
581             break;
582
583          case vtLARGEINT:
584             arg_ldw = va_arg (arg, LARGE_INTEGER*);
585             cch = cchMaxNUMBER * 2;
586             break;
587          }
588
589                //
590                // Okay; now, depending on what {vt} is, one of
591                // {arg_psz, arg_d, arg_f} has been set to match
592                // this argument.  Allocate an string for apszArgs[]
593                // to represent the formatted, ready-for-output version
594                // of this argument.
595                //
596
597       cch = max( cch, cchMin );
598
599       if ((apszArgs[ argno ] = AllocateString (cch+1)) == NULL)
600          goto lblDONE;
601
602       switch (vt)
603          {
604          case vtWORD:
605             wsprintf (apszArgs[ argno ], szFmt, (short)arg_d);
606             break;
607
608          case vtDWORD:
609             wsprintf (apszArgs[ argno ], szFmt, arg_d);
610             break;
611
612          case vtFLOAT:
613             FormatDouble (apszArgs[ argno ], szFmt, arg_f);
614             break;
615
616          case vtDOUBLE:
617             FormatDouble (apszArgs[ argno ], szFmt, arg_f);
618             break;
619
620          case vtSTRINGA:
621          case vtSTRINGW:
622          case vtMESSAGE:
623             wsprintf (apszArgs[ argno ], szFmt, arg_psz);
624             FreeString (arg_psz);
625             break;
626
627          case vtBYTES:
628          case vtBYTES_DOUBLE:
629             FormatBytes (apszArgs[ argno ], szFmt, arg_f);
630             break;
631
632          case vtELAPSEDTIME:
633             FormatElapsed (apszArgs[ argno ], szFmt, arg_t);
634             break;
635
636          case vtSYSTEMTIME:
637             FormatTime (apszArgs[ argno ], szFmt, arg_t);
638             break;
639
640          case vtERROR:
641             FormatError (apszArgs[ argno ], szFmt, arg_d);
642             break;
643
644          case vtADDRESS:
645             FormatSockAddr (apszArgs[ argno ], szFmt, arg_a);
646             break;
647
648          case vtLARGEINT:
649             FormatLargeInt (apszArgs[ argno ], szFmt, arg_ldw);
650             break;
651          }
652       }
653
654             //
655             // Determine how big the final string will be once the arguments
656             // are inserted, and allocate space for it.
657             //
658
659    cch = 0;
660
661    for (psz = pszTemplate; *psz; )
662       {
663       if (*psz != TEXT('%'))
664          {
665          ++cch;
666          ++psz;
667          continue;
668          }
669
670       if (*(++psz) == TEXT('\0'))
671          break;
672
673       if (!isdigit(*psz))
674          {
675          cch++;
676          psz++;
677          continue;
678          }
679
680       for (argno = 0; *psz && isdigit (*psz); psz++)
681          {
682          argno *= 10;
683          argno += (int)(*psz -'0');
684          }
685       if (*psz == '~')
686          psz++;
687       if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL)
688          cch += lstrlen (cszNULL);
689       else
690          cch += lstrlen (apszArgs[argno -1]);
691       }
692
693    if ((pszOut = AllocateString (cch+1)) == NULL)
694       goto lblDONE;
695
696             //
697             // Then generate a final output, by copying over the template
698             // string into the final string, inserting arguments wherever
699             // a "%1" (or "%43" etc) is found.
700             //
701
702    for (psz = pszTemplate, pchOut = pszOut; *psz; )
703       {
704       if (*psz != TEXT('%'))
705          {
706          *pchOut++ = *psz++;
707          continue;
708          }
709
710       if (*(++psz) == TEXT('\0'))
711          break;
712
713       if (!isdigit(*psz))
714          {
715          *pchOut++ = *psz++;
716          continue;
717          }
718
719       for (argno = 0; *psz && isdigit (*psz); psz++)
720          {
721          argno *= 10;
722          argno += (int)(*psz -'0');
723          }
724       if (*psz == '~')
725          psz++;
726       if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL)
727          lstrcpy (pchOut, cszNULL);
728       else
729          lstrcpy (pchOut, apszArgs[argno -1]);
730       pchOut = &pchOut[ lstrlen(pchOut) ];
731       }
732
733    *pchOut = 0;
734
735 lblDONE:
736    if (apszArgs != NULL)
737       {
738       for (argno = 0; argno < nArgs; argno++)
739          {
740          if (apszArgs[ argno ] != NULL)
741             FreeString (apszArgs[ argno ]);
742          }
743       Free (apszArgs);
744       }
745
746    if (HIWORD(pszSource) == 0)  // Did we allocate {pszTemplate}?
747       {
748       if (pszTemplate != NULL)
749          {
750          FreeString (pszTemplate);
751          }
752       }
753
754    return pszOut;
755 }
756
757
758 /*** FormatSockAddr() - formats a SOCKADDR_IN as text
759  *
760  */
761
762 void FormatSockAddr (LPTSTR pszTarget, LPTSTR pszFmt, SOCKADDR_IN *paddr)
763 {
764    TCHAR szText[ cchRESOURCE ];
765
766    LPSTR pszSockAddrA = inet_ntoa (*(struct in_addr *)&paddr->sin_addr.s_addr);
767    LPTSTR pszSockAddr = AnsiToString (pszSockAddrA);
768
769    lstrcpy (szText, pszSockAddr);
770
771    FreeString (pszSockAddr, pszSockAddrA);
772
773    wsprintf (pszTarget, FixFormatString (pszFmt), szText);
774 }
775
776
777 /*** FormatElapsed() - formats a SYSTEMTIME* as HH:MM:SS
778  *
779  */
780
781 BOOL FormatElapsed (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst)
782 {
783    TCHAR szTimeSep[ cchRESOURCE ];
784    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_STIME, szTimeSep, cchRESOURCE))
785       lstrcpy (szTimeSep, TEXT(":"));
786
787    TCHAR szElapsed[ cchRESOURCE ];
788    wsprintf (szElapsed, TEXT("%02lu%s%02lu%s%02lu"), 
789              pst->wHour + (pst->wDay * 24),
790              szTimeSep,
791              pst->wMinute,
792              szTimeSep,
793              pst->wSecond);
794
795    wsprintf (pszTarget, FixFormatString (pszFormatUser), szElapsed);
796    return TRUE;
797 }
798
799
800 /*** FormatTime() - formats a SYSTEMTIME* according to the current locale
801  *
802  */
803
804 BOOL FormatTime (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst, BOOL fShowDate, BOOL fShowTime)
805 {
806    BOOL rc = TRUE;
807
808    SYSTEMTIME lt;
809    FILETIME ft;
810    FILETIME lft;
811    SystemTimeToFileTime (pst, &ft);
812    FileTimeToLocalFileTime (&ft, &lft);
813    FileTimeToSystemTime (&lft, &lt);
814
815    TCHAR szTime[ cchRESOURCE ];
816    TCHAR szDate[ cchRESOURCE ];
817
818    if ( ((pst->wYear == 1970) && (pst->wMonth == 1) && (pst->wDay == 1)) ||
819         ((pst->wYear == 0)    && (pst->wMonth == 0) && (pst->wDay == 0)) )
820       {
821       szTime[0] = TEXT('\0');
822       rc = FALSE;
823       }
824    else
825       {
826       GetTimeFormat (LOCALE_USER_DEFAULT, 0, &lt, TEXT("HH:mm:ss"), szTime, cchRESOURCE);
827       GetDateFormat (LOCALE_USER_DEFAULT, 0, &lt, TEXT("yyyy-MM-dd"), szDate, cchRESOURCE);
828
829       if (fShowTime && fShowDate)
830          {
831          lstrcat (szDate, TEXT(" "));
832          lstrcat (szDate, szTime);
833          }
834       else if (!fShowDate && fShowTime)
835          {
836          lstrcpy (szDate, szTime);
837          }
838       }
839
840    wsprintf (pszTarget, FixFormatString (pszFormatUser), szDate);
841    return rc;
842 }
843
844
845 /*** FormatError() - formats an error code as text
846  *
847  */
848
849 LPERRORPROC pfnTranslateError = NULL;
850 void SetErrorTranslationFunction (LPERRORPROC pfn)
851 {
852    pfnTranslateError = pfn;
853 }
854
855 BOOL FormatError (LPTSTR pszTarget, LPTSTR pszFmt, DWORD dwError)
856 {
857    TCHAR szError[ cchRESOURCE ];
858    BOOL rc = TranslateError (szError, dwError);
859
860    LPTSTR pchTarget = szError;
861    for (LPTSTR pchSource = szError; *pchSource; ++pchSource)
862       {
863       if (*pchSource == TEXT('\n'))
864          *pchTarget++ = TEXT(' ');
865       else if (*pchSource != TEXT('\r'))
866          *pchTarget++ = *pchSource;
867       }
868    *pchTarget = TEXT('\0');
869
870    wsprintf (pszTarget, FixFormatString (pszFmt), szError);
871    return rc;
872 }
873
874
875 BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage)
876 {
877    BOOL rc = FALSE;
878
879    // Guess an appropriate language ID if none was supplied explicitly
880    //
881    if (idLanguage == 0)
882       {
883       idLanguage = TaLocale_GetLanguage();
884       }
885
886    // See if the caller supplied a function for us to use.
887    //
888    if (pfnTranslateError)
889       {
890       rc = (*pfnTranslateError)(pszText, code, idLanguage);
891       }
892
893    // Try to get Windows to translate the thing...
894    //
895    if (!rc)
896       {
897       if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, idLanguage, pszText, cchRESOURCE, NULL))
898          {
899          rc = TRUE;
900          }
901       }
902
903    // See if the message is buried in NTDLL...
904    //
905    if (!rc)
906       {
907       HINSTANCE hiNTDLL;
908       if ((hiNTDLL = LoadLibrary (TEXT("NTDLL.DLL"))) != NULL)
909          {
910          if (FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, hiNTDLL, code, idLanguage, pszText, cchRESOURCE, NULL))
911             {
912             rc = TRUE;
913             }
914          FreeLibrary (hiNTDLL);
915          }
916       }
917
918    return rc;
919 }
920
921
922 BOOL TranslateError (LPTSTR pszText, ULONG status)
923 {
924    BOOL rc;
925
926    if ((rc = TranslateErrorFunc (pszText, status, 0)) == TRUE)
927       {
928       wsprintf (&pszText[ lstrlen(pszText) ], TEXT(" (0x%08lX)"), status);
929       }
930    else
931       {
932       wsprintf (pszText, TEXT("0x%08lX"), status);
933       }
934
935    return rc;
936 }
937
938
939 /*** FormatBytes() - formats L1048576 as "1.0 MB" (etc)
940  *
941  * Wherever "%1" appears is where the number will show up (formatted
942  * according to {pszFormat}).
943  *
944  */
945
946 void FormatBytes (LPTSTR pszTarget, LPTSTR pszFormatUser, double cb)
947 {
948    TCHAR szFormat[ cchRESOURCE ];
949    TCHAR szTemplate[ cchRESOURCE ];
950    TCHAR szJustTheNumber[ cchRESOURCE ];
951    LPTSTR pszOut;
952
953             //
954             // The first task is to generate a useful {pszFormat}; it may
955             // come in looking like "%-15.15s" or "%0.2b" or "%lu" --who knows.
956             // We'll need it to produce a formatted string from a DWORD, so
957             // make sure it ends in "lf".
958             //
959
960    if (*pszFormatUser != TEXT('%'))
961       {
962       lstrcpy (szFormat, TEXT("%.2lf")); // sigh--totally bogus format string.
963       }
964    else
965       {
966       lstrcpy (szFormat, pszFormatUser);
967
968       LPTSTR pch;
969       for (pch = &szFormat[1]; *pch; ++pch)
970          {
971          if (!isfmtgarbage(*pch))
972             {
973             lstrcpy (pch, TEXT("lf"));
974             break;
975             }
976          }
977       if (!*pch)
978          {
979          lstrcat (pch, TEXT("lf"));
980          }
981       }
982
983             //
984             // The next step is to generate just the number portion of the
985             // output; that will look like "0.90" or "5.3", or whatever.
986             //
987
988    FormatDouble (szJustTheNumber, szFormat, cb/1048576.0);
989    lstrcpy (szTemplate, TEXT("%1 MB"));
990
991             //
992             // Cheesy bit: if all we have are 0's and "."'s, just
993             // make the number read "0".
994             //
995    TCHAR * pch;
996    for (pch = szJustTheNumber; *pch; ++pch)
997       {
998       if (*pch != TEXT('0') && *pch != TEXT('.'))
999          break;
1000       }
1001    if (!*pch)
1002       {
1003       lstrcpy (szJustTheNumber, TEXT("0"));
1004       }
1005
1006             //
1007             // Not particularly hard.  Now let FormatString() generate
1008             // the final output, by tacking on the necessary units.
1009             //
1010
1011    if ((pszOut = FormatString (szTemplate, TEXT("%s"), szJustTheNumber)) == NULL)
1012       {
1013       // whoops--out of memory?
1014       // well, just give it back in bytes--we can still do that.
1015       //
1016       FormatDouble (pszTarget, szFormat, cb);
1017       }
1018    else
1019       {
1020       lstrcpy (pszTarget, pszOut);
1021       FreeString (pszOut);
1022       }
1023 }
1024
1025
1026 /*** FormatLargeInt() - formats a large int as "hidword,,lodword"
1027  *
1028  */
1029
1030 void FormatLargeInt (LPTSTR pszTarget, LPTSTR pszFormatUser, LARGE_INTEGER *pldw)
1031 {
1032    // Generate just the number portion of the output;
1033    // that will look like "###,,###"
1034    //
1035    TCHAR szJustTheNumber[ cchRESOURCE ];
1036    wsprintf (szJustTheNumber, TEXT("%lu,,%lu"), (DWORD)pldw->HighPart, (DWORD)pldw->LowPart);
1037
1038    // Not particularly hard.  Now let FormatString() generate
1039    // the final output, by tacking on the necessary units
1040    // description.
1041    //
1042    wsprintf (pszTarget, FixFormatString (pszFormatUser), szJustTheNumber);
1043 }
1044
1045
1046 /*** FormatDouble() - like wsprintf(), but for one argument (a double)
1047  *
1048  * since {wsprintf} doesn't understand "%lf", "%f", "%g", "%e", etc,
1049  * use this function instead.  {pszFormat} should consist of nothing
1050  * except the formatting string: thus, "%3.15lf" is okay, but "hi %lf there"
1051  * isn't.
1052  *
1053  */
1054
1055 void FormatDouble (LPTSTR pszTarget, LPTSTR pszFormat, double lfValue)
1056 {
1057    BOOL fPlusUpFront = FALSE;
1058    BOOL fZeroUpFront = FALSE;
1059    int  cchMin = 0;
1060    int  cchMax = 8;
1061
1062    // First, parse the format string: look for "%{zero|plus}{min}.{max}",
1063    // and ignore any other characters.
1064    //
1065    while (*pszFormat && *pszFormat != TEXT('%'))
1066       ++pszFormat;
1067
1068    while (*pszFormat == TEXT('%') ||
1069           *pszFormat == TEXT('0') ||
1070           *pszFormat == TEXT('+') ||
1071           *pszFormat == TEXT('-') )
1072       {
1073       if (*pszFormat == TEXT('0'))
1074          {
1075          fZeroUpFront = TRUE;
1076          }
1077       else if (*pszFormat == TEXT('+'))
1078          {
1079          fPlusUpFront = TRUE;
1080          }
1081       ++pszFormat;
1082       }
1083
1084    cchMin = _ttol( pszFormat );
1085
1086    while (isdigit(*pszFormat))
1087       ++pszFormat;
1088
1089    if (*pszFormat == TEXT('.'))
1090       {
1091       ++pszFormat;
1092       cchMax = _ttol( pszFormat );
1093       }
1094
1095    // Then generate the output string, based on what we just learned
1096    // and what {lfValue} is.
1097    //
1098    if (lfValue >= 0 && fPlusUpFront)
1099       {
1100       *pszTarget++ = TEXT('+');
1101       }
1102    else if (lfValue < 0)
1103       {
1104       *pszTarget++ = TEXT('-');
1105       lfValue = 0 - lfValue;
1106       }
1107
1108    if (lfValue >= 1 || fZeroUpFront)
1109       {
1110       unsigned long ulValue = (unsigned long)lfValue;
1111       wsprintf (pszTarget, TEXT("%lu"), ulValue);
1112       pszTarget = &pszTarget[ lstrlen(pszTarget) ];
1113       lfValue -= (double)ulValue;
1114       }
1115
1116    if (cchMax != 0)
1117       {
1118       unsigned long ulValue;
1119       TCHAR szTemp[ cchRESOURCE ];
1120       LPTSTR pszDecimalPoint = pszTarget;
1121       *pszTarget++ = TEXT('.');
1122
1123       for (int ii = 0; ii < cchMax; ++ii)
1124          {
1125          lfValue *= 10.0;
1126          *pszTarget++ = TEXT('0');
1127          }
1128       ulValue = (unsigned long)lfValue;
1129
1130       wsprintf (szTemp, TEXT("%lu"), ulValue);
1131       szTemp[ cchMax ] = 0;
1132       lstrcpy (&pszDecimalPoint[ 1 + cchMax - lstrlen(szTemp) ], szTemp);
1133
1134       pszTarget = &pszTarget[ lstrlen(pszTarget) ];
1135       }
1136
1137    *pszTarget = TEXT('\0');
1138 }
1139
1140
1141 void lstrupr (LPTSTR psz)
1142 {
1143    for ( ; psz && *psz; psz = CharNext (psz))
1144       *psz = _ttoupper (*psz);
1145 }
1146
1147
1148 LPCTSTR lstrchr (LPCTSTR pszTarget, TCHAR ch)
1149 {
1150    for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget))
1151       {
1152       if (*pszTarget == ch)
1153          return pszTarget;
1154       }
1155    return NULL;
1156 }
1157
1158
1159 LPCTSTR lstrrchr (LPCTSTR pszTarget, TCHAR ch)
1160 {
1161    LPCTSTR pszLast = NULL;
1162
1163    for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget))
1164       {
1165       if (*pszTarget == ch)
1166          pszLast = pszTarget;
1167       }
1168    return pszLast;
1169 }
1170
1171
1172 int lstrncmpi (LPCTSTR pszA, LPCTSTR pszB, size_t cch)
1173 {
1174    if (!pszA || !pszB)
1175       {
1176       return (!pszB) - (!pszA);   // A,!B:1, !A,B:-1, !A,!B:0
1177       }
1178
1179    for ( ; cch > 0; cch--, pszA = CharNext(pszA), pszB = CharNext(pszB))
1180       {
1181       TCHAR chA = _ttoupper( *pszA );
1182       TCHAR chB = _ttoupper( *pszB );
1183
1184       if (!chA || !chB)
1185          return (!chB) - (!chA);    // A,!B:1, !A,B:-1, !A,!B:0
1186
1187       if (chA != chB)
1188          return (int)(chA) - (int)(chB);   // -1:A<B, 0:A==B, 1:A>B
1189       }
1190
1191    return 0;  // no differences before told to stop comparing, so A==B
1192 }
1193
1194
1195 void lstrncpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch)
1196 {
1197    size_t ich;
1198    for (ich = 0; ich < cch; ich++)
1199       {
1200       if ((pszTarget[ich] = pszSource[ich]) == TEXT('\0'))
1201          break;
1202       }
1203 }
1204
1205
1206 void lstrzcpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch)
1207 {
1208    cch = min(cch, (size_t)(1+lstrlen(pszSource)));
1209    lstrncpy (pszTarget, pszSource, cch-1);
1210    pszTarget[ cch-1 ] = TEXT('\0');
1211 }
1212
1213
1214 void lsplitpath (LPCTSTR pszSource,
1215                  LPTSTR pszDrive, LPTSTR pszPath, LPTSTR pszName, LPTSTR pszExt)
1216 {
1217    LPCTSTR  pszLastSlash = NULL;
1218    LPCTSTR  pszLastDot = NULL;
1219    LPCTSTR  pch;
1220    size_t   cchCopy;
1221
1222         /*
1223          * NOTE: This routine was snitched out of USERPRI.LIB 'cause the
1224          * one in there doesn't split the extension off the name properly.
1225          *
1226          * We assume that the path argument has the following form, where any
1227          * or all of the components may be missing.
1228          *
1229          *      <drive><dir><fname><ext>
1230          *
1231          * and each of the components has the following expected form(s)
1232          *
1233          *  drive:
1234          *      0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
1235          *      ':'
1236          *  dir:
1237          *      0 to _MAX_DIR-1 characters in the form of an absolute path
1238          *      (leading '/' or '\') or relative path, the last of which, if
1239          *      any, must be a '/' or '\'.  E.g -
1240          *      absolute path:
1241          *          \top\next\last\     ; or
1242          *          /top/next/last/
1243          *      relative path:
1244          *          top\next\last\      ; or
1245          *          top/next/last/
1246          *      Mixed use of '/' and '\' within a path is also tolerated
1247          *  fname:
1248          *      0 to _MAX_FNAME-1 characters not including the '.' character
1249          *  ext:
1250          *      0 to _MAX_EXT-1 characters where, if any, the first must be a
1251          *      '.'
1252          *
1253          */
1254
1255              // extract drive letter and :, if any
1256              //
1257    if (*(pszSource + _MAX_DRIVE - 2) == TEXT(':'))
1258       {
1259       if (pszDrive)
1260          {
1261          lstrncpy (pszDrive, pszSource, _MAX_DRIVE-1);
1262          pszDrive[ _MAX_DRIVE-1 ] = TEXT('\0');
1263          }
1264       pszSource += _MAX_DRIVE-1;
1265       }
1266    else if (pszDrive)
1267       {
1268       *pszDrive = TEXT('\0');
1269       }
1270
1271           // extract path string, if any.  pszSource now points to the first
1272           // character of the path, if any, or the filename or extension, if
1273           // no path was specified.  Scan ahead for the last occurence, if
1274           // any, of a '/' or '\' path separator character.  If none is found,
1275           // there is no path.  We will also note the last '.' character found,
1276           // if any, to aid in handling the extension.
1277           //
1278    for (pch = pszSource; *pch != TEXT('\0'); pch++)
1279       {
1280       if (*pch == TEXT('/') || *pch == TEXT('\\'))
1281          pszLastSlash = pch;
1282       else if (*pch == TEXT('.'))
1283          pszLastDot = pch;
1284       }
1285
1286           // if we found a '\\' or '/', fill in pszPath
1287           //
1288    if (pszLastSlash)
1289       {
1290       if (pszPath)
1291          {
1292          cchCopy = min( _MAX_DIR -1, pszLastSlash -pszSource +1 );
1293          lstrncpy (pszPath, pszSource, cchCopy);
1294          pszPath[ cchCopy ] = 0;
1295          }
1296       pszSource = pszLastSlash +1;
1297       }
1298    else if (pszPath)
1299       {
1300       *pszPath = TEXT('\0');
1301       }
1302
1303              // extract file name and extension, if any.  Path now points to
1304              // the first character of the file name, if any, or the extension
1305              // if no file name was given.  Dot points to the '.' beginning the
1306              // extension, if any.
1307              //
1308
1309    if (pszLastDot && (pszLastDot >= pszSource))
1310       {
1311                // found the marker for an extension -
1312                // copy the file name up to the '.'.
1313                //
1314       if (pszName)
1315          {
1316          cchCopy = min( _MAX_DIR-1, pszLastDot -pszSource );
1317          lstrncpy (pszName, pszSource, cchCopy);
1318          pszName[ cchCopy ] = 0;
1319          }
1320
1321                // now we can get the extension
1322                //
1323       if (pszExt)
1324          {
1325          lstrncpy (pszExt, pszLastDot, _MAX_EXT -1);
1326          pszExt[ _MAX_EXT-1 ] = TEXT('\0');
1327          }
1328       }
1329    else
1330       {
1331                // found no extension, give empty extension and copy rest of
1332                // string into fname.
1333                //
1334       if (pszName)
1335          {
1336          lstrncpy (pszName, pszSource, _MAX_FNAME -1);
1337          pszName[ _MAX_FNAME -1 ] = TEXT('\0');
1338          }
1339
1340       if (pszExt)
1341          {
1342          *pszExt = TEXT('\0');
1343          }
1344       }
1345 }
1346
1347
1348 LPCTSTR FindExtension (LPCTSTR name)
1349 {
1350    LPCTSTR pch;
1351
1352    if ((pch = lstrchr (FindBaseFileName(name), TEXT('.'))) != NULL)
1353       return pch;
1354
1355    return &name[ lstrlen(name) ];
1356 }
1357
1358
1359 LPCTSTR FindBaseFileName (LPCTSTR name)
1360 {
1361    LPCTSTR pszBase;
1362
1363    if ((pszBase = lstrrchr (name, TEXT('\\'))) != NULL)
1364       return CharNext(pszBase);
1365    else
1366       return name;
1367 }
1368
1369
1370 void ChangeExtension (LPTSTR pszOut, LPCTSTR pszIn, LPCTSTR pszExt, BOOL fForce)
1371 {
1372    LPCTSTR pch;
1373
1374    if (pszOut != pszIn)
1375       lstrcpy (pszOut, pszIn);
1376
1377    if ( (*(pch = FindExtension (pszOut)) != TEXT('\0')) && (!fForce) )
1378       return;
1379
1380    if (pszExt[0] == TEXT('.') || pszExt[0] == TEXT('\0'))
1381       {
1382       lstrcpy ((LPTSTR)pch, pszExt);
1383       }
1384    else
1385       {
1386       lstrcpy ((LPTSTR)pch, TEXT("."));
1387       lstrcat ((LPTSTR)pch, pszExt);
1388       }
1389 }
1390
1391
1392 void CopyBaseFileName (LPTSTR pszTarget, LPCTSTR pszSource)
1393 {
1394    lstrcpy (pszTarget, FindBaseFileName (pszSource));
1395
1396    LPTSTR pszExt;
1397    if ((pszExt = (LPTSTR)FindExtension(pszTarget)) != NULL)
1398       *pszExt = TEXT('\0');
1399 }
1400
1401
1402 /*
1403  * ANSI/UNICODE _______________________________________________________________
1404  *
1405  */
1406
1407 void CopyUnicodeToAnsi (LPSTR pszTargetA, LPCWSTR pszOriginalW, size_t cchMax)
1408 {
1409    static size_t cchBufferW = 0;
1410    static LPWSTR pszBufferW = NULL;
1411
1412    size_t cchSource = min( cchMax, (size_t)lstrlenW(pszOriginalW)+1 );
1413    if (cchSource > cchBufferW)
1414       {
1415       if (pszBufferW)
1416          FreeString (pszBufferW);
1417       pszBufferW = AllocateUnicode (cchSource);
1418       cchBufferW = cchSource;
1419       }
1420
1421    memcpy ((LPBYTE)pszBufferW, (LPBYTE)pszOriginalW, cchSource * sizeof(WCHAR));
1422    pszBufferW[ cchSource-1 ] = L'\0';
1423
1424    UINT cpTarget = CP_ACP;
1425    BOOL fDefault = FALSE;
1426    size_t cchOut = WideCharToMultiByte (cpTarget, 0, pszOriginalW, (INT)cchSource-1, pszTargetA, (INT)cchMax * 2, " ", &fDefault);
1427    pszTargetA[ cchOut ] = 0;
1428 }
1429
1430 void CopyAnsiToUnicode (LPWSTR pszTargetW, LPCSTR pszOriginalA, size_t cchMax)
1431 {
1432    static size_t cchBufferA = 0;
1433    static LPSTR  pszBufferA = NULL;
1434
1435    cchMax = min( cchMax, (size_t)lstrlenA(pszOriginalA)+1 );
1436    if (cchMax > cchBufferA)
1437       {
1438       if (pszBufferA)
1439          FreeString (pszBufferA);
1440       pszBufferA = AllocateAnsi (cchMax);
1441       cchBufferA = cchMax;
1442       }
1443
1444    memcpy ((LPBYTE)pszBufferA, (LPBYTE)pszOriginalA, cchMax);
1445    pszBufferA[ cchMax-1 ] = L'\0';
1446
1447    wsprintfW (pszTargetW, L"%hs", pszBufferA);
1448 }
1449
1450 void CopyAnsiToString (LPTSTR pszTarget, LPCSTR pszOriginalA, size_t cchMax)
1451 {
1452 #ifdef UNICODE
1453    CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA, cchMax);
1454 #else
1455    lstrzcpy ((LPSTR)pszTarget, pszOriginalA, cchMax);
1456 #endif
1457 }
1458
1459 void CopyUnicodeToString (LPTSTR pszTarget, LPCWSTR pszOriginalW, size_t cchMax)
1460 {
1461 #ifdef UNICODE
1462    lstrzcpy ((LPWSTR)pszTarget, pszOriginalW, cchMax);
1463 #else
1464    CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW, cchMax);
1465 #endif
1466 }
1467
1468 void CopyStringToUnicode (LPWSTR pszTargetW, LPCTSTR pszOriginal, size_t cchMax)
1469 {
1470 #ifdef UNICODE
1471    lstrzcpy (pszTargetW, (LPWSTR)pszOriginal, cchMax);
1472 #else
1473    CopyAnsiToUnicode (pszTargetW, (LPSTR)pszOriginal, cchMax);
1474 #endif
1475 }
1476
1477 void CopyStringToAnsi (LPSTR pszTargetA, LPCTSTR pszOriginal, size_t cchMax)
1478 {
1479 #ifdef UNICODE
1480    CopyUnicodeToAnsi (pszTargetA, (LPWSTR)pszOriginal, cchMax);
1481 #else
1482    lstrzcpy (pszTargetA, (LPSTR)pszOriginal, cchMax);
1483 #endif
1484 }
1485
1486
1487 void FreeString (LPCVOID pszString, LPCVOID pszOriginalString)
1488 {
1489    if ( (pszString != NULL) && (pszString != pszOriginalString) )
1490       {
1491       Free ((PVOID)pszString);
1492       }
1493 }
1494
1495
1496 LPSTR StringToAnsi (LPCTSTR pszOriginal)
1497 {
1498     if (!pszOriginal)
1499         return NULL;
1500     LPSTR pszTargetA;
1501     int len = lstrlen(pszOriginal);
1502     if ((pszTargetA = AllocateAnsi (1+len)) != NULL) {
1503 #ifdef UNICODE
1504         CopyUnicodeToAnsi (pszTargetA, pszOriginal);
1505 #else
1506         lstrcpy (pszTargetA, (LPSTR)pszOriginal);
1507 #endif
1508     }
1509     return pszTargetA;
1510 }
1511
1512 LPTSTR AnsiToString (LPCSTR pszOriginalA)
1513 {
1514     if (!pszOriginalA)
1515         return NULL;
1516     LPTSTR pszTarget;
1517     int lenA = lstrlenA(pszOriginalA);
1518     if ((pszTarget = AllocateString (1+lenA)) != NULL) {
1519 #ifdef UNICODE
1520         CopyAnsiToUnicode (pszTarget, pszOriginalA);
1521 #else
1522         lstrcpy (pszTarget, (LPSTR)pszOriginalA);
1523 #endif
1524     }
1525     return pszTarget;
1526 }
1527
1528
1529 LPWSTR StringToUnicode (LPCTSTR pszOriginal)
1530 {
1531     if (!pszOriginal)
1532         return NULL;
1533     LPWSTR pszTargetW;
1534     int len = lstrlen(pszOriginal);
1535     if ((pszTargetW = AllocateUnicode (1+len)) != NULL) {
1536 #ifdef UNICODE
1537         lstrcpyW ((LPWSTR)pszTargetW, (LPWSTR)pszOriginal);
1538 #else
1539         CopyAnsiToUnicode (pszTargetW, pszOriginal);
1540 #endif
1541     }
1542     return pszTargetW;
1543 }
1544
1545 LPTSTR UnicodeToString (LPCWSTR pszOriginalW)
1546 {
1547     if (!pszOriginalW)
1548         return NULL;
1549     LPTSTR pszTarget;
1550     if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL) {
1551 #ifdef UNICODE
1552         lstrcpyW ((LPWSTR)pszTarget, (LPWSTR)pszOriginalW);
1553 #else
1554         CopyUnicodeToAnsi (pszTarget, pszOriginalW);
1555 #endif
1556     }
1557     return pszTarget;
1558 }
1559
1560
1561 LPWSTR AnsiToUnicode (LPCSTR pszOriginalA)
1562 {
1563    if (!pszOriginalA)
1564       return NULL;
1565    LPWSTR pszTargetW;
1566    if ((pszTargetW = AllocateUnicode (1+lstrlenA(pszOriginalA))) != NULL)
1567       CopyAnsiToUnicode (pszTargetW, pszOriginalA);
1568    return pszTargetW;
1569 }
1570
1571 LPSTR UnicodeToAnsi (LPCWSTR pszOriginalW)
1572 {
1573    if (!pszOriginalW)
1574       return NULL;
1575    LPSTR pszTargetA;
1576    if ((pszTargetA = AllocateAnsi (1+lstrlenW(pszOriginalW))) != NULL)
1577       CopyUnicodeToAnsi (pszTargetA, pszOriginalW);
1578    return pszTargetA;
1579 }
1580
1581
1582 LPTSTR CloneAnsi (LPSTR pszOriginalA)
1583 {
1584    if (!pszOriginalA)
1585       return NULL;
1586
1587    LPTSTR pszTarget;
1588
1589    if ((pszTarget = AllocateString (1+lstrlenA(pszOriginalA))) != NULL)
1590       {
1591 #ifdef UNICODE
1592       CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA);
1593 #else
1594       lstrcpyA ((LPSTR)pszTarget, pszOriginalA);
1595 #endif
1596       }
1597
1598    return pszTarget;
1599 }
1600
1601
1602 LPTSTR CloneUnicode (LPWSTR pszOriginalW)
1603 {
1604    if (!pszOriginalW)
1605       return NULL;
1606
1607    LPTSTR pszTarget;
1608
1609    if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL)
1610       {
1611 #ifdef UNICODE
1612       lstrcpyW ((LPWSTR)pszTarget, pszOriginalW);
1613 #else
1614       CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW);
1615 #endif
1616       }
1617
1618    return pszTarget;
1619 }
1620
1621
1622 LPTSTR CloneMultiString (LPCTSTR mszOriginal)
1623 {
1624    if (!mszOriginal)
1625       return NULL;
1626
1627    size_t cchOld = 1; // for the second NUL-terminator
1628    for (LPCTSTR psz = mszOriginal; psz && *psz; psz += 1+lstrlen(psz))
1629       cchOld += lstrlen(psz) +1;
1630
1631    LPTSTR pszTarget;
1632    if ((pszTarget = AllocateString (cchOld)) != NULL)
1633       memcpy (pszTarget, mszOriginal, cchOld);
1634
1635    return pszTarget;
1636 }
1637
1638
1639 LPTSTR CloneString (LPTSTR pszOriginal)
1640 {
1641    if (!pszOriginal)
1642       return NULL;
1643
1644    LPTSTR pszTarget;
1645    if ((pszTarget = AllocateString (1+lstrlen(pszOriginal))) != NULL)
1646       lstrcpy (pszTarget, pszOriginal);
1647
1648    return pszTarget;
1649 }
1650
1651
1652 LPTSTR FixFormatString (LPTSTR pszFormat)
1653 {
1654    static TCHAR szFormat[ 256 ];
1655    lstrcpy (szFormat, TEXT("%s"));
1656
1657    if (*pszFormat == TEXT('%'))
1658       {
1659       lstrcpy (szFormat, pszFormat);
1660
1661       LPTSTR pch;
1662       for (pch = &szFormat[1]; *pch; ++pch)
1663          {
1664          if (!isfmtgarbage(*pch))
1665             {
1666             lstrcpy (pch, TEXT("s"));
1667             break;
1668             }
1669          }
1670       if (!*pch)
1671          {
1672          lstrcat (pch, TEXT("s"));
1673          }
1674       }
1675
1676    return szFormat;
1677 }
1678
1679