2 * Copyright 2000, International Business Machines Corporation and others.
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
11 #include <afs/param.h>
17 #include <WINNT/talocale.h>
22 * DEFINITIONS ________________________________________________________________
30 #define _MAX_FNAME 260
33 #define _ttoupper(_ch) (TCHAR)CharUpper((LPTSTR)_ch)
35 #define isfmtgarbage(_ch) (isdigit(_ch) || (_ch==TEXT('.')) || (_ch==TEXT(',')) || (_ch==TEXT('-')))
39 * PROTOTYPES _________________________________________________________________
43 LPTSTR cdecl vFormatString (LONG, LPCTSTR, va_list);
45 BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage);
46 BOOL TranslateError (LPTSTR pszText, ULONG status);
48 LPTSTR FixFormatString (LPTSTR pszFormat);
52 * ROUTINES ___________________________________________________________________
57 /*** GetString - Loads one or more consecutive strings from a resource file
62 void GetString (LPTSTR psz, int ids, int cchMax)
68 LPCSTRINGTEMPLATE pst;
69 if ((pst = TaLocale_GetStringResource (ids)) == NULL)
72 CopyUnicodeToString (psz, (LPWSTR)pst->achString, min(cchMax,pst->cchString+1));
73 if (psz[ lstrlen(psz)-1 ] != TEXT('+'))
75 psz[ lstrlen(psz)-1 ] = TEXT('\0');
77 cchMax -= lstrlen(psz);
84 /*** GetStringLength() - Returns the number of chars in a string resource
88 size_t GetStringLength (int ids)
94 LPCSTRINGTEMPLATE pst;
95 if ((pst = TaLocale_GetStringResource (ids)) == NULL)
98 char szAnsi[ cchRESOURCE * 2 + 1 ];
99 CopyUnicodeToAnsi (szAnsi, pst->achString, pst->cchString+1);
100 cch += 1+ lstrlenA(szAnsi);
102 if ((!pst->cchString) || (pst->achString[ pst->cchString-1 ] != L'+'))
113 /*** SearchMultiString() - scans multistrings for a given string
117 BOOL SearchMultiString (LPCTSTR pmsz, LPCTSTR pszString, BOOL fCaseSensitive)
121 for (LPCTSTR psz = pmsz; *psz; psz += 1+lstrlen(psz))
123 if (!*pszString && !lstrlen(psz))
125 else if (pszString && fCaseSensitive && !lstrcmp (psz, pszString))
127 else if (pszString && !fCaseSensitive && !lstrcmpi (psz, pszString))
136 /*** FormatMultiString() - manipulates multistrings ("EXA\0MP\0LE\0\0")
140 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LONG ids, LPCTSTR pszFormat, va_list arg)
142 static const TCHAR szMultiStringNULL[] = cszMultiStringNULL;
143 LPTSTR pszNew = vFormatString (ids, pszFormat, arg);
145 if (!pszNew || !*pszNew)
148 pszNew = (LPTSTR)szMultiStringNULL;
151 size_t cchOld = 1; // for the second NUL-terminator
152 for (LPTSTR psz = *ppszTarget; psz && *psz; psz += 1+lstrlen(psz))
153 cchOld += lstrlen(psz) +1;
156 if ((pszFinal = AllocateString (cchOld +lstrlen(pszNew) +1)) != NULL)
160 lstrcpy (pszFinal, pszNew);
164 lstrcpy (pszFinal, pszNew);
165 memcpy (&pszFinal[ lstrlen(pszNew)+1 ], *ppszTarget, sizeof(TCHAR) * cchOld);
167 else // (*ppszTarget && !fAddHead)
169 memcpy (pszFinal, *ppszTarget, sizeof(TCHAR) * cchOld);
170 lstrcpy (&pszFinal[ cchOld-1 ], pszNew);
172 pszFinal[ cchOld +lstrlen(pszNew) ] = TEXT('\0');
174 FreeString (*ppszTarget);
175 *ppszTarget = pszFinal;
178 if (pszNew != (LPTSTR)szMultiStringNULL)
183 void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, ...)
186 //if (pszFormat != NULL)
187 va_start (arg, pszFormat);
188 vFormatMultiString (ppszTarget, fAddHead, PtrToLong(pszTemplate), pszFormat, arg);
191 void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, ...)
194 //if (pszFormat != NULL)
195 va_start (arg, pszFormat);
196 vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg);
199 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, va_list arg)
201 vFormatMultiString (ppszTarget, fAddHead, PtrToLong(pszTemplate), pszFormat, arg);
204 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, va_list arg)
206 vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg);
210 /*** FormatString() - replacable-parameter version of wsprintf()
214 LPTSTR cdecl FormatString (LPCTSTR psz, LPCTSTR pszFmt, ...)
217 //if (pszFmt != NULL)
218 va_start (arg, pszFmt);
219 return vFormatString (PtrToLong(psz), pszFmt, arg);
222 LPTSTR cdecl FormatString (int ids, LPCTSTR pszFmt, ...)
225 //if (pszFmt != NULL)
226 va_start (arg, pszFmt);
227 return vFormatString ((LONG)ids, pszFmt, arg);
230 LPTSTR cdecl vFormatString (LPCTSTR psz, LPCTSTR pszFmt, va_list arg)
232 return vFormatString (PtrToLong(psz), pszFmt, arg);
235 LPTSTR cdecl vFormatString (int ids, LPCTSTR pszFmt, va_list arg)
237 return vFormatString ((LONG)ids, pszFmt, arg);
241 typedef enum // vartype
261 #define vtSTRING vtSTRINGW // %s gives vtSTRING
263 #define vtSTRING vtSTRINGA
266 #define cchMaxNUMBER 40
267 #define cszNULL TEXT("(null)")
269 LPTSTR cdecl vFormatString (LONG pszSource, LPCTSTR pszFmt, va_list arg)
273 LPTSTR pszOut = NULL;
279 TCHAR szFmt[ cchRESOURCE ];
284 if (HIWORD(pszSource) != 0) // It's a string
286 pszTemplate = (LPTSTR)LongToPtr(pszSource);
288 else // It's a message
290 cch = GetStringLength((INT)pszSource);
292 if ((pszTemplate = AllocateString (1+cch)) == NULL)
295 GetString (pszTemplate, (int)pszSource, (INT)cch);
299 // First off, count the number of arguments given in {pszFmt}.
306 for (psz = (LPTSTR)pszFmt; *psz; psz++)
308 if (*psz == TEXT('%'))
314 // If there are no arguments, then we're just expanding {pszSource}.
319 if (HIWORD(pszSource) == 0) // It's a message
323 else // (HIWORD(pszSource) != 0) // It's a string
325 psz = CloneString (pszTemplate);
331 // Otherwise, allocate an array of LPTSTR's, one for each
335 apszArgs = (LPTSTR*)Allocate (sizeof(LPTSTR) * nArgs);
337 if (apszArgs == NULL)
339 if (pszSource != PtrToLong(pszTemplate))
340 FreeString (pszTemplate);
344 memset (apszArgs, 0x00, sizeof(LPTSTR) * nArgs);
347 // Extract each argument in turn, using {pszFmt} and {arg}.
350 for (argno = 0; pszFmt && *pszFmt; argno++)
360 LARGE_INTEGER *arg_ldw;
363 // At this point, {pstFmt} points to the start of an argument's
364 // decription within the user-supplied format string
365 // (which may look like, say, "%-12.30ls%40lu"). Parse
366 // the "%-12.30" part of each argument first...
369 if ((*pszFmt != TEXT('%')) || (*(1+pszFmt) == TEXT('\0')))
372 lstrcpy (szFmt, pszFmt);
373 if ((psz = (LPTSTR)lstrchr (&szFmt[1], TEXT('%'))) != NULL)
377 for (ii = 1; szFmt[ii] == TEXT('-') || szFmt[ii] == TEXT(','); ii++)
379 cchMin = _ttol (&szFmt[ii]);
381 for (pszFmt++; *pszFmt && isfmtgarbage(*pszFmt); ++pszFmt)
383 if (*pszFmt == TEXT('%'))
387 if (!pszFmt || !*pszFmt)
389 if (*pszFmt == TEXT('%'))
393 // Yuck. At this point,
394 // cchMin is either 0 or whatever appeared as X in "%X.##s"
395 // pszFmt points to the first character after "%##.##"
397 // Step through the format string's definition ("c", "lu", etc)
398 // to determine what kind of argument this one is, setting {vt}
402 for (vt = vtINVALID; *pszFmt && *pszFmt != TEXT('%'); pszFmt++)
426 case TEXT('i'): // "%i"==int, "%li"==large_integer
444 else if (vt == vtWORD)
452 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
455 case TEXT('B'): // %B = double cb
457 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
460 case TEXT('b'): // %b = ULONG cb
462 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
466 if (vt == vtERROR) // %et == systemtime* (shown as elapsed)
468 else // (vt != vtERROR) // %t == systemtime* (shown as elapsed)
472 case TEXT('e'): // %e == error
476 case TEXT('a'): // %a = LPSOCKADDR_IN
478 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
487 // Now the fun part. Pop the appropriate argument off the
488 // stack; note that we had to know how big the argument is,
489 // and in what format, in order to do this.
495 arg_d = (LONG)(va_arg (arg, short));
500 arg_d = va_arg (arg, LONG);
505 arg_f = (double)( va_arg (arg, float) );
510 arg_f = va_arg (arg, double);
516 LPSTR arg_pszA = va_arg (arg, LPSTR);
518 if (arg_pszA == NULL)
519 arg_psz = CloneString (cszNULL);
521 arg_psz = CloneAnsi (arg_pszA);
523 cch = lstrlen(arg_psz);
529 LPWSTR arg_pszW = va_arg (arg, LPWSTR);
531 if (arg_pszW == NULL)
532 arg_psz = CloneString (cszNULL);
534 arg_psz = CloneUnicode (arg_pszW);
536 cch = lstrlen(arg_psz);
542 int arg_ids = va_arg (arg, int);
544 if ((cch = GetStringLength (arg_ids)) == 0)
545 arg_psz = CloneString (cszNULL);
548 if ((arg_psz = AllocateString (cch)) == NULL)
550 GetString (arg_psz, arg_ids, (INT)cch);
553 cch = lstrlen(arg_psz);
558 arg_f = va_arg (arg, double);
563 arg_f = (double)va_arg (arg, LONG);
569 arg_t = va_arg (arg, SYSTEMTIME*);
574 arg_d = va_arg (arg, DWORD);
579 arg_a = va_arg (arg, SOCKADDR_IN*);
584 arg_ldw = va_arg (arg, LARGE_INTEGER*);
585 cch = cchMaxNUMBER * 2;
590 // Okay; now, depending on what {vt} is, one of
591 // {arg_psz, arg_d, arg_f} has been set to match
592 // this argument. Allocate an string for apszArgs[]
593 // to represent the formatted, ready-for-output version
597 cch = max( cch, cchMin );
599 if ((apszArgs[ argno ] = AllocateString (cch+1)) == NULL)
605 wsprintf (apszArgs[ argno ], szFmt, (short)arg_d);
609 wsprintf (apszArgs[ argno ], szFmt, arg_d);
613 FormatDouble (apszArgs[ argno ], szFmt, arg_f);
617 FormatDouble (apszArgs[ argno ], szFmt, arg_f);
623 wsprintf (apszArgs[ argno ], szFmt, arg_psz);
624 FreeString (arg_psz);
629 FormatBytes (apszArgs[ argno ], szFmt, arg_f);
633 FormatElapsed (apszArgs[ argno ], szFmt, arg_t);
637 FormatTime (apszArgs[ argno ], szFmt, arg_t);
641 FormatError (apszArgs[ argno ], szFmt, arg_d);
645 FormatSockAddr (apszArgs[ argno ], szFmt, arg_a);
649 FormatLargeInt (apszArgs[ argno ], szFmt, arg_ldw);
655 // Determine how big the final string will be once the arguments
656 // are inserted, and allocate space for it.
661 for (psz = pszTemplate; *psz; )
663 if (*psz != TEXT('%'))
670 if (*(++psz) == TEXT('\0'))
680 for (argno = 0; *psz && isdigit (*psz); psz++)
683 argno += (int)(*psz -'0');
687 if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL)
688 cch += lstrlen (cszNULL);
690 cch += lstrlen (apszArgs[argno -1]);
693 if ((pszOut = AllocateString (cch+1)) == NULL)
697 // Then generate a final output, by copying over the template
698 // string into the final string, inserting arguments wherever
699 // a "%1" (or "%43" etc) is found.
702 for (psz = pszTemplate, pchOut = pszOut; *psz; )
704 if (*psz != TEXT('%'))
710 if (*(++psz) == TEXT('\0'))
719 for (argno = 0; *psz && isdigit (*psz); psz++)
722 argno += (int)(*psz -'0');
726 if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL)
727 lstrcpy (pchOut, cszNULL);
729 lstrcpy (pchOut, apszArgs[argno -1]);
730 pchOut = &pchOut[ lstrlen(pchOut) ];
736 if (apszArgs != NULL)
738 for (argno = 0; argno < nArgs; argno++)
740 if (apszArgs[ argno ] != NULL)
741 FreeString (apszArgs[ argno ]);
746 if (HIWORD(pszSource) == 0) // Did we allocate {pszTemplate}?
748 if (pszTemplate != NULL)
750 FreeString (pszTemplate);
758 /*** FormatSockAddr() - formats a SOCKADDR_IN as text
762 void FormatSockAddr (LPTSTR pszTarget, LPTSTR pszFmt, SOCKADDR_IN *paddr)
764 TCHAR szText[ cchRESOURCE ];
766 LPSTR pszSockAddrA = inet_ntoa (*(struct in_addr *)&paddr->sin_addr.s_addr);
767 LPTSTR pszSockAddr = AnsiToString (pszSockAddrA);
769 lstrcpy (szText, pszSockAddr);
771 FreeString (pszSockAddr, pszSockAddrA);
773 wsprintf (pszTarget, FixFormatString (pszFmt), szText);
777 /*** FormatElapsed() - formats a SYSTEMTIME* as HH:MM:SS
781 BOOL FormatElapsed (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst)
783 TCHAR szTimeSep[ cchRESOURCE ];
784 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_STIME, szTimeSep, cchRESOURCE))
785 lstrcpy (szTimeSep, TEXT(":"));
787 TCHAR szElapsed[ cchRESOURCE ];
788 wsprintf (szElapsed, TEXT("%02lu%s%02lu%s%02lu"),
789 pst->wHour + (pst->wDay * 24),
795 wsprintf (pszTarget, FixFormatString (pszFormatUser), szElapsed);
800 /*** FormatTime() - formats a SYSTEMTIME* according to the current locale
804 BOOL FormatTime (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst, BOOL fShowDate, BOOL fShowTime)
811 SystemTimeToFileTime (pst, &ft);
812 FileTimeToLocalFileTime (&ft, &lft);
813 FileTimeToSystemTime (&lft, <);
815 TCHAR szTime[ cchRESOURCE ];
816 TCHAR szDate[ cchRESOURCE ];
818 if ( ((pst->wYear == 1970) && (pst->wMonth == 1) && (pst->wDay == 1)) ||
819 ((pst->wYear == 0) && (pst->wMonth == 0) && (pst->wDay == 0)) )
821 szTime[0] = TEXT('\0');
826 GetTimeFormat (LOCALE_USER_DEFAULT, 0, <, NULL, szTime, cchRESOURCE);
827 GetDateFormat (LOCALE_USER_DEFAULT, DATE_SHORTDATE, <, NULL, szDate, cchRESOURCE);
829 if (fShowTime && fShowDate)
831 lstrcat (szTime, TEXT(" "));
832 lstrcat (szTime, szDate);
834 else if (fShowDate && !fShowTime)
836 lstrcpy (szTime, szDate);
840 wsprintf (pszTarget, FixFormatString (pszFormatUser), szTime);
845 /*** FormatError() - formats an error code as text
849 LPERRORPROC pfnTranslateError = NULL;
850 void SetErrorTranslationFunction (LPERRORPROC pfn)
852 pfnTranslateError = pfn;
855 BOOL FormatError (LPTSTR pszTarget, LPTSTR pszFmt, DWORD dwError)
857 TCHAR szError[ cchRESOURCE ];
858 BOOL rc = TranslateError (szError, dwError);
860 LPTSTR pchTarget = szError;
861 for (LPTSTR pchSource = szError; *pchSource; ++pchSource)
863 if (*pchSource == TEXT('\n'))
864 *pchTarget++ = TEXT(' ');
865 else if (*pchSource != TEXT('\r'))
866 *pchTarget++ = *pchSource;
868 *pchTarget = TEXT('\0');
870 wsprintf (pszTarget, FixFormatString (pszFmt), szError);
875 BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage)
879 // Guess an appropriate language ID if none was supplied explicitly
883 idLanguage = TaLocale_GetLanguage();
886 // See if the caller supplied a function for us to use.
888 if (pfnTranslateError)
890 rc = (*pfnTranslateError)(pszText, code, idLanguage);
893 // Try to get Windows to translate the thing...
897 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, idLanguage, pszText, cchRESOURCE, NULL))
903 // See if the message is buried in NTDLL...
908 if ((hiNTDLL = LoadLibrary (TEXT("NTDLL.DLL"))) != NULL)
910 if (FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, hiNTDLL, code, idLanguage, pszText, cchRESOURCE, NULL))
914 FreeLibrary (hiNTDLL);
922 BOOL TranslateError (LPTSTR pszText, ULONG status)
926 if ((rc = TranslateErrorFunc (pszText, status, 0)) == TRUE)
928 wsprintf (&pszText[ lstrlen(pszText) ], TEXT(" (0x%08lX)"), status);
932 wsprintf (pszText, TEXT("0x%08lX"), status);
939 /*** FormatBytes() - formats L1048576 as "1.0 MB" (etc)
941 * Wherever "%1" appears is where the number will show up (formatted
942 * according to {pszFormat}).
946 void FormatBytes (LPTSTR pszTarget, LPTSTR pszFormatUser, double cb)
948 TCHAR szFormat[ cchRESOURCE ];
949 TCHAR szTemplate[ cchRESOURCE ];
950 TCHAR szJustTheNumber[ cchRESOURCE ];
954 // The first task is to generate a useful {pszFormat}; it may
955 // come in looking like "%-15.15s" or "%0.2b" or "%lu" --who knows.
956 // We'll need it to produce a formatted string from a DWORD, so
957 // make sure it ends in "lf".
960 if (*pszFormatUser != TEXT('%'))
962 lstrcpy (szFormat, TEXT("%.2lf")); // sigh--totally bogus format string.
966 lstrcpy (szFormat, pszFormatUser);
969 for (pch = &szFormat[1]; *pch; ++pch)
971 if (!isfmtgarbage(*pch))
973 lstrcpy (pch, TEXT("lf"));
979 lstrcat (pch, TEXT("lf"));
984 // The next step is to generate just the number portion of the
985 // output; that will look like "0.90" or "5.3", or whatever.
988 FormatDouble (szJustTheNumber, szFormat, cb/1048576.0);
989 lstrcpy (szTemplate, TEXT("%1 MB"));
992 // Cheesy bit: if all we have are 0's and "."'s, just
993 // make the number read "0".
996 for (pch = szJustTheNumber; *pch; ++pch)
998 if (*pch != TEXT('0') && *pch != TEXT('.'))
1003 lstrcpy (szJustTheNumber, TEXT("0"));
1007 // Not particularly hard. Now let FormatString() generate
1008 // the final output, by tacking on the necessary units.
1011 if ((pszOut = FormatString (szTemplate, TEXT("%s"), szJustTheNumber)) == NULL)
1013 // whoops--out of memory?
1014 // well, just give it back in bytes--we can still do that.
1016 FormatDouble (pszTarget, szFormat, cb);
1020 lstrcpy (pszTarget, pszOut);
1021 FreeString (pszOut);
1026 /*** FormatLargeInt() - formats a large int as "hidword,,lodword"
1030 void FormatLargeInt (LPTSTR pszTarget, LPTSTR pszFormatUser, LARGE_INTEGER *pldw)
1032 // Generate just the number portion of the output;
1033 // that will look like "###,,###"
1035 TCHAR szJustTheNumber[ cchRESOURCE ];
1036 wsprintf (szJustTheNumber, TEXT("%lu,,%lu"), (DWORD)pldw->HighPart, (DWORD)pldw->LowPart);
1038 // Not particularly hard. Now let FormatString() generate
1039 // the final output, by tacking on the necessary units
1042 wsprintf (pszTarget, FixFormatString (pszFormatUser), szJustTheNumber);
1046 /*** FormatDouble() - like wsprintf(), but for one argument (a double)
1048 * since {wsprintf} doesn't understand "%lf", "%f", "%g", "%e", etc,
1049 * use this function instead. {pszFormat} should consist of nothing
1050 * except the formatting string: thus, "%3.15lf" is okay, but "hi %lf there"
1055 void FormatDouble (LPTSTR pszTarget, LPTSTR pszFormat, double lfValue)
1057 BOOL fPlusUpFront = FALSE;
1058 BOOL fZeroUpFront = FALSE;
1062 // First, parse the format string: look for "%{zero|plus}{min}.{max}",
1063 // and ignore any other characters.
1065 while (*pszFormat && *pszFormat != TEXT('%'))
1068 while (*pszFormat == TEXT('%') ||
1069 *pszFormat == TEXT('0') ||
1070 *pszFormat == TEXT('+') ||
1071 *pszFormat == TEXT('-') )
1073 if (*pszFormat == TEXT('0'))
1075 fZeroUpFront = TRUE;
1077 else if (*pszFormat == TEXT('+'))
1079 fPlusUpFront = TRUE;
1084 cchMin = _ttol( pszFormat );
1086 while (isdigit(*pszFormat))
1089 if (*pszFormat == TEXT('.'))
1092 cchMax = _ttol( pszFormat );
1095 // Then generate the output string, based on what we just learned
1096 // and what {lfValue} is.
1098 if (lfValue >= 0 && fPlusUpFront)
1100 *pszTarget++ = TEXT('+');
1102 else if (lfValue < 0)
1104 *pszTarget++ = TEXT('-');
1105 lfValue = 0 - lfValue;
1108 if (lfValue >= 1 || fZeroUpFront)
1110 unsigned long ulValue = (unsigned long)lfValue;
1111 wsprintf (pszTarget, TEXT("%lu"), ulValue);
1112 pszTarget = &pszTarget[ lstrlen(pszTarget) ];
1113 lfValue -= (double)ulValue;
1118 unsigned long ulValue;
1119 TCHAR szTemp[ cchRESOURCE ];
1120 LPTSTR pszDecimalPoint = pszTarget;
1121 *pszTarget++ = TEXT('.');
1123 for (int ii = 0; ii < cchMax; ++ii)
1126 *pszTarget++ = TEXT('0');
1128 ulValue = (unsigned long)lfValue;
1130 wsprintf (szTemp, TEXT("%lu"), ulValue);
1131 szTemp[ cchMax ] = 0;
1132 lstrcpy (&pszDecimalPoint[ 1 + cchMax - lstrlen(szTemp) ], szTemp);
1134 pszTarget = &pszTarget[ lstrlen(pszTarget) ];
1137 *pszTarget = TEXT('\0');
1141 void lstrupr (LPTSTR psz)
1143 for ( ; psz && *psz; psz = CharNext (psz))
1144 *psz = _ttoupper (*psz);
1148 LPCTSTR lstrchr (LPCTSTR pszTarget, TCHAR ch)
1150 for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget))
1152 if (*pszTarget == ch)
1159 LPCTSTR lstrrchr (LPCTSTR pszTarget, TCHAR ch)
1161 LPCTSTR pszLast = NULL;
1163 for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget))
1165 if (*pszTarget == ch)
1166 pszLast = pszTarget;
1172 int lstrncmpi (LPCTSTR pszA, LPCTSTR pszB, size_t cch)
1176 return (!pszB) - (!pszA); // A,!B:1, !A,B:-1, !A,!B:0
1179 for ( ; cch > 0; cch--, pszA = CharNext(pszA), pszB = CharNext(pszB))
1181 TCHAR chA = _ttoupper( *pszA );
1182 TCHAR chB = _ttoupper( *pszB );
1185 return (!chB) - (!chA); // A,!B:1, !A,B:-1, !A,!B:0
1188 return (int)(chA) - (int)(chB); // -1:A<B, 0:A==B, 1:A>B
1191 return 0; // no differences before told to stop comparing, so A==B
1195 void lstrncpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch)
1198 for (ich = 0; ich < cch; ich++)
1200 if ((pszTarget[ich] = pszSource[ich]) == TEXT('\0'))
1206 void lstrzcpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch)
1208 cch = min(cch, (size_t)(1+lstrlen(pszSource)));
1209 lstrncpy (pszTarget, pszSource, cch-1);
1210 pszTarget[ cch-1 ] = TEXT('\0');
1214 void lsplitpath (LPCTSTR pszSource,
1215 LPTSTR pszDrive, LPTSTR pszPath, LPTSTR pszName, LPTSTR pszExt)
1217 LPCTSTR pszLastSlash = NULL;
1218 LPCTSTR pszLastDot = NULL;
1223 * NOTE: This routine was snitched out of USERPRI.LIB 'cause the
1224 * one in there doesn't split the extension off the name properly.
1226 * We assume that the path argument has the following form, where any
1227 * or all of the components may be missing.
1229 * <drive><dir><fname><ext>
1231 * and each of the components has the following expected form(s)
1234 * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
1237 * 0 to _MAX_DIR-1 characters in the form of an absolute path
1238 * (leading '/' or '\') or relative path, the last of which, if
1239 * any, must be a '/' or '\'. E.g -
1241 * \top\next\last\ ; or
1244 * top\next\last\ ; or
1246 * Mixed use of '/' and '\' within a path is also tolerated
1248 * 0 to _MAX_FNAME-1 characters not including the '.' character
1250 * 0 to _MAX_EXT-1 characters where, if any, the first must be a
1255 // extract drive letter and :, if any
1257 if (*(pszSource + _MAX_DRIVE - 2) == TEXT(':'))
1261 lstrncpy (pszDrive, pszSource, _MAX_DRIVE-1);
1262 pszDrive[ _MAX_DRIVE-1 ] = TEXT('\0');
1264 pszSource += _MAX_DRIVE-1;
1268 *pszDrive = TEXT('\0');
1271 // extract path string, if any. pszSource now points to the first
1272 // character of the path, if any, or the filename or extension, if
1273 // no path was specified. Scan ahead for the last occurence, if
1274 // any, of a '/' or '\' path separator character. If none is found,
1275 // there is no path. We will also note the last '.' character found,
1276 // if any, to aid in handling the extension.
1278 for (pch = pszSource; *pch != TEXT('\0'); pch++)
1280 if (*pch == TEXT('/') || *pch == TEXT('\\'))
1282 else if (*pch == TEXT('.'))
1286 // if we found a '\\' or '/', fill in pszPath
1292 cchCopy = min( _MAX_DIR -1, pszLastSlash -pszSource +1 );
1293 lstrncpy (pszPath, pszSource, cchCopy);
1294 pszPath[ cchCopy ] = 0;
1296 pszSource = pszLastSlash +1;
1300 *pszPath = TEXT('\0');
1303 // extract file name and extension, if any. Path now points to
1304 // the first character of the file name, if any, or the extension
1305 // if no file name was given. Dot points to the '.' beginning the
1306 // extension, if any.
1309 if (pszLastDot && (pszLastDot >= pszSource))
1311 // found the marker for an extension -
1312 // copy the file name up to the '.'.
1316 cchCopy = min( _MAX_DIR-1, pszLastDot -pszSource );
1317 lstrncpy (pszName, pszSource, cchCopy);
1318 pszName[ cchCopy ] = 0;
1321 // now we can get the extension
1325 lstrncpy (pszExt, pszLastDot, _MAX_EXT -1);
1326 pszExt[ _MAX_EXT-1 ] = TEXT('\0');
1331 // found no extension, give empty extension and copy rest of
1332 // string into fname.
1336 lstrncpy (pszName, pszSource, _MAX_FNAME -1);
1337 pszName[ _MAX_FNAME -1 ] = TEXT('\0');
1342 *pszExt = TEXT('\0');
1348 LPCTSTR FindExtension (LPCTSTR name)
1352 if ((pch = lstrchr (FindBaseFileName(name), TEXT('.'))) != NULL)
1355 return &name[ lstrlen(name) ];
1359 LPCTSTR FindBaseFileName (LPCTSTR name)
1363 if ((pszBase = lstrrchr (name, TEXT('\\'))) != NULL)
1364 return CharNext(pszBase);
1370 void ChangeExtension (LPTSTR pszOut, LPCTSTR pszIn, LPCTSTR pszExt, BOOL fForce)
1374 if (pszOut != pszIn)
1375 lstrcpy (pszOut, pszIn);
1377 if ( (*(pch = FindExtension (pszOut)) != TEXT('\0')) && (!fForce) )
1380 if (pszExt[0] == TEXT('.') || pszExt[0] == TEXT('\0'))
1382 lstrcpy ((LPTSTR)pch, pszExt);
1386 lstrcpy ((LPTSTR)pch, TEXT("."));
1387 lstrcat ((LPTSTR)pch, pszExt);
1392 void CopyBaseFileName (LPTSTR pszTarget, LPCTSTR pszSource)
1394 lstrcpy (pszTarget, FindBaseFileName (pszSource));
1397 if ((pszExt = (LPTSTR)FindExtension(pszTarget)) != NULL)
1398 *pszExt = TEXT('\0');
1403 * ANSI/UNICODE _______________________________________________________________
1407 void CopyUnicodeToAnsi (LPSTR pszTargetA, LPCWSTR pszOriginalW, size_t cchMax)
1409 static size_t cchBufferW = 0;
1410 static LPWSTR pszBufferW = NULL;
1412 size_t cchSource = min( cchMax, (size_t)lstrlenW(pszOriginalW)+1 );
1413 if (cchSource > cchBufferW)
1416 FreeString (pszBufferW);
1417 pszBufferW = AllocateUnicode (cchSource);
1418 cchBufferW = cchSource;
1421 memcpy ((LPBYTE)pszBufferW, (LPBYTE)pszOriginalW, cchSource * sizeof(WCHAR));
1422 pszBufferW[ cchSource-1 ] = L'\0';
1424 UINT cpTarget = CP_ACP;
1425 BOOL fDefault = FALSE;
1426 size_t cchOut = WideCharToMultiByte (cpTarget, 0, pszOriginalW, (INT)cchSource-1, pszTargetA, (INT)cchMax * 2, TEXT(" "), &fDefault);
1427 pszTargetA[ cchOut ] = 0;
1430 void CopyAnsiToUnicode (LPWSTR pszTargetW, LPCSTR pszOriginalA, size_t cchMax)
1432 static size_t cchBufferA = 0;
1433 static LPSTR pszBufferA = NULL;
1435 cchMax = min( cchMax, (size_t)lstrlenA(pszOriginalA)+1 );
1436 if (cchMax > cchBufferA)
1439 FreeString (pszBufferA);
1440 pszBufferA = AllocateAnsi (cchMax);
1441 cchBufferA = cchMax;
1444 memcpy ((LPBYTE)pszBufferA, (LPBYTE)pszOriginalA, cchMax);
1445 pszBufferA[ cchMax-1 ] = L'\0';
1447 wsprintfW (pszTargetW, L"%hs", pszBufferA);
1450 void CopyAnsiToString (LPTSTR pszTarget, LPCSTR pszOriginalA, size_t cchMax)
1453 CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA, cchMax);
1455 lstrzcpy ((LPSTR)pszTarget, pszOriginalA, cchMax);
1459 void CopyUnicodeToString (LPTSTR pszTarget, LPCWSTR pszOriginalW, size_t cchMax)
1462 lstrzcpy ((LPWSTR)pszTarget, pszOriginalW, cchMax);
1464 CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW, cchMax);
1468 void CopyStringToUnicode (LPWSTR pszTargetW, LPCTSTR pszOriginal, size_t cchMax)
1471 lstrzcpy (pszTargetW, (LPWSTR)pszOriginal, cchMax);
1473 CopyAnsiToUnicode (pszTargetW, (LPSTR)pszOriginal, cchMax);
1477 void CopyStringToAnsi (LPSTR pszTargetA, LPCTSTR pszOriginal, size_t cchMax)
1480 CopyUnicodeToAnsi (pszTargetA, (LPWSTR)pszOriginal, cchMax);
1482 lstrzcpy (pszTargetA, (LPSTR)pszOriginal, cchMax);
1487 void FreeString (LPCVOID pszString, LPCVOID pszOriginalString)
1489 if ( (pszString != NULL) && (pszString != pszOriginalString) )
1491 Free ((PVOID)pszString);
1496 LPSTR StringToAnsi (LPCTSTR pszOriginal)
1501 int len = lstrlen(pszOriginal);
1502 if ((pszTargetA = AllocateAnsi (1+len)) != NULL) {
1504 CopyUnicodeToAnsi (pszTargetA, pszOriginal);
1506 lstrcpy (pszTargetA, (LPSTR)pszOriginal);
1512 LPTSTR AnsiToString (LPCSTR pszOriginalA)
1517 int lenA = lstrlenA(pszOriginalA);
1518 if ((pszTarget = AllocateString (1+lenA)) != NULL) {
1520 CopyAnsiToUnicode (pszTarget, pszOriginalA);
1522 lstrcpy (pszTarget, (LPSTR)pszOriginalA);
1529 LPWSTR StringToUnicode (LPCTSTR pszOriginal)
1534 int len = lstrlen(pszOriginal);
1535 if ((pszTargetW = AllocateUnicode (1+len)) != NULL) {
1537 lstrcpyW ((LPWSTR)pszTargetW, (LPWSTR)pszOriginal);
1539 CopyAnsiToUnicode (pszTargetW, pszOriginal);
1545 LPTSTR UnicodeToString (LPCWSTR pszOriginalW)
1550 if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL) {
1552 lstrcpyW ((LPWSTR)pszTargetW, (LPWSTR)pszOriginal);
1554 CopyUnicodeToAnsi (pszTarget, pszOriginalW);
1561 LPWSTR AnsiToUnicode (LPCSTR pszOriginalA)
1566 if ((pszTargetW = AllocateUnicode (1+lstrlenA(pszOriginalA))) != NULL)
1567 CopyAnsiToUnicode (pszTargetW, pszOriginalA);
1571 LPSTR UnicodeToAnsi (LPCWSTR pszOriginalW)
1576 if ((pszTargetA = AllocateAnsi (1+lstrlenW(pszOriginalW))) != NULL)
1577 CopyUnicodeToAnsi (pszTargetA, pszOriginalW);
1582 LPTSTR CloneAnsi (LPSTR pszOriginalA)
1589 if ((pszTarget = AllocateString (1+lstrlenA(pszOriginalA))) != NULL)
1592 CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA);
1594 lstrcpyA ((LPSTR)pszTarget, pszOriginalA);
1602 LPTSTR CloneUnicode (LPWSTR pszOriginalW)
1609 if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL)
1612 lstrcpyW ((LPWSTR)pszTarget, pszOriginalW);
1614 CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW);
1622 LPTSTR CloneMultiString (LPCTSTR mszOriginal)
1627 size_t cchOld = 1; // for the second NUL-terminator
1628 for (LPCTSTR psz = mszOriginal; psz && *psz; psz += 1+lstrlen(psz))
1629 cchOld += lstrlen(psz) +1;
1632 if ((pszTarget = AllocateString (cchOld)) != NULL)
1633 memcpy (pszTarget, mszOriginal, cchOld);
1639 LPTSTR CloneString (LPTSTR pszOriginal)
1645 if ((pszTarget = AllocateString (1+lstrlen(pszOriginal))) != NULL)
1646 lstrcpy (pszTarget, pszOriginal);
1652 LPTSTR FixFormatString (LPTSTR pszFormat)
1654 static TCHAR szFormat[ 256 ];
1655 lstrcpy (szFormat, TEXT("%s"));
1657 if (*pszFormat == TEXT('%'))
1659 lstrcpy (szFormat, pszFormat);
1662 for (pch = &szFormat[1]; *pch; ++pch)
1664 if (!isfmtgarbage(*pch))
1666 lstrcpy (pch, TEXT("s"));
1672 lstrcat (pch, TEXT("s"));