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