7 #include <WINNT/talocale.h>
12 * DEFINITIONS ________________________________________________________________
20 #define _MAX_FNAME 260
23 #define _ttoupper(_ch) (TCHAR)CharUpper((LPTSTR)_ch)
25 #define isfmtgarbage(_ch) (isdigit(_ch) || (_ch==TEXT('.')) || (_ch==TEXT(',')) || (_ch==TEXT('-')))
29 * PROTOTYPES _________________________________________________________________
33 LPTSTR cdecl vFormatString (LONG, LPCTSTR, va_list);
35 BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage);
36 BOOL TranslateError (LPTSTR pszText, ULONG status);
38 LPTSTR FixFormatString (LPTSTR pszFormat);
42 * ROUTINES ___________________________________________________________________
47 /*** GetString - Loads one or more consecutive strings from a resource file
52 void GetString (LPTSTR psz, int ids, int cchMax)
58 LPCSTRINGTEMPLATE pst;
59 if ((pst = TaLocale_GetStringResource (ids)) == NULL)
62 CopyUnicodeToString (psz, (LPWSTR)pst->achString, min(cchMax,pst->cchString+1));
63 if (psz[ lstrlen(psz)-1 ] != TEXT('+'))
65 psz[ lstrlen(psz)-1 ] = TEXT('\0');
67 cchMax -= lstrlen(psz);
74 /*** GetStringLength() - Returns the number of chars in a string resource
78 size_t GetStringLength (int ids)
84 LPCSTRINGTEMPLATE pst;
85 if ((pst = TaLocale_GetStringResource (ids)) == NULL)
88 char szAnsi[ cchRESOURCE * 2 + 1 ];
89 CopyUnicodeToAnsi (szAnsi, pst->achString, pst->cchString+1);
90 cch += 1+ lstrlenA(szAnsi);
92 if ((!pst->cchString) || (pst->achString[ pst->cchString-1 ] != L'+'))
103 /*** SearchMultiString() - scans multistrings for a given string
107 BOOL SearchMultiString (LPCTSTR pmsz, LPCTSTR pszString, BOOL fCaseSensitive)
111 for (LPCTSTR psz = pmsz; *psz; psz += 1+lstrlen(psz))
113 if (!*pszString && !lstrlen(psz))
115 else if (pszString && fCaseSensitive && !lstrcmp (psz, pszString))
117 else if (pszString && !fCaseSensitive && !lstrcmpi (psz, pszString))
126 /*** FormatMultiString() - manipulates multistrings ("EXA\0MP\0LE\0\0")
130 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LONG ids, LPCTSTR pszFormat, va_list arg)
132 static const TCHAR szMultiStringNULL[] = cszMultiStringNULL;
133 LPTSTR pszNew = vFormatString (ids, pszFormat, arg);
135 if (!pszNew || !*pszNew)
138 pszNew = (LPTSTR)szMultiStringNULL;
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;
146 if ((pszFinal = AllocateString (cchOld +lstrlen(pszNew) +1)) != NULL)
150 lstrcpy (pszFinal, pszNew);
154 lstrcpy (pszFinal, pszNew);
155 memcpy (&pszFinal[ lstrlen(pszNew)+1 ], *ppszTarget, sizeof(TCHAR) * cchOld);
157 else // (*ppszTarget && !fAddHead)
159 memcpy (pszFinal, *ppszTarget, sizeof(TCHAR) * cchOld);
160 lstrcpy (&pszFinal[ cchOld-1 ], pszNew);
162 pszFinal[ cchOld +lstrlen(pszNew) ] = TEXT('\0');
164 FreeString (*ppszTarget);
165 *ppszTarget = pszFinal;
168 if (pszNew != (LPTSTR)szMultiStringNULL)
173 void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, ...)
176 if (pszFormat != NULL)
177 va_start (arg, pszFormat);
178 vFormatMultiString (ppszTarget, fAddHead, (LONG)pszTemplate, pszFormat, arg);
181 void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, ...)
184 if (pszFormat != NULL)
185 va_start (arg, pszFormat);
186 vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg);
189 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, va_list arg)
191 vFormatMultiString (ppszTarget, fAddHead, (LONG)pszTemplate, pszFormat, arg);
194 void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, va_list arg)
196 vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg);
200 /*** FormatString() - replacable-parameter version of wsprintf()
204 LPTSTR cdecl FormatString (LPCTSTR psz, LPCTSTR pszFmt, ...)
208 va_start (arg, pszFmt);
209 return vFormatString ((LONG)psz, pszFmt, arg);
212 LPTSTR cdecl FormatString (int ids, LPCTSTR pszFmt, ...)
216 va_start (arg, pszFmt);
217 return vFormatString ((LONG)ids, pszFmt, arg);
220 LPTSTR cdecl vFormatString (LPCTSTR psz, LPCTSTR pszFmt, va_list arg)
222 return vFormatString ((LONG)psz, pszFmt, arg);
225 LPTSTR cdecl vFormatString (int ids, LPCTSTR pszFmt, va_list arg)
227 return vFormatString ((LONG)ids, pszFmt, arg);
231 typedef enum // vartype
251 #define vtSTRING vtSTRINGW // %s gives vtSTRING
253 #define vtSTRING vtSTRINGA
256 #define cchMaxNUMBER 40
257 #define cszNULL TEXT("(null)")
259 LPTSTR cdecl vFormatString (LONG pszSource, LPCTSTR pszFmt, va_list arg)
263 LPTSTR pszOut = NULL;
269 TCHAR szFmt[ cchRESOURCE ];
274 if (HIWORD(pszSource) != 0) // It's a string
276 pszTemplate = (LPTSTR)pszSource;
278 else // It's a message
280 cch = GetStringLength ((int)pszSource);
282 if ((pszTemplate = AllocateString (1+cch)) == NULL)
285 GetString (pszTemplate, (int)pszSource, cch);
289 // First off, count the number of arguments given in {pszFmt}.
296 for (psz = (LPTSTR)pszFmt; *psz; psz++)
298 if (*psz == TEXT('%'))
304 // If there are no arguments, then we're just expanding {pszSource}.
309 if (HIWORD(pszSource) == 0) // It's a message
313 else // (HIWORD(pszSource) != 0) // It's a string
315 psz = CloneString (pszTemplate);
321 // Otherwise, allocate an array of LPTSTR's, one for each
325 apszArgs = (LPTSTR*)Allocate (sizeof(LPTSTR) * nArgs);
327 if (apszArgs == NULL)
329 if (pszSource != (LONG)pszTemplate)
330 FreeString (pszTemplate);
334 memset (apszArgs, 0x00, sizeof(LPTSTR) * nArgs);
337 // Extract each argument in turn, using {pszFmt} and {arg}.
340 for (argno = 0; pszFmt && *pszFmt; argno++)
350 LARGE_INTEGER *arg_ldw;
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...
359 if ((*pszFmt != TEXT('%')) || (*(1+pszFmt) == TEXT('\0')))
362 lstrcpy (szFmt, pszFmt);
363 if ((psz = (LPTSTR)lstrchr (&szFmt[1], TEXT('%'))) != NULL)
366 for (int ii = 1; szFmt[ii] == TEXT('-') || szFmt[ii] == TEXT(','); ii++)
368 cchMin = _ttol (&szFmt[ii]);
370 for (pszFmt++; *pszFmt && isfmtgarbage(*pszFmt); ++pszFmt)
372 if (*pszFmt == TEXT('%'))
376 if (!pszFmt || !*pszFmt)
378 if (*pszFmt == TEXT('%'))
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 "%##.##"
386 // Step through the format string's definition ("c", "lu", etc)
387 // to determine what kind of argument this one is, setting {vt}
391 for (vt = vtINVALID; *pszFmt && *pszFmt != TEXT('%'); pszFmt++)
415 case TEXT('i'): // "%i"==int, "%li"==large_integer
433 else if (vt == vtWORD)
441 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
444 case TEXT('B'): // %B = double cb
446 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
449 case TEXT('b'): // %b = ULONG cb
451 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
455 if (vt == vtERROR) // %et == systemtime* (shown as elapsed)
457 else // (vt != vtERROR) // %t == systemtime* (shown as elapsed)
461 case TEXT('e'): // %e == error
465 case TEXT('a'): // %a = LPSOCKADDR_IN
467 *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s
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.
484 arg_d = (LONG)(va_arg (arg, short));
489 arg_d = va_arg (arg, LONG);
494 arg_f = (double)( va_arg (arg, float) );
499 arg_f = va_arg (arg, double);
505 LPSTR arg_pszA = va_arg (arg, LPSTR);
507 if (arg_pszA == NULL)
508 arg_psz = CloneString (cszNULL);
510 arg_psz = CloneAnsi (arg_pszA);
512 cch = lstrlen(arg_psz);
518 LPWSTR arg_pszW = va_arg (arg, LPWSTR);
520 if (arg_pszW == NULL)
521 arg_psz = CloneString (cszNULL);
523 arg_psz = CloneUnicode (arg_pszW);
525 cch = lstrlen(arg_psz);
531 int arg_ids = va_arg (arg, int);
533 if ((cch = GetStringLength (arg_ids)) == 0)
534 arg_psz = CloneString (cszNULL);
537 if ((arg_psz = AllocateString (cch)) == NULL)
539 GetString (arg_psz, arg_ids, cch);
542 cch = lstrlen(arg_psz);
547 arg_f = va_arg (arg, double);
552 arg_f = (double)va_arg (arg, LONG);
558 arg_t = va_arg (arg, SYSTEMTIME*);
563 arg_d = va_arg (arg, DWORD);
568 arg_a = va_arg (arg, SOCKADDR_IN*);
573 arg_ldw = va_arg (arg, LARGE_INTEGER*);
574 cch = cchMaxNUMBER * 2;
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
586 cch = max( cch, cchMin );
588 if ((apszArgs[ argno ] = AllocateString (cch+1)) == NULL)
594 wsprintf (apszArgs[ argno ], szFmt, (short)arg_d);
598 wsprintf (apszArgs[ argno ], szFmt, arg_d);
602 FormatDouble (apszArgs[ argno ], szFmt, arg_f);
606 FormatDouble (apszArgs[ argno ], szFmt, arg_f);
612 wsprintf (apszArgs[ argno ], szFmt, arg_psz);
613 FreeString (arg_psz);
618 FormatBytes (apszArgs[ argno ], szFmt, arg_f);
622 FormatElapsed (apszArgs[ argno ], szFmt, arg_t);
626 FormatTime (apszArgs[ argno ], szFmt, arg_t);
630 FormatError (apszArgs[ argno ], szFmt, arg_d);
634 FormatSockAddr (apszArgs[ argno ], szFmt, arg_a);
638 FormatLargeInt (apszArgs[ argno ], szFmt, arg_ldw);
644 // Determine how big the final string will be once the arguments
645 // are inserted, and allocate space for it.
650 for (psz = pszTemplate; *psz; )
652 if (*psz != TEXT('%'))
659 if (*(++psz) == TEXT('\0'))
669 for (argno = 0; *psz && isdigit (*psz); psz++)
672 argno += (int)(*psz -'0');
676 if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL)
677 cch += lstrlen (cszNULL);
679 cch += lstrlen (apszArgs[argno -1]);
682 if ((pszOut = AllocateString (cch+1)) == NULL)
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.
691 for (psz = pszTemplate, pchOut = pszOut; *psz; )
693 if (*psz != TEXT('%'))
699 if (*(++psz) == TEXT('\0'))
708 for (argno = 0; *psz && isdigit (*psz); psz++)
711 argno += (int)(*psz -'0');
715 if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL)
716 lstrcpy (pchOut, cszNULL);
718 lstrcpy (pchOut, apszArgs[argno -1]);
719 pchOut = &pchOut[ lstrlen(pchOut) ];
725 if (apszArgs != NULL)
727 for (argno = 0; argno < nArgs; argno++)
729 if (apszArgs[ argno ] != NULL)
730 FreeString (apszArgs[ argno ]);
735 if (HIWORD(pszSource) == 0) // Did we allocate {pszTemplate}?
737 if (pszTemplate != NULL)
739 FreeString (pszTemplate);
747 /*** FormatSockAddr() - formats a SOCKADDR_IN as text
751 void FormatSockAddr (LPTSTR pszTarget, LPTSTR pszFmt, SOCKADDR_IN *paddr)
753 TCHAR szText[ cchRESOURCE ];
755 LPSTR pszSockAddrA = inet_ntoa (*(struct in_addr *)&paddr->sin_addr.s_addr);
756 LPTSTR pszSockAddr = AnsiToString (pszSockAddrA);
758 lstrcpy (szText, pszSockAddr);
760 FreeString (pszSockAddr, pszSockAddrA);
762 wsprintf (pszTarget, FixFormatString (pszFmt), szText);
766 /*** FormatElapsed() - formats a SYSTEMTIME* as HH:MM:SS
770 BOOL FormatElapsed (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst)
772 TCHAR szTimeSep[ cchRESOURCE ];
773 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_STIME, szTimeSep, cchRESOURCE))
774 lstrcpy (szTimeSep, TEXT(":"));
776 TCHAR szElapsed[ cchRESOURCE ];
777 wsprintf (szElapsed, TEXT("%02lu%s%02lu%s%02lu"),
778 pst->wHour + (pst->wDay * 24),
784 wsprintf (pszTarget, FixFormatString (pszFormatUser), szElapsed);
789 /*** FormatTime() - formats a SYSTEMTIME* according to the current locale
793 BOOL FormatTime (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst, BOOL fShowDate, BOOL fShowTime)
800 SystemTimeToFileTime (pst, &ft);
801 FileTimeToLocalFileTime (&ft, &lft);
802 FileTimeToSystemTime (&lft, <);
804 TCHAR szTime[ cchRESOURCE ];
805 TCHAR szDate[ cchRESOURCE ];
807 if ( ((pst->wYear == 1970) && (pst->wMonth == 1) && (pst->wDay == 1)) ||
808 ((pst->wYear == 0) && (pst->wMonth == 0) && (pst->wDay == 0)) )
810 szTime[0] = TEXT('\0');
815 GetTimeFormat (LOCALE_USER_DEFAULT, 0, <, NULL, szTime, cchRESOURCE);
816 GetDateFormat (LOCALE_USER_DEFAULT, DATE_SHORTDATE, <, NULL, szDate, cchRESOURCE);
818 if (fShowTime && fShowDate)
820 lstrcat (szTime, TEXT(" "));
821 lstrcat (szTime, szDate);
823 else if (fShowDate && !fShowTime)
825 lstrcpy (szTime, szDate);
829 wsprintf (pszTarget, FixFormatString (pszFormatUser), szTime);
834 /*** FormatError() - formats an error code as text
838 LPERRORPROC pfnTranslateError = NULL;
839 void SetErrorTranslationFunction (LPERRORPROC pfn)
841 pfnTranslateError = pfn;
844 BOOL FormatError (LPTSTR pszTarget, LPTSTR pszFmt, DWORD dwError)
846 TCHAR szError[ cchRESOURCE ];
847 BOOL rc = TranslateError (szError, dwError);
849 LPTSTR pchTarget = szError;
850 for (LPTSTR pchSource = szError; *pchSource; ++pchSource)
852 if (*pchSource == TEXT('\n'))
853 *pchTarget++ = TEXT(' ');
854 else if (*pchSource != TEXT('\r'))
855 *pchTarget++ = *pchSource;
857 *pchTarget = TEXT('\0');
859 wsprintf (pszTarget, FixFormatString (pszFmt), szError);
864 BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage)
868 // Guess an appropriate language ID if none was supplied explicitly
872 idLanguage = TaLocale_GetLanguage();
875 // See if the caller supplied a function for us to use.
877 if (pfnTranslateError)
879 rc = (*pfnTranslateError)(pszText, code, idLanguage);
882 // Try to get Windows to translate the thing...
886 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, idLanguage, pszText, cchRESOURCE, NULL))
892 // See if the message is buried in NTDLL...
897 if ((hiNTDLL = LoadLibrary (TEXT("NTDLL.DLL"))) != NULL)
899 if (FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, hiNTDLL, code, idLanguage, pszText, cchRESOURCE, NULL))
903 FreeLibrary (hiNTDLL);
911 BOOL TranslateError (LPTSTR pszText, ULONG status)
915 if ((rc = TranslateErrorFunc (pszText, status, 0)) == TRUE)
917 wsprintf (&pszText[ lstrlen(pszText) ], TEXT(" (0x%08lX)"), status);
921 wsprintf (pszText, TEXT("0x%08lX"), status);
928 /*** FormatBytes() - formats L1048576 as "1.0 MB" (etc)
930 * Wherever "%1" appears is where the number will show up (formatted
931 * according to {pszFormat}).
935 void FormatBytes (LPTSTR pszTarget, LPTSTR pszFormatUser, double cb)
937 TCHAR szFormat[ cchRESOURCE ];
938 TCHAR szTemplate[ cchRESOURCE ];
939 TCHAR szJustTheNumber[ cchRESOURCE ];
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".
949 if (*pszFormatUser != TEXT('%'))
951 lstrcpy (szFormat, TEXT("%.2lf")); // sigh--totally bogus format string.
955 lstrcpy (szFormat, pszFormatUser);
957 for (LPTSTR pch = &szFormat[1]; *pch; ++pch)
959 if (!isfmtgarbage(*pch))
961 lstrcpy (pch, TEXT("lf"));
967 lstrcat (pch, TEXT("lf"));
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.
976 FormatDouble (szJustTheNumber, szFormat, cb/1048576.0);
977 lstrcpy (szTemplate, TEXT("%1 MB"));
980 // Cheesy bit: if all we have are 0's and "."'s, just
981 // make the number read "0".
984 for (TCHAR *pch = szJustTheNumber; *pch; ++pch)
986 if (*pch != TEXT('0') && *pch != TEXT('.'))
991 lstrcpy (szJustTheNumber, TEXT("0"));
995 // Not particularly hard. Now let FormatString() generate
996 // the final output, by tacking on the necessary units.
999 if ((pszOut = FormatString (szTemplate, TEXT("%s"), szJustTheNumber)) == NULL)
1001 // whoops--out of memory?
1002 // well, just give it back in bytes--we can still do that.
1004 FormatDouble (pszTarget, szFormat, cb);
1008 lstrcpy (pszTarget, pszOut);
1009 FreeString (pszOut);
1014 /*** FormatLargeInt() - formats a large int as "hidword,,lodword"
1018 void FormatLargeInt (LPTSTR pszTarget, LPTSTR pszFormatUser, LARGE_INTEGER *pldw)
1020 // Generate just the number portion of the output;
1021 // that will look like "###,,###"
1023 TCHAR szJustTheNumber[ cchRESOURCE ];
1024 wsprintf (szJustTheNumber, TEXT("%lu,,%lu"), (DWORD)pldw->HighPart, (DWORD)pldw->LowPart);
1026 // Not particularly hard. Now let FormatString() generate
1027 // the final output, by tacking on the necessary units
1030 wsprintf (pszTarget, FixFormatString (pszFormatUser), szJustTheNumber);
1034 /*** FormatDouble() - like wsprintf(), but for one argument (a double)
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"
1043 void FormatDouble (LPTSTR pszTarget, LPTSTR pszFormat, double lfValue)
1045 BOOL fPlusUpFront = FALSE;
1046 BOOL fZeroUpFront = FALSE;
1050 // First, parse the format string: look for "%{zero|plus}{min}.{max}",
1051 // and ignore any other characters.
1053 while (*pszFormat && *pszFormat != TEXT('%'))
1056 while (*pszFormat == TEXT('%') ||
1057 *pszFormat == TEXT('0') ||
1058 *pszFormat == TEXT('+') ||
1059 *pszFormat == TEXT('-') )
1061 if (*pszFormat == TEXT('0'))
1063 fZeroUpFront = TRUE;
1065 else if (*pszFormat == TEXT('+'))
1067 fPlusUpFront = TRUE;
1072 cchMin = _ttol( pszFormat );
1074 while (isdigit(*pszFormat))
1077 if (*pszFormat == TEXT('.'))
1080 cchMax = _ttol( pszFormat );
1083 // Then generate the output string, based on what we just learned
1084 // and what {lfValue} is.
1086 if (lfValue >= 0 && fPlusUpFront)
1088 *pszTarget++ = TEXT('+');
1090 else if (lfValue < 0)
1092 *pszTarget++ = TEXT('-');
1093 lfValue = 0 - lfValue;
1096 if (lfValue >= 1 || fZeroUpFront)
1098 unsigned long ulValue = (unsigned long)lfValue;
1099 wsprintf (pszTarget, TEXT("%lu"), ulValue);
1100 pszTarget = &pszTarget[ lstrlen(pszTarget) ];
1101 lfValue -= (double)ulValue;
1106 unsigned long ulValue;
1107 TCHAR szTemp[ cchRESOURCE ];
1108 LPTSTR pszDecimalPoint = pszTarget;
1109 *pszTarget++ = TEXT('.');
1111 for (int ii = 0; ii < cchMax; ++ii)
1114 *pszTarget++ = TEXT('0');
1116 ulValue = (unsigned long)lfValue;
1118 wsprintf (szTemp, TEXT("%lu"), ulValue);
1119 szTemp[ cchMax ] = 0;
1120 lstrcpy (&pszDecimalPoint[ 1 + cchMax - lstrlen(szTemp) ], szTemp);
1122 pszTarget = &pszTarget[ lstrlen(pszTarget) ];
1125 *pszTarget = TEXT('\0');
1129 void lstrupr (LPTSTR psz)
1131 for ( ; psz && *psz; psz = CharNext (psz))
1132 *psz = _ttoupper (*psz);
1136 LPCTSTR lstrchr (LPCTSTR pszTarget, TCHAR ch)
1138 for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget))
1140 if (*pszTarget == ch)
1147 LPCTSTR lstrrchr (LPCTSTR pszTarget, TCHAR ch)
1149 LPCTSTR pszLast = NULL;
1151 for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget))
1153 if (*pszTarget == ch)
1154 pszLast = pszTarget;
1160 int lstrncmpi (LPCTSTR pszA, LPCTSTR pszB, size_t cch)
1164 return (!pszB) - (!pszA); // A,!B:1, !A,B:-1, !A,!B:0
1167 for ( ; cch > 0; cch--, pszA = CharNext(pszA), pszB = CharNext(pszB))
1169 TCHAR chA = _ttoupper( *pszA );
1170 TCHAR chB = _ttoupper( *pszB );
1173 return (!chB) - (!chA); // A,!B:1, !A,B:-1, !A,!B:0
1176 return (int)(chA) - (int)(chB); // -1:A<B, 0:A==B, 1:A>B
1179 return 0; // no differences before told to stop comparing, so A==B
1183 void lstrncpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch)
1186 for (ich = 0; ich < cch; ich++)
1188 if ((pszTarget[ich] = pszSource[ich]) == TEXT('\0'))
1194 void lstrzcpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch)
1196 cch = min(cch, (size_t)(1+lstrlen(pszSource)));
1197 lstrncpy (pszTarget, pszSource, cch-1);
1198 pszTarget[ cch-1 ] = TEXT('\0');
1202 void lsplitpath (LPCTSTR pszSource,
1203 LPTSTR pszDrive, LPTSTR pszPath, LPTSTR pszName, LPTSTR pszExt)
1205 LPCTSTR pszLastSlash = NULL;
1206 LPCTSTR pszLastDot = NULL;
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.
1214 * We assume that the path argument has the following form, where any
1215 * or all of the components may be missing.
1217 * <drive><dir><fname><ext>
1219 * and each of the components has the following expected form(s)
1222 * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
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 -
1229 * \top\next\last\ ; or
1232 * top\next\last\ ; or
1234 * Mixed use of '/' and '\' within a path is also tolerated
1236 * 0 to _MAX_FNAME-1 characters not including the '.' character
1238 * 0 to _MAX_EXT-1 characters where, if any, the first must be a
1243 // extract drive letter and :, if any
1245 if (*(pszSource + _MAX_DRIVE - 2) == TEXT(':'))
1249 lstrncpy (pszDrive, pszSource, _MAX_DRIVE-1);
1250 pszDrive[ _MAX_DRIVE-1 ] = TEXT('\0');
1252 pszSource += _MAX_DRIVE-1;
1256 *pszDrive = TEXT('\0');
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.
1266 for (pch = pszSource; *pch != TEXT('\0'); pch++)
1268 if (*pch == TEXT('/') || *pch == TEXT('\\'))
1270 else if (*pch == TEXT('.'))
1274 // if we found a '\\' or '/', fill in pszPath
1280 cchCopy = min( _MAX_DIR -1, pszLastSlash -pszSource +1 );
1281 lstrncpy (pszPath, pszSource, cchCopy);
1282 pszPath[ cchCopy ] = 0;
1284 pszSource = pszLastSlash +1;
1288 *pszPath = TEXT('\0');
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.
1297 if (pszLastDot && (pszLastDot >= pszSource))
1299 // found the marker for an extension -
1300 // copy the file name up to the '.'.
1304 cchCopy = min( _MAX_DIR-1, pszLastDot -pszSource );
1305 lstrncpy (pszName, pszSource, cchCopy);
1306 pszName[ cchCopy ] = 0;
1309 // now we can get the extension
1313 lstrncpy (pszExt, pszLastDot, _MAX_EXT -1);
1314 pszExt[ _MAX_EXT-1 ] = TEXT('\0');
1319 // found no extension, give empty extension and copy rest of
1320 // string into fname.
1324 lstrncpy (pszName, pszSource, _MAX_FNAME -1);
1325 pszName[ _MAX_FNAME -1 ] = TEXT('\0');
1330 *pszExt = TEXT('\0');
1336 LPCTSTR FindExtension (LPCTSTR name)
1340 if ((pch = lstrchr (FindBaseFileName(name), TEXT('.'))) != NULL)
1343 return &name[ lstrlen(name) ];
1347 LPCTSTR FindBaseFileName (LPCTSTR name)
1351 if ((pszBase = lstrrchr (name, TEXT('\\'))) != NULL)
1352 return CharNext(pszBase);
1358 void ChangeExtension (LPTSTR pszOut, LPCTSTR pszIn, LPCTSTR pszExt, BOOL fForce)
1362 if (pszOut != pszIn)
1363 lstrcpy (pszOut, pszIn);
1365 if ( (*(pch = FindExtension (pszOut)) != TEXT('\0')) && (!fForce) )
1368 if (pszExt[0] == TEXT('.') || pszExt[0] == TEXT('\0'))
1370 lstrcpy ((LPTSTR)pch, pszExt);
1374 lstrcpy ((LPTSTR)pch, TEXT("."));
1375 lstrcat ((LPTSTR)pch, pszExt);
1380 void CopyBaseFileName (LPTSTR pszTarget, LPCTSTR pszSource)
1382 lstrcpy (pszTarget, FindBaseFileName (pszSource));
1385 if ((pszExt = (LPTSTR)FindExtension(pszTarget)) != NULL)
1386 *pszExt = TEXT('\0');
1391 * ANSI/UNICODE _______________________________________________________________
1395 void CopyUnicodeToAnsi (LPSTR pszTargetA, LPCWSTR pszOriginalW, size_t cchMax)
1397 static size_t cchBufferW = 0;
1398 static LPWSTR pszBufferW = NULL;
1400 size_t cchSource = min( cchMax, (size_t)lstrlenW(pszOriginalW)+1 );
1401 if (cchSource > cchBufferW)
1404 FreeString (pszBufferW);
1405 pszBufferW = AllocateUnicode (cchSource);
1406 cchBufferW = cchSource;
1409 memcpy ((LPBYTE)pszBufferW, (LPBYTE)pszOriginalW, cchSource * sizeof(WCHAR));
1410 pszBufferW[ cchSource-1 ] = L'\0';
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;
1418 void CopyAnsiToUnicode (LPWSTR pszTargetW, LPCSTR pszOriginalA, size_t cchMax)
1420 static size_t cchBufferA = 0;
1421 static LPSTR pszBufferA = NULL;
1423 cchMax = min( cchMax, (size_t)lstrlenA(pszOriginalA)+1 );
1424 if (cchMax > cchBufferA)
1427 FreeString (pszBufferA);
1428 pszBufferA = AllocateAnsi (cchMax);
1429 cchBufferA = cchMax;
1432 memcpy ((LPBYTE)pszBufferA, (LPBYTE)pszOriginalA, cchMax);
1433 pszBufferA[ cchMax-1 ] = L'\0';
1435 wsprintfW (pszTargetW, L"%hs", pszBufferA);
1438 void CopyAnsiToString (LPTSTR pszTarget, LPCSTR pszOriginalA, size_t cchMax)
1441 CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA, cchMax);
1443 lstrzcpy ((LPSTR)pszTarget, pszOriginalA, cchMax);
1447 void CopyUnicodeToString (LPTSTR pszTarget, LPCWSTR pszOriginalW, size_t cchMax)
1450 lstrzcpy ((LPWSTR)pszTarget, pszOriginalW, cchMax);
1452 CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW, cchMax);
1456 void CopyStringToUnicode (LPWSTR pszTargetW, LPCTSTR pszOriginal, size_t cchMax)
1459 lstrzcpy (pszTargetW, (LPWSTR)pszOriginal, cchMax);
1461 CopyAnsiToUnicode (pszTargetW, (LPSTR)pszOriginal, cchMax);
1465 void CopyStringToAnsi (LPSTR pszTargetA, LPCTSTR pszOriginal, size_t cchMax)
1468 CopyUnicodeToAnsi (pszTargetA, (LPWSTR)pszOriginal, cchMax);
1470 lstrzcpy (pszTargetA, (LPSTR)pszOriginal, cchMax);
1475 void FreeString (LPCVOID pszString, LPCVOID pszOriginalString)
1477 if ( (pszString != NULL) && (pszString != pszOriginalString) )
1479 Free ((PVOID)pszString);
1484 LPSTR StringToAnsi (LPCTSTR pszOriginal)
1487 return (LPSTR)pszOriginal;
1492 if ((pszTargetA = AllocateAnsi (1+lstrlen(pszOriginal))) != NULL)
1493 CopyUnicodeToAnsi (pszTargetA, pszOriginal);
1498 LPTSTR AnsiToString (LPCSTR pszOriginalA)
1501 return (LPTSTR)pszOriginalA;
1506 if ((pszTarget = AllocateString (1+lstrlenA(pszOriginalA))) != NULL)
1507 CopyAnsiToUnicode (pszTarget, pszOriginalA);
1513 LPWSTR StringToUnicode (LPCTSTR pszOriginal)
1516 return (LPWSTR)pszOriginal;
1521 if ((pszTargetW = AllocateUnicode (1+lstrlen(pszOriginal))) != NULL)
1522 CopyAnsiToUnicode (pszTargetW, pszOriginal);
1527 LPTSTR UnicodeToString (LPCWSTR pszOriginalW)
1530 return (LPTSTR)pszOriginalW;
1535 if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL)
1536 CopyUnicodeToAnsi (pszTarget, pszOriginalW);
1542 LPWSTR AnsiToUnicode (LPCSTR pszOriginalA)
1547 if ((pszTargetW = AllocateUnicode (1+lstrlenA(pszOriginalA))) != NULL)
1548 CopyAnsiToUnicode (pszTargetW, pszOriginalA);
1552 LPSTR UnicodeToAnsi (LPCWSTR pszOriginalW)
1557 if ((pszTargetA = AllocateAnsi (1+lstrlenW(pszOriginalW))) != NULL)
1558 CopyUnicodeToAnsi (pszTargetA, pszOriginalW);
1563 LPTSTR CloneAnsi (LPSTR pszOriginalA)
1570 if ((pszTarget = AllocateString (1+lstrlenA(pszOriginalA))) != NULL)
1573 CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA);
1575 lstrcpyA ((LPSTR)pszTarget, pszOriginalA);
1583 LPTSTR CloneUnicode (LPWSTR pszOriginalW)
1590 if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL)
1593 lstrcpyW ((LPWSTR)pszTarget, pszOriginalW);
1595 CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW);
1603 LPTSTR CloneMultiString (LPCTSTR mszOriginal)
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;
1613 if ((pszTarget = AllocateString (cchOld)) != NULL)
1614 memcpy (pszTarget, mszOriginal, cchOld);
1620 LPTSTR CloneString (LPTSTR pszOriginal)
1626 if ((pszTarget = AllocateString (1+lstrlen(pszOriginal))) != NULL)
1627 lstrcpy (pszTarget, pszOriginal);
1633 LPTSTR FixFormatString (LPTSTR pszFormat)
1635 static TCHAR szFormat[ 256 ];
1636 lstrcpy (szFormat, TEXT("%s"));
1638 if (*pszFormat == TEXT('%'))
1640 lstrcpy (szFormat, pszFormat);
1642 for (LPTSTR pch = &szFormat[1]; *pch; ++pch)
1644 if (!isfmtgarbage(*pch))
1646 lstrcpy (pch, TEXT("s"));
1652 lstrcat (pch, TEXT("s"));