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>
15 #include <WINNT/talocale.h>
20 * DEFINITIONS ________________________________________________________________
28 #define _MAX_FNAME 260
31 #define _ttoupper(_ch) (TCHAR)CharUpper((LPTSTR)_ch)
33 #define isfmtgarbage(_ch) (isdigit(_ch) || (_ch==TEXT('.')) || (_ch==TEXT(',')) || (_ch==TEXT('-')))
37 * PROTOTYPES _________________________________________________________________
41 LPTSTR cdecl vFormatString (LONG, LPCTSTR, va_list);
43 BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage);
44 BOOL TranslateError (LPTSTR pszText, ULONG status);
46 LPTSTR FixFormatString (LPTSTR pszFormat);
50 * ROUTINES ___________________________________________________________________
55 /*** GetString - Loads one or more consecutive strings from a resource file
60 void GetString (LPTSTR psz, int ids, int cchMax)
66 LPCSTRINGTEMPLATE pst;
67 if ((pst = TaLocale_GetStringResource (ids)) == NULL)
70 CopyUnicodeToString (psz, (LPWSTR)pst->achString, min(cchMax,pst->cchString+1));
71 if (psz[ lstrlen(psz)-1 ] != TEXT('+'))
73 psz[ lstrlen(psz)-1 ] = TEXT('\0');
75 cchMax -= lstrlen(psz);
82 /*** GetStringLength() - Returns the number of chars in a string resource
86 size_t GetStringLength (int ids)
92 LPCSTRINGTEMPLATE pst;
93 if ((pst = TaLocale_GetStringResource (ids)) == NULL)
96 char szAnsi[ cchRESOURCE * 2 + 1 ];
97 CopyUnicodeToAnsi (szAnsi, pst->achString, pst->cchString+1);
98 cch += 1+ lstrlenA(szAnsi);
100 if ((!pst->cchString) || (pst->achString[ pst->cchString-1 ] != L'+'))
111 /*** SearchMultiString() - scans multistrings for a given string
115 BOOL SearchMultiString (LPCTSTR pmsz, LPCTSTR pszString, BOOL fCaseSensitive)
119 for (LPCTSTR psz = pmsz; *psz; psz += 1+lstrlen(psz))
121 if (!*pszString && !lstrlen(psz))
123 else if (pszString && fCaseSensitive && !lstrcmp (psz, pszString))
125 else if (pszString && !fCaseSensitive && !lstrcmpi (psz, pszString))
134 /*** FormatMultiString() - manipulates multistrings ("EXA\0MP\0LE\0\0")
138 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LONG ids, LPCTSTR pszFormat, va_list arg)
140 static const TCHAR szMultiStringNULL[] = cszMultiStringNULL;
141 LPTSTR pszNew = vFormatString (ids, pszFormat, arg);
143 if (!pszNew || !*pszNew)
146 pszNew = (LPTSTR)szMultiStringNULL;
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;
154 if ((pszFinal = AllocateString (cchOld +lstrlen(pszNew) +1)) != NULL)
158 lstrcpy (pszFinal, pszNew);
162 lstrcpy (pszFinal, pszNew);
163 memcpy (&pszFinal[ lstrlen(pszNew)+1 ], *ppszTarget, sizeof(TCHAR) * cchOld);
165 else // (*ppszTarget && !fAddHead)
167 memcpy (pszFinal, *ppszTarget, sizeof(TCHAR) * cchOld);
168 lstrcpy (&pszFinal[ cchOld-1 ], pszNew);
170 pszFinal[ cchOld +lstrlen(pszNew) ] = TEXT('\0');
172 FreeString (*ppszTarget);
173 *ppszTarget = pszFinal;
176 if (pszNew != (LPTSTR)szMultiStringNULL)
181 void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, ...)
184 if (pszFormat != NULL)
185 va_start (arg, pszFormat);
186 vFormatMultiString (ppszTarget, fAddHead, (LONG)pszTemplate, pszFormat, arg);
189 void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, ...)
192 if (pszFormat != NULL)
193 va_start (arg, pszFormat);
194 vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg);
197 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, va_list arg)
199 vFormatMultiString (ppszTarget, fAddHead, (LONG)pszTemplate, pszFormat, arg);
202 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, va_list arg)
204 vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg);
208 /*** FormatString() - replacable-parameter version of wsprintf()
212 LPTSTR cdecl FormatString (LPCTSTR psz, LPCTSTR pszFmt, ...)
216 va_start (arg, pszFmt);
217 return vFormatString ((LONG)psz, pszFmt, arg);
220 LPTSTR cdecl FormatString (int ids, LPCTSTR pszFmt, ...)
224 va_start (arg, pszFmt);
225 return vFormatString ((LONG)ids, pszFmt, arg);
228 LPTSTR cdecl vFormatString (LPCTSTR psz, LPCTSTR pszFmt, va_list arg)
230 return vFormatString ((LONG)psz, pszFmt, arg);
233 LPTSTR cdecl vFormatString (int ids, LPCTSTR pszFmt, va_list arg)
235 return vFormatString ((LONG)ids, pszFmt, arg);
239 typedef enum // vartype
259 #define vtSTRING vtSTRINGW // %s gives vtSTRING
261 #define vtSTRING vtSTRINGA
264 #define cchMaxNUMBER 40
265 #define cszNULL TEXT("(null)")
267 LPTSTR cdecl vFormatString (LONG pszSource, LPCTSTR pszFmt, va_list arg)
271 LPTSTR pszOut = NULL;
277 TCHAR szFmt[ cchRESOURCE ];
282 if (HIWORD(pszSource) != 0) // It's a string
284 pszTemplate = (LPTSTR)pszSource;
286 else // It's a message
288 cch = GetStringLength ((int)pszSource);
290 if ((pszTemplate = AllocateString (1+cch)) == NULL)
293 GetString (pszTemplate, (int)pszSource, cch);
297 // First off, count the number of arguments given in {pszFmt}.
304 for (psz = (LPTSTR)pszFmt; *psz; psz++)
306 if (*psz == TEXT('%'))
312 // If there are no arguments, then we're just expanding {pszSource}.
317 if (HIWORD(pszSource) == 0) // It's a message
321 else // (HIWORD(pszSource) != 0) // It's a string
323 psz = CloneString (pszTemplate);
329 // Otherwise, allocate an array of LPTSTR's, one for each
333 apszArgs = (LPTSTR*)Allocate (sizeof(LPTSTR) * nArgs);
335 if (apszArgs == NULL)
337 if (pszSource != (LONG)pszTemplate)
338 FreeString (pszTemplate);
342 memset (apszArgs, 0x00, sizeof(LPTSTR) * nArgs);
345 // Extract each argument in turn, using {pszFmt} and {arg}.
348 for (argno = 0; pszFmt && *pszFmt; argno++)
358 LARGE_INTEGER *arg_ldw;
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...
367 if ((*pszFmt != TEXT('%')) || (*(1+pszFmt) == TEXT('\0')))
370 lstrcpy (szFmt, pszFmt);
371 if ((psz = (LPTSTR)lstrchr (&szFmt[1], TEXT('%'))) != NULL)
374 for (int ii = 1; szFmt[ii] == TEXT('-') || szFmt[ii] == TEXT(','); ii++)
376 cchMin = _ttol (&szFmt[ii]);
378 for (pszFmt++; *pszFmt && isfmtgarbage(*pszFmt); ++pszFmt)
380 if (*pszFmt == TEXT('%'))
384 if (!pszFmt || !*pszFmt)
386 if (*pszFmt == TEXT('%'))
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 "%##.##"
394 // Step through the format string's definition ("c", "lu", etc)
395 // to determine what kind of argument this one is, setting {vt}
399 for (vt = vtINVALID; *pszFmt && *pszFmt != TEXT('%'); pszFmt++)
423 case TEXT('i'): // "%i"==int, "%li"==large_integer
441 else if (vt == vtWORD)
449 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
452 case TEXT('B'): // %B = double cb
454 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
457 case TEXT('b'): // %b = ULONG cb
459 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
463 if (vt == vtERROR) // %et == systemtime* (shown as elapsed)
465 else // (vt != vtERROR) // %t == systemtime* (shown as elapsed)
469 case TEXT('e'): // %e == error
473 case TEXT('a'): // %a = LPSOCKADDR_IN
475 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
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.
492 arg_d = (LONG)(va_arg (arg, short));
497 arg_d = va_arg (arg, LONG);
502 arg_f = (double)( va_arg (arg, float) );
507 arg_f = va_arg (arg, double);
513 LPSTR arg_pszA = va_arg (arg, LPSTR);
515 if (arg_pszA == NULL)
516 arg_psz = CloneString (cszNULL);
518 arg_psz = CloneAnsi (arg_pszA);
520 cch = lstrlen(arg_psz);
526 LPWSTR arg_pszW = va_arg (arg, LPWSTR);
528 if (arg_pszW == NULL)
529 arg_psz = CloneString (cszNULL);
531 arg_psz = CloneUnicode (arg_pszW);
533 cch = lstrlen(arg_psz);
539 int arg_ids = va_arg (arg, int);
541 if ((cch = GetStringLength (arg_ids)) == 0)
542 arg_psz = CloneString (cszNULL);
545 if ((arg_psz = AllocateString (cch)) == NULL)
547 GetString (arg_psz, arg_ids, cch);
550 cch = lstrlen(arg_psz);
555 arg_f = va_arg (arg, double);
560 arg_f = (double)va_arg (arg, LONG);
566 arg_t = va_arg (arg, SYSTEMTIME*);
571 arg_d = va_arg (arg, DWORD);
576 arg_a = va_arg (arg, SOCKADDR_IN*);
581 arg_ldw = va_arg (arg, LARGE_INTEGER*);
582 cch = cchMaxNUMBER * 2;
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
594 cch = max( cch, cchMin );
596 if ((apszArgs[ argno ] = AllocateString (cch+1)) == NULL)
602 wsprintf (apszArgs[ argno ], szFmt, (short)arg_d);
606 wsprintf (apszArgs[ argno ], szFmt, arg_d);
610 FormatDouble (apszArgs[ argno ], szFmt, arg_f);
614 FormatDouble (apszArgs[ argno ], szFmt, arg_f);
620 wsprintf (apszArgs[ argno ], szFmt, arg_psz);
621 FreeString (arg_psz);
626 FormatBytes (apszArgs[ argno ], szFmt, arg_f);
630 FormatElapsed (apszArgs[ argno ], szFmt, arg_t);
634 FormatTime (apszArgs[ argno ], szFmt, arg_t);
638 FormatError (apszArgs[ argno ], szFmt, arg_d);
642 FormatSockAddr (apszArgs[ argno ], szFmt, arg_a);
646 FormatLargeInt (apszArgs[ argno ], szFmt, arg_ldw);
652 // Determine how big the final string will be once the arguments
653 // are inserted, and allocate space for it.
658 for (psz = pszTemplate; *psz; )
660 if (*psz != TEXT('%'))
667 if (*(++psz) == TEXT('\0'))
677 for (argno = 0; *psz && isdigit (*psz); psz++)
680 argno += (int)(*psz -'0');
684 if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL)
685 cch += lstrlen (cszNULL);
687 cch += lstrlen (apszArgs[argno -1]);
690 if ((pszOut = AllocateString (cch+1)) == NULL)
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.
699 for (psz = pszTemplate, pchOut = pszOut; *psz; )
701 if (*psz != TEXT('%'))
707 if (*(++psz) == TEXT('\0'))
716 for (argno = 0; *psz && isdigit (*psz); psz++)
719 argno += (int)(*psz -'0');
723 if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL)
724 lstrcpy (pchOut, cszNULL);
726 lstrcpy (pchOut, apszArgs[argno -1]);
727 pchOut = &pchOut[ lstrlen(pchOut) ];
733 if (apszArgs != NULL)
735 for (argno = 0; argno < nArgs; argno++)
737 if (apszArgs[ argno ] != NULL)
738 FreeString (apszArgs[ argno ]);
743 if (HIWORD(pszSource) == 0) // Did we allocate {pszTemplate}?
745 if (pszTemplate != NULL)
747 FreeString (pszTemplate);
755 /*** FormatSockAddr() - formats a SOCKADDR_IN as text
759 void FormatSockAddr (LPTSTR pszTarget, LPTSTR pszFmt, SOCKADDR_IN *paddr)
761 TCHAR szText[ cchRESOURCE ];
763 LPSTR pszSockAddrA = inet_ntoa (*(struct in_addr *)&paddr->sin_addr.s_addr);
764 LPTSTR pszSockAddr = AnsiToString (pszSockAddrA);
766 lstrcpy (szText, pszSockAddr);
768 FreeString (pszSockAddr, pszSockAddrA);
770 wsprintf (pszTarget, FixFormatString (pszFmt), szText);
774 /*** FormatElapsed() - formats a SYSTEMTIME* as HH:MM:SS
778 BOOL FormatElapsed (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst)
780 TCHAR szTimeSep[ cchRESOURCE ];
781 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_STIME, szTimeSep, cchRESOURCE))
782 lstrcpy (szTimeSep, TEXT(":"));
784 TCHAR szElapsed[ cchRESOURCE ];
785 wsprintf (szElapsed, TEXT("%02lu%s%02lu%s%02lu"),
786 pst->wHour + (pst->wDay * 24),
792 wsprintf (pszTarget, FixFormatString (pszFormatUser), szElapsed);
797 /*** FormatTime() - formats a SYSTEMTIME* according to the current locale
801 BOOL FormatTime (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst, BOOL fShowDate, BOOL fShowTime)
808 SystemTimeToFileTime (pst, &ft);
809 FileTimeToLocalFileTime (&ft, &lft);
810 FileTimeToSystemTime (&lft, <);
812 TCHAR szTime[ cchRESOURCE ];
813 TCHAR szDate[ cchRESOURCE ];
815 if ( ((pst->wYear == 1970) && (pst->wMonth == 1) && (pst->wDay == 1)) ||
816 ((pst->wYear == 0) && (pst->wMonth == 0) && (pst->wDay == 0)) )
818 szTime[0] = TEXT('\0');
823 GetTimeFormat (LOCALE_USER_DEFAULT, 0, <, NULL, szTime, cchRESOURCE);
824 GetDateFormat (LOCALE_USER_DEFAULT, DATE_SHORTDATE, <, NULL, szDate, cchRESOURCE);
826 if (fShowTime && fShowDate)
828 lstrcat (szTime, TEXT(" "));
829 lstrcat (szTime, szDate);
831 else if (fShowDate && !fShowTime)
833 lstrcpy (szTime, szDate);
837 wsprintf (pszTarget, FixFormatString (pszFormatUser), szTime);
842 /*** FormatError() - formats an error code as text
846 LPERRORPROC pfnTranslateError = NULL;
847 void SetErrorTranslationFunction (LPERRORPROC pfn)
849 pfnTranslateError = pfn;
852 BOOL FormatError (LPTSTR pszTarget, LPTSTR pszFmt, DWORD dwError)
854 TCHAR szError[ cchRESOURCE ];
855 BOOL rc = TranslateError (szError, dwError);
857 LPTSTR pchTarget = szError;
858 for (LPTSTR pchSource = szError; *pchSource; ++pchSource)
860 if (*pchSource == TEXT('\n'))
861 *pchTarget++ = TEXT(' ');
862 else if (*pchSource != TEXT('\r'))
863 *pchTarget++ = *pchSource;
865 *pchTarget = TEXT('\0');
867 wsprintf (pszTarget, FixFormatString (pszFmt), szError);
872 BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage)
876 // Guess an appropriate language ID if none was supplied explicitly
880 idLanguage = TaLocale_GetLanguage();
883 // See if the caller supplied a function for us to use.
885 if (pfnTranslateError)
887 rc = (*pfnTranslateError)(pszText, code, idLanguage);
890 // Try to get Windows to translate the thing...
894 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, idLanguage, pszText, cchRESOURCE, NULL))
900 // See if the message is buried in NTDLL...
905 if ((hiNTDLL = LoadLibrary (TEXT("NTDLL.DLL"))) != NULL)
907 if (FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, hiNTDLL, code, idLanguage, pszText, cchRESOURCE, NULL))
911 FreeLibrary (hiNTDLL);
919 BOOL TranslateError (LPTSTR pszText, ULONG status)
923 if ((rc = TranslateErrorFunc (pszText, status, 0)) == TRUE)
925 wsprintf (&pszText[ lstrlen(pszText) ], TEXT(" (0x%08lX)"), status);
929 wsprintf (pszText, TEXT("0x%08lX"), status);
936 /*** FormatBytes() - formats L1048576 as "1.0 MB" (etc)
938 * Wherever "%1" appears is where the number will show up (formatted
939 * according to {pszFormat}).
943 void FormatBytes (LPTSTR pszTarget, LPTSTR pszFormatUser, double cb)
945 TCHAR szFormat[ cchRESOURCE ];
946 TCHAR szTemplate[ cchRESOURCE ];
947 TCHAR szJustTheNumber[ cchRESOURCE ];
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".
957 if (*pszFormatUser != TEXT('%'))
959 lstrcpy (szFormat, TEXT("%.2lf")); // sigh--totally bogus format string.
963 lstrcpy (szFormat, pszFormatUser);
965 for (LPTSTR pch = &szFormat[1]; *pch; ++pch)
967 if (!isfmtgarbage(*pch))
969 lstrcpy (pch, TEXT("lf"));
975 lstrcat (pch, TEXT("lf"));
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.
984 FormatDouble (szJustTheNumber, szFormat, cb/1048576.0);
985 lstrcpy (szTemplate, TEXT("%1 MB"));
988 // Cheesy bit: if all we have are 0's and "."'s, just
989 // make the number read "0".
992 for (TCHAR *pch = szJustTheNumber; *pch; ++pch)
994 if (*pch != TEXT('0') && *pch != TEXT('.'))
999 lstrcpy (szJustTheNumber, TEXT("0"));
1003 // Not particularly hard. Now let FormatString() generate
1004 // the final output, by tacking on the necessary units.
1007 if ((pszOut = FormatString (szTemplate, TEXT("%s"), szJustTheNumber)) == NULL)
1009 // whoops--out of memory?
1010 // well, just give it back in bytes--we can still do that.
1012 FormatDouble (pszTarget, szFormat, cb);
1016 lstrcpy (pszTarget, pszOut);
1017 FreeString (pszOut);
1022 /*** FormatLargeInt() - formats a large int as "hidword,,lodword"
1026 void FormatLargeInt (LPTSTR pszTarget, LPTSTR pszFormatUser, LARGE_INTEGER *pldw)
1028 // Generate just the number portion of the output;
1029 // that will look like "###,,###"
1031 TCHAR szJustTheNumber[ cchRESOURCE ];
1032 wsprintf (szJustTheNumber, TEXT("%lu,,%lu"), (DWORD)pldw->HighPart, (DWORD)pldw->LowPart);
1034 // Not particularly hard. Now let FormatString() generate
1035 // the final output, by tacking on the necessary units
1038 wsprintf (pszTarget, FixFormatString (pszFormatUser), szJustTheNumber);
1042 /*** FormatDouble() - like wsprintf(), but for one argument (a double)
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"
1051 void FormatDouble (LPTSTR pszTarget, LPTSTR pszFormat, double lfValue)
1053 BOOL fPlusUpFront = FALSE;
1054 BOOL fZeroUpFront = FALSE;
1058 // First, parse the format string: look for "%{zero|plus}{min}.{max}",
1059 // and ignore any other characters.
1061 while (*pszFormat && *pszFormat != TEXT('%'))
1064 while (*pszFormat == TEXT('%') ||
1065 *pszFormat == TEXT('0') ||
1066 *pszFormat == TEXT('+') ||
1067 *pszFormat == TEXT('-') )
1069 if (*pszFormat == TEXT('0'))
1071 fZeroUpFront = TRUE;
1073 else if (*pszFormat == TEXT('+'))
1075 fPlusUpFront = TRUE;
1080 cchMin = _ttol( pszFormat );
1082 while (isdigit(*pszFormat))
1085 if (*pszFormat == TEXT('.'))
1088 cchMax = _ttol( pszFormat );
1091 // Then generate the output string, based on what we just learned
1092 // and what {lfValue} is.
1094 if (lfValue >= 0 && fPlusUpFront)
1096 *pszTarget++ = TEXT('+');
1098 else if (lfValue < 0)
1100 *pszTarget++ = TEXT('-');
1101 lfValue = 0 - lfValue;
1104 if (lfValue >= 1 || fZeroUpFront)
1106 unsigned long ulValue = (unsigned long)lfValue;
1107 wsprintf (pszTarget, TEXT("%lu"), ulValue);
1108 pszTarget = &pszTarget[ lstrlen(pszTarget) ];
1109 lfValue -= (double)ulValue;
1114 unsigned long ulValue;
1115 TCHAR szTemp[ cchRESOURCE ];
1116 LPTSTR pszDecimalPoint = pszTarget;
1117 *pszTarget++ = TEXT('.');
1119 for (int ii = 0; ii < cchMax; ++ii)
1122 *pszTarget++ = TEXT('0');
1124 ulValue = (unsigned long)lfValue;
1126 wsprintf (szTemp, TEXT("%lu"), ulValue);
1127 szTemp[ cchMax ] = 0;
1128 lstrcpy (&pszDecimalPoint[ 1 + cchMax - lstrlen(szTemp) ], szTemp);
1130 pszTarget = &pszTarget[ lstrlen(pszTarget) ];
1133 *pszTarget = TEXT('\0');
1137 void lstrupr (LPTSTR psz)
1139 for ( ; psz && *psz; psz = CharNext (psz))
1140 *psz = _ttoupper (*psz);
1144 LPCTSTR lstrchr (LPCTSTR pszTarget, TCHAR ch)
1146 for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget))
1148 if (*pszTarget == ch)
1155 LPCTSTR lstrrchr (LPCTSTR pszTarget, TCHAR ch)
1157 LPCTSTR pszLast = NULL;
1159 for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget))
1161 if (*pszTarget == ch)
1162 pszLast = pszTarget;
1168 int lstrncmpi (LPCTSTR pszA, LPCTSTR pszB, size_t cch)
1172 return (!pszB) - (!pszA); // A,!B:1, !A,B:-1, !A,!B:0
1175 for ( ; cch > 0; cch--, pszA = CharNext(pszA), pszB = CharNext(pszB))
1177 TCHAR chA = _ttoupper( *pszA );
1178 TCHAR chB = _ttoupper( *pszB );
1181 return (!chB) - (!chA); // A,!B:1, !A,B:-1, !A,!B:0
1184 return (int)(chA) - (int)(chB); // -1:A<B, 0:A==B, 1:A>B
1187 return 0; // no differences before told to stop comparing, so A==B
1191 void lstrncpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch)
1194 for (ich = 0; ich < cch; ich++)
1196 if ((pszTarget[ich] = pszSource[ich]) == TEXT('\0'))
1202 void lstrzcpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch)
1204 cch = min(cch, (size_t)(1+lstrlen(pszSource)));
1205 lstrncpy (pszTarget, pszSource, cch-1);
1206 pszTarget[ cch-1 ] = TEXT('\0');
1210 void lsplitpath (LPCTSTR pszSource,
1211 LPTSTR pszDrive, LPTSTR pszPath, LPTSTR pszName, LPTSTR pszExt)
1213 LPCTSTR pszLastSlash = NULL;
1214 LPCTSTR pszLastDot = NULL;
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.
1222 * We assume that the path argument has the following form, where any
1223 * or all of the components may be missing.
1225 * <drive><dir><fname><ext>
1227 * and each of the components has the following expected form(s)
1230 * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
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 -
1237 * \top\next\last\ ; or
1240 * top\next\last\ ; or
1242 * Mixed use of '/' and '\' within a path is also tolerated
1244 * 0 to _MAX_FNAME-1 characters not including the '.' character
1246 * 0 to _MAX_EXT-1 characters where, if any, the first must be a
1251 // extract drive letter and :, if any
1253 if (*(pszSource + _MAX_DRIVE - 2) == TEXT(':'))
1257 lstrncpy (pszDrive, pszSource, _MAX_DRIVE-1);
1258 pszDrive[ _MAX_DRIVE-1 ] = TEXT('\0');
1260 pszSource += _MAX_DRIVE-1;
1264 *pszDrive = TEXT('\0');
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.
1274 for (pch = pszSource; *pch != TEXT('\0'); pch++)
1276 if (*pch == TEXT('/') || *pch == TEXT('\\'))
1278 else if (*pch == TEXT('.'))
1282 // if we found a '\\' or '/', fill in pszPath
1288 cchCopy = min( _MAX_DIR -1, pszLastSlash -pszSource +1 );
1289 lstrncpy (pszPath, pszSource, cchCopy);
1290 pszPath[ cchCopy ] = 0;
1292 pszSource = pszLastSlash +1;
1296 *pszPath = TEXT('\0');
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.
1305 if (pszLastDot && (pszLastDot >= pszSource))
1307 // found the marker for an extension -
1308 // copy the file name up to the '.'.
1312 cchCopy = min( _MAX_DIR-1, pszLastDot -pszSource );
1313 lstrncpy (pszName, pszSource, cchCopy);
1314 pszName[ cchCopy ] = 0;
1317 // now we can get the extension
1321 lstrncpy (pszExt, pszLastDot, _MAX_EXT -1);
1322 pszExt[ _MAX_EXT-1 ] = TEXT('\0');
1327 // found no extension, give empty extension and copy rest of
1328 // string into fname.
1332 lstrncpy (pszName, pszSource, _MAX_FNAME -1);
1333 pszName[ _MAX_FNAME -1 ] = TEXT('\0');
1338 *pszExt = TEXT('\0');
1344 LPCTSTR FindExtension (LPCTSTR name)
1348 if ((pch = lstrchr (FindBaseFileName(name), TEXT('.'))) != NULL)
1351 return &name[ lstrlen(name) ];
1355 LPCTSTR FindBaseFileName (LPCTSTR name)
1359 if ((pszBase = lstrrchr (name, TEXT('\\'))) != NULL)
1360 return CharNext(pszBase);
1366 void ChangeExtension (LPTSTR pszOut, LPCTSTR pszIn, LPCTSTR pszExt, BOOL fForce)
1370 if (pszOut != pszIn)
1371 lstrcpy (pszOut, pszIn);
1373 if ( (*(pch = FindExtension (pszOut)) != TEXT('\0')) && (!fForce) )
1376 if (pszExt[0] == TEXT('.') || pszExt[0] == TEXT('\0'))
1378 lstrcpy ((LPTSTR)pch, pszExt);
1382 lstrcpy ((LPTSTR)pch, TEXT("."));
1383 lstrcat ((LPTSTR)pch, pszExt);
1388 void CopyBaseFileName (LPTSTR pszTarget, LPCTSTR pszSource)
1390 lstrcpy (pszTarget, FindBaseFileName (pszSource));
1393 if ((pszExt = (LPTSTR)FindExtension(pszTarget)) != NULL)
1394 *pszExt = TEXT('\0');
1399 * ANSI/UNICODE _______________________________________________________________
1403 void CopyUnicodeToAnsi (LPSTR pszTargetA, LPCWSTR pszOriginalW, size_t cchMax)
1405 static size_t cchBufferW = 0;
1406 static LPWSTR pszBufferW = NULL;
1408 size_t cchSource = min( cchMax, (size_t)lstrlenW(pszOriginalW)+1 );
1409 if (cchSource > cchBufferW)
1412 FreeString (pszBufferW);
1413 pszBufferW = AllocateUnicode (cchSource);
1414 cchBufferW = cchSource;
1417 memcpy ((LPBYTE)pszBufferW, (LPBYTE)pszOriginalW, cchSource * sizeof(WCHAR));
1418 pszBufferW[ cchSource-1 ] = L'\0';
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;
1426 void CopyAnsiToUnicode (LPWSTR pszTargetW, LPCSTR pszOriginalA, size_t cchMax)
1428 static size_t cchBufferA = 0;
1429 static LPSTR pszBufferA = NULL;
1431 cchMax = min( cchMax, (size_t)lstrlenA(pszOriginalA)+1 );
1432 if (cchMax > cchBufferA)
1435 FreeString (pszBufferA);
1436 pszBufferA = AllocateAnsi (cchMax);
1437 cchBufferA = cchMax;
1440 memcpy ((LPBYTE)pszBufferA, (LPBYTE)pszOriginalA, cchMax);
1441 pszBufferA[ cchMax-1 ] = L'\0';
1443 wsprintfW (pszTargetW, L"%hs", pszBufferA);
1446 void CopyAnsiToString (LPTSTR pszTarget, LPCSTR pszOriginalA, size_t cchMax)
1449 CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA, cchMax);
1451 lstrzcpy ((LPSTR)pszTarget, pszOriginalA, cchMax);
1455 void CopyUnicodeToString (LPTSTR pszTarget, LPCWSTR pszOriginalW, size_t cchMax)
1458 lstrzcpy ((LPWSTR)pszTarget, pszOriginalW, cchMax);
1460 CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW, cchMax);
1464 void CopyStringToUnicode (LPWSTR pszTargetW, LPCTSTR pszOriginal, size_t cchMax)
1467 lstrzcpy (pszTargetW, (LPWSTR)pszOriginal, cchMax);
1469 CopyAnsiToUnicode (pszTargetW, (LPSTR)pszOriginal, cchMax);
1473 void CopyStringToAnsi (LPSTR pszTargetA, LPCTSTR pszOriginal, size_t cchMax)
1476 CopyUnicodeToAnsi (pszTargetA, (LPWSTR)pszOriginal, cchMax);
1478 lstrzcpy (pszTargetA, (LPSTR)pszOriginal, cchMax);
1483 void FreeString (LPCVOID pszString, LPCVOID pszOriginalString)
1485 if ( (pszString != NULL) && (pszString != pszOriginalString) )
1487 Free ((PVOID)pszString);
1492 LPSTR StringToAnsi (LPCTSTR pszOriginal)
1497 int len = lstrlen(pszOriginal);
1498 if ((pszTargetA = AllocateAnsi (1+len)) != NULL) {
1500 CopyUnicodeToAnsi (pszTargetA, pszOriginal);
1502 lstrcpy (pszTargetA, (LPSTR)pszOriginal);
1508 LPTSTR AnsiToString (LPCSTR pszOriginalA)
1513 int lenA = lstrlenA(pszOriginalA);
1514 if ((pszTarget = AllocateString (1+lenA)) != NULL) {
1516 CopyAnsiToUnicode (pszTarget, pszOriginalA);
1518 lstrcpy (pszTargetA, (LPSTR)pszOriginal);
1526 LPWSTR StringToUnicode (LPCTSTR pszOriginal)
1531 int len = lstrlen(pszOriginal);
1532 if ((pszTargetW = AllocateUnicode (1+len)) != NULL) {
1534 CopyAnsiToUnicode (pszTargetW, pszOriginal);
1536 lstrcpy ((LPSTR)pszTargetW, (LPSTR)pszOriginal);
1543 LPTSTR UnicodeToString (LPCWSTR pszOriginalW)
1548 if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL) {
1550 CopyUnicodeToAnsi (pszTarget, pszOriginalW);
1552 lstrcpyA ((LPSTR)pszTarget, pszOriginalW);
1560 LPWSTR AnsiToUnicode (LPCSTR pszOriginalA)
1565 if ((pszTargetW = AllocateUnicode (1+lstrlenA(pszOriginalA))) != NULL)
1566 CopyAnsiToUnicode (pszTargetW, pszOriginalA);
1570 LPSTR UnicodeToAnsi (LPCWSTR pszOriginalW)
1575 if ((pszTargetA = AllocateAnsi (1+lstrlenW(pszOriginalW))) != NULL)
1576 CopyUnicodeToAnsi (pszTargetA, pszOriginalW);
1581 LPTSTR CloneAnsi (LPSTR pszOriginalA)
1588 if ((pszTarget = AllocateString (1+lstrlenA(pszOriginalA))) != NULL)
1591 CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA);
1593 lstrcpyA ((LPSTR)pszTarget, pszOriginalA);
1601 LPTSTR CloneUnicode (LPWSTR pszOriginalW)
1608 if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL)
1611 lstrcpyW ((LPWSTR)pszTarget, pszOriginalW);
1613 CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW);
1621 LPTSTR CloneMultiString (LPCTSTR mszOriginal)
1626 size_t cchOld = 1; // for the second NUL-terminator
1627 for (LPCTSTR psz = mszOriginal; psz && *psz; psz += 1+lstrlen(psz))
1628 cchOld += lstrlen(psz) +1;
1631 if ((pszTarget = AllocateString (cchOld)) != NULL)
1632 memcpy (pszTarget, mszOriginal, cchOld);
1638 LPTSTR CloneString (LPTSTR pszOriginal)
1644 if ((pszTarget = AllocateString (1+lstrlen(pszOriginal))) != NULL)
1645 lstrcpy (pszTarget, pszOriginal);
1651 LPTSTR FixFormatString (LPTSTR pszFormat)
1653 static TCHAR szFormat[ 256 ];
1654 lstrcpy (szFormat, TEXT("%s"));
1656 if (*pszFormat == TEXT('%'))
1658 lstrcpy (szFormat, pszFormat);
1660 for (LPTSTR pch = &szFormat[1]; *pch; ++pch)
1662 if (!isfmtgarbage(*pch))
1664 lstrcpy (pch, TEXT("s"));
1670 lstrcat (pch, TEXT("s"));