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