/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ extern "C" { #include #include } #include #include /* * DEFINITIONS ________________________________________________________________ * */ #ifndef _MAX_DRIVE #define _MAX_DRIVE 3 #define _MAX_DIR 260 #define _MAX_EXT 5 #define _MAX_FNAME 260 #endif #define _ttoupper(_ch) (TCHAR)CharUpper((LPTSTR)_ch) #define isfmtgarbage(_ch) (isdigit(_ch) || (_ch==TEXT('.')) || (_ch==TEXT(',')) || (_ch==TEXT('-'))) /* * PROTOTYPES _________________________________________________________________ * */ LPTSTR cdecl vFormatString (LONG, LPCTSTR, va_list); BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage); BOOL TranslateError (LPTSTR pszText, ULONG status); LPTSTR FixFormatString (LPTSTR pszFormat); /* * ROUTINES ___________________________________________________________________ * */ /*** GetString - Loads one or more consecutive strings from a resource file * */ void GetString (LPTSTR psz, int ids, int cchMax) { *psz = TEXT('\0'); for (;;) { LPCSTRINGTEMPLATE pst; if ((pst = TaLocale_GetStringResource (ids)) == NULL) break; CopyUnicodeToString (psz, (LPWSTR)pst->achString, min(cchMax,pst->cchString+1)); if (psz[ lstrlen(psz)-1 ] != TEXT('+')) break; psz[ lstrlen(psz)-1 ] = TEXT('\0'); cchMax -= lstrlen(psz); psz += lstrlen(psz); ++ids; } } /*** GetStringLength() - Returns the number of chars in a string resource * */ size_t GetStringLength (int ids) { size_t cch = 0; for (;;) { LPCSTRINGTEMPLATE pst; if ((pst = TaLocale_GetStringResource (ids)) == NULL) break; char szAnsi[ cchRESOURCE * 2 + 1 ]; CopyUnicodeToAnsi (szAnsi, pst->achString, pst->cchString+1); cch += 1+ lstrlenA(szAnsi); if ((!pst->cchString) || (pst->achString[ pst->cchString-1 ] != L'+')) break; ++ids; } return cch; } /*** SearchMultiString() - scans multistrings for a given string * */ BOOL SearchMultiString (LPCTSTR pmsz, LPCTSTR pszString, BOOL fCaseSensitive) { if (pmsz) { for (LPCTSTR psz = pmsz; *psz; psz += 1+lstrlen(psz)) { if (!*pszString && !lstrlen(psz)) return TRUE; else if (pszString && fCaseSensitive && !lstrcmp (psz, pszString)) return TRUE; else if (pszString && !fCaseSensitive && !lstrcmpi (psz, pszString)) return TRUE; } } return FALSE; } /*** FormatMultiString() - manipulates multistrings ("EXA\0MP\0LE\0\0") * */ void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LONG ids, LPCTSTR pszFormat, va_list arg) { static const TCHAR szMultiStringNULL[] = cszMultiStringNULL; LPTSTR pszNew = vFormatString (ids, pszFormat, arg); if (!pszNew || !*pszNew) { FreeString (pszNew); pszNew = (LPTSTR)szMultiStringNULL; } size_t cchOld = 1; // for the second NUL-terminator for (LPTSTR psz = *ppszTarget; psz && *psz; psz += 1+lstrlen(psz)) cchOld += lstrlen(psz) +1; LPTSTR pszFinal; if ((pszFinal = AllocateString (cchOld +lstrlen(pszNew) +1)) != NULL) { if (!*ppszTarget) { lstrcpy (pszFinal, pszNew); } else if (fAddHead) { lstrcpy (pszFinal, pszNew); memcpy (&pszFinal[ lstrlen(pszNew)+1 ], *ppszTarget, sizeof(TCHAR) * cchOld); } else // (*ppszTarget && !fAddHead) { memcpy (pszFinal, *ppszTarget, sizeof(TCHAR) * cchOld); lstrcpy (&pszFinal[ cchOld-1 ], pszNew); } pszFinal[ cchOld +lstrlen(pszNew) ] = TEXT('\0'); FreeString (*ppszTarget); *ppszTarget = pszFinal; } if (pszNew != (LPTSTR)szMultiStringNULL) FreeString (pszNew); } void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, ...) { va_list arg; if (pszFormat != NULL) va_start (arg, pszFormat); vFormatMultiString (ppszTarget, fAddHead, (LONG)pszTemplate, pszFormat, arg); } void cdecl FormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, ...) { va_list arg; if (pszFormat != NULL) va_start (arg, pszFormat); vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg); } void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, LPCTSTR pszTemplate, LPCTSTR pszFormat, va_list arg) { vFormatMultiString (ppszTarget, fAddHead, (LONG)pszTemplate, pszFormat, arg); } void cdecl vFormatMultiString (LPTSTR *ppszTarget, BOOL fAddHead, int idsTemplate, LPCTSTR pszFormat, va_list arg) { vFormatMultiString (ppszTarget, fAddHead, (LONG)idsTemplate, pszFormat, arg); } /*** FormatString() - replacable-parameter version of wsprintf() * */ LPTSTR cdecl FormatString (LPCTSTR psz, LPCTSTR pszFmt, ...) { va_list arg; if (pszFmt != NULL) va_start (arg, pszFmt); return vFormatString ((LONG)psz, pszFmt, arg); } LPTSTR cdecl FormatString (int ids, LPCTSTR pszFmt, ...) { va_list arg; if (pszFmt != NULL) va_start (arg, pszFmt); return vFormatString ((LONG)ids, pszFmt, arg); } LPTSTR cdecl vFormatString (LPCTSTR psz, LPCTSTR pszFmt, va_list arg) { return vFormatString ((LONG)psz, pszFmt, arg); } LPTSTR cdecl vFormatString (int ids, LPCTSTR pszFmt, va_list arg) { return vFormatString ((LONG)ids, pszFmt, arg); } typedef enum // vartype { vtINVALID, vtWORD, vtDWORD, vtFLOAT, vtDOUBLE, vtSTRINGW, vtSTRINGA, vtMESSAGE, vtBYTES_DOUBLE, vtBYTES, vtSYSTEMTIME, vtELAPSEDTIME, vtERROR, vtADDRESS, vtLARGEINT, } vartype; #ifdef UNICODE #define vtSTRING vtSTRINGW // %s gives vtSTRING #else #define vtSTRING vtSTRINGA #endif #define cchMaxNUMBER 40 #define cszNULL TEXT("(null)") LPTSTR cdecl vFormatString (LONG pszSource, LPCTSTR pszFmt, va_list arg) { LPTSTR *apszArgs; LPTSTR psz; LPTSTR pszOut = NULL; LPTSTR pchOut; LPTSTR pszTemplate; LONG cch; int nArgs; int argno; TCHAR szFmt[ cchRESOURCE ]; if (pszSource == 0) return NULL; if (HIWORD(pszSource) != 0) // It's a string { pszTemplate = (LPTSTR)pszSource; } else // It's a message { cch = GetStringLength ((int)pszSource); if ((pszTemplate = AllocateString (1+cch)) == NULL) return NULL; GetString (pszTemplate, (int)pszSource, cch); } // // First off, count the number of arguments given in {pszFmt}. // nArgs = 0; if (pszFmt != NULL) { for (psz = (LPTSTR)pszFmt; *psz; psz++) { if (*psz == TEXT('%')) nArgs++; } } // // If there are no arguments, then we're just expanding {pszSource}. // if (nArgs == 0) { if (HIWORD(pszSource) == 0) // It's a message { psz = pszTemplate; } else // (HIWORD(pszSource) != 0) // It's a string { psz = CloneString (pszTemplate); } return psz; } // // Otherwise, allocate an array of LPTSTR's, one for each // argument. // apszArgs = (LPTSTR*)Allocate (sizeof(LPTSTR) * nArgs); if (apszArgs == NULL) { if (pszSource != (LONG)pszTemplate) FreeString (pszTemplate); return NULL; } memset (apszArgs, 0x00, sizeof(LPTSTR) * nArgs); // // Extract each argument in turn, using {pszFmt} and {arg}. // for (argno = 0; pszFmt && *pszFmt; argno++) { LONG cchMin; vartype vt; double arg_f; LONG arg_d; LPTSTR arg_psz; SYSTEMTIME *arg_t; SOCKADDR_IN *arg_a; LARGE_INTEGER *arg_ldw; // // At this point, {pstFmt} points to the start of an argument's // decription within the user-supplied format string // (which may look like, say, "%-12.30ls%40lu"). Parse // the "%-12.30" part of each argument first... // if ((*pszFmt != TEXT('%')) || (*(1+pszFmt) == TEXT('\0'))) break; lstrcpy (szFmt, pszFmt); if ((psz = (LPTSTR)lstrchr (&szFmt[1], TEXT('%'))) != NULL) *psz = 0; for (int ii = 1; szFmt[ii] == TEXT('-') || szFmt[ii] == TEXT(','); ii++) ; cchMin = _ttol (&szFmt[ii]); for (pszFmt++; *pszFmt && isfmtgarbage(*pszFmt); ++pszFmt) { if (*pszFmt == TEXT('%')) break; } if (!pszFmt || !*pszFmt) break; if (*pszFmt == TEXT('%')) continue; // // Yuck. At this point, // cchMin is either 0 or whatever appeared as X in "%X.##s" // pszFmt points to the first character after "%##.##" // // Step through the format string's definition ("c", "lu", etc) // to determine what kind of argument this one is, setting {vt} // accordingly. // for (vt = vtINVALID; *pszFmt && *pszFmt != TEXT('%'); pszFmt++) { switch (*pszFmt) { case TEXT('c'): vt = vtWORD; break; case TEXT('x'): case TEXT('X'): case TEXT('h'): case TEXT('u'): case TEXT('d'): if (vt != vtDWORD) vt = vtWORD; break; case TEXT('l'): if (vt == vtFLOAT) vt = vtDOUBLE; else vt = vtDWORD; break; case TEXT('i'): // "%i"==int, "%li"==large_integer if (vt != vtDWORD) vt = vtWORD; else vt = vtLARGEINT; break; case TEXT('g'): case TEXT('f'): if (vt == vtDWORD) vt = vtDOUBLE; else vt = vtFLOAT; break; case TEXT('s'): if (vt == vtDWORD) vt = vtSTRINGW; else if (vt == vtWORD) vt = vtSTRINGA; else vt = vtSTRING; break; case TEXT('m'): vt = vtMESSAGE; *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s break; case TEXT('B'): // %B = double cb vt = vtBYTES_DOUBLE; *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s break; case TEXT('b'): // %b = ULONG cb vt = vtBYTES; *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s break; case TEXT('t'): if (vt == vtERROR) // %et == systemtime* (shown as elapsed) vt = vtELAPSEDTIME; else // (vt != vtERROR) // %t == systemtime* (shown as elapsed) vt = vtSYSTEMTIME; break; case TEXT('e'): // %e == error vt = vtERROR; break; case TEXT('a'): // %a = LPSOCKADDR_IN vt = vtADDRESS; *(LPTSTR)(lstrchr(szFmt, *pszFmt)) = TEXT('s'); // prints as %s break; } } if (vt == vtINVALID) continue; // // Now the fun part. Pop the appropriate argument off the // stack; note that we had to know how big the argument is, // and in what format, in order to do this. // switch (vt) { case vtWORD: arg_d = (LONG)(va_arg (arg, short)); cch = cchMaxNUMBER; break; case vtDWORD: arg_d = va_arg (arg, LONG); cch = cchMaxNUMBER; break; case vtFLOAT: arg_f = (double)( va_arg (arg, float) ); cch = cchMaxNUMBER; break; case vtDOUBLE: arg_f = va_arg (arg, double); cch = cchMaxNUMBER; break; case vtSTRINGA: { LPSTR arg_pszA = va_arg (arg, LPSTR); if (arg_pszA == NULL) arg_psz = CloneString (cszNULL); else arg_psz = CloneAnsi (arg_pszA); cch = lstrlen(arg_psz); break; } case vtSTRINGW: { LPWSTR arg_pszW = va_arg (arg, LPWSTR); if (arg_pszW == NULL) arg_psz = CloneString (cszNULL); else arg_psz = CloneUnicode (arg_pszW); cch = lstrlen(arg_psz); break; } case vtMESSAGE: { int arg_ids = va_arg (arg, int); if ((cch = GetStringLength (arg_ids)) == 0) arg_psz = CloneString (cszNULL); else { if ((arg_psz = AllocateString (cch)) == NULL) goto lblDONE; GetString (arg_psz, arg_ids, cch); } cch = lstrlen(arg_psz); break; } case vtBYTES_DOUBLE: arg_f = va_arg (arg, double); cch = cchMaxNUMBER; break; case vtBYTES: arg_f = (double)va_arg (arg, LONG); cch = cchMaxNUMBER; break; case vtELAPSEDTIME: case vtSYSTEMTIME: arg_t = va_arg (arg, SYSTEMTIME*); cch = cchRESOURCE; break; case vtERROR: arg_d = va_arg (arg, DWORD); cch = cchRESOURCE; break; case vtADDRESS: arg_a = va_arg (arg, SOCKADDR_IN*); cch = cchRESOURCE; break; case vtLARGEINT: arg_ldw = va_arg (arg, LARGE_INTEGER*); cch = cchMaxNUMBER * 2; break; } // // Okay; now, depending on what {vt} is, one of // {arg_psz, arg_d, arg_f} has been set to match // this argument. Allocate an string for apszArgs[] // to represent the formatted, ready-for-output version // of this argument. // cch = max( cch, cchMin ); if ((apszArgs[ argno ] = AllocateString (cch+1)) == NULL) goto lblDONE; switch (vt) { case vtWORD: wsprintf (apszArgs[ argno ], szFmt, (short)arg_d); break; case vtDWORD: wsprintf (apszArgs[ argno ], szFmt, arg_d); break; case vtFLOAT: FormatDouble (apszArgs[ argno ], szFmt, arg_f); break; case vtDOUBLE: FormatDouble (apszArgs[ argno ], szFmt, arg_f); break; case vtSTRINGA: case vtSTRINGW: case vtMESSAGE: wsprintf (apszArgs[ argno ], szFmt, arg_psz); FreeString (arg_psz); break; case vtBYTES: case vtBYTES_DOUBLE: FormatBytes (apszArgs[ argno ], szFmt, arg_f); break; case vtELAPSEDTIME: FormatElapsed (apszArgs[ argno ], szFmt, arg_t); break; case vtSYSTEMTIME: FormatTime (apszArgs[ argno ], szFmt, arg_t); break; case vtERROR: FormatError (apszArgs[ argno ], szFmt, arg_d); break; case vtADDRESS: FormatSockAddr (apszArgs[ argno ], szFmt, arg_a); break; case vtLARGEINT: FormatLargeInt (apszArgs[ argno ], szFmt, arg_ldw); break; } } // // Determine how big the final string will be once the arguments // are inserted, and allocate space for it. // cch = 0; for (psz = pszTemplate; *psz; ) { if (*psz != TEXT('%')) { ++cch; ++psz; continue; } if (*(++psz) == TEXT('\0')) break; if (!isdigit(*psz)) { cch++; psz++; continue; } for (argno = 0; *psz && isdigit (*psz); psz++) { argno *= 10; argno += (int)(*psz -'0'); } if (*psz == '~') psz++; if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL) cch += lstrlen (cszNULL); else cch += lstrlen (apszArgs[argno -1]); } if ((pszOut = AllocateString (cch+1)) == NULL) goto lblDONE; // // Then generate a final output, by copying over the template // string into the final string, inserting arguments wherever // a "%1" (or "%43" etc) is found. // for (psz = pszTemplate, pchOut = pszOut; *psz; ) { if (*psz != TEXT('%')) { *pchOut++ = *psz++; continue; } if (*(++psz) == TEXT('\0')) break; if (!isdigit(*psz)) { *pchOut++ = *psz++; continue; } for (argno = 0; *psz && isdigit (*psz); psz++) { argno *= 10; argno += (int)(*psz -'0'); } if (*psz == '~') psz++; if (argno == 0 || argno > nArgs || apszArgs[argno -1] == NULL) lstrcpy (pchOut, cszNULL); else lstrcpy (pchOut, apszArgs[argno -1]); pchOut = &pchOut[ lstrlen(pchOut) ]; } *pchOut = 0; lblDONE: if (apszArgs != NULL) { for (argno = 0; argno < nArgs; argno++) { if (apszArgs[ argno ] != NULL) FreeString (apszArgs[ argno ]); } Free (apszArgs); } if (HIWORD(pszSource) == 0) // Did we allocate {pszTemplate}? { if (pszTemplate != NULL) { FreeString (pszTemplate); } } return pszOut; } /*** FormatSockAddr() - formats a SOCKADDR_IN as text * */ void FormatSockAddr (LPTSTR pszTarget, LPTSTR pszFmt, SOCKADDR_IN *paddr) { TCHAR szText[ cchRESOURCE ]; LPSTR pszSockAddrA = inet_ntoa (*(struct in_addr *)&paddr->sin_addr.s_addr); LPTSTR pszSockAddr = AnsiToString (pszSockAddrA); lstrcpy (szText, pszSockAddr); FreeString (pszSockAddr, pszSockAddrA); wsprintf (pszTarget, FixFormatString (pszFmt), szText); } /*** FormatElapsed() - formats a SYSTEMTIME* as HH:MM:SS * */ BOOL FormatElapsed (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst) { TCHAR szTimeSep[ cchRESOURCE ]; if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_STIME, szTimeSep, cchRESOURCE)) lstrcpy (szTimeSep, TEXT(":")); TCHAR szElapsed[ cchRESOURCE ]; wsprintf (szElapsed, TEXT("%02lu%s%02lu%s%02lu"), pst->wHour + (pst->wDay * 24), szTimeSep, pst->wMinute, szTimeSep, pst->wSecond); wsprintf (pszTarget, FixFormatString (pszFormatUser), szElapsed); return TRUE; } /*** FormatTime() - formats a SYSTEMTIME* according to the current locale * */ BOOL FormatTime (LPTSTR pszTarget, LPTSTR pszFormatUser, SYSTEMTIME *pst, BOOL fShowDate, BOOL fShowTime) { BOOL rc = TRUE; SYSTEMTIME lt; FILETIME ft; FILETIME lft; SystemTimeToFileTime (pst, &ft); FileTimeToLocalFileTime (&ft, &lft); FileTimeToSystemTime (&lft, <); TCHAR szTime[ cchRESOURCE ]; TCHAR szDate[ cchRESOURCE ]; if ( ((pst->wYear == 1970) && (pst->wMonth == 1) && (pst->wDay == 1)) || ((pst->wYear == 0) && (pst->wMonth == 0) && (pst->wDay == 0)) ) { szTime[0] = TEXT('\0'); rc = FALSE; } else { GetTimeFormat (LOCALE_USER_DEFAULT, 0, <, NULL, szTime, cchRESOURCE); GetDateFormat (LOCALE_USER_DEFAULT, DATE_SHORTDATE, <, NULL, szDate, cchRESOURCE); if (fShowTime && fShowDate) { lstrcat (szTime, TEXT(" ")); lstrcat (szTime, szDate); } else if (fShowDate && !fShowTime) { lstrcpy (szTime, szDate); } } wsprintf (pszTarget, FixFormatString (pszFormatUser), szTime); return rc; } /*** FormatError() - formats an error code as text * */ LPERRORPROC pfnTranslateError = NULL; void SetErrorTranslationFunction (LPERRORPROC pfn) { pfnTranslateError = pfn; } BOOL FormatError (LPTSTR pszTarget, LPTSTR pszFmt, DWORD dwError) { TCHAR szError[ cchRESOURCE ]; BOOL rc = TranslateError (szError, dwError); LPTSTR pchTarget = szError; for (LPTSTR pchSource = szError; *pchSource; ++pchSource) { if (*pchSource == TEXT('\n')) *pchTarget++ = TEXT(' '); else if (*pchSource != TEXT('\r')) *pchTarget++ = *pchSource; } *pchTarget = TEXT('\0'); wsprintf (pszTarget, FixFormatString (pszFmt), szError); return rc; } BOOL TranslateErrorFunc (LPTSTR pszText, ULONG code, LANGID idLanguage) { BOOL rc = FALSE; // Guess an appropriate language ID if none was supplied explicitly // if (idLanguage == 0) { idLanguage = TaLocale_GetLanguage(); } // See if the caller supplied a function for us to use. // if (pfnTranslateError) { rc = (*pfnTranslateError)(pszText, code, idLanguage); } // Try to get Windows to translate the thing... // if (!rc) { if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, idLanguage, pszText, cchRESOURCE, NULL)) { rc = TRUE; } } // See if the message is buried in NTDLL... // if (!rc) { HINSTANCE hiNTDLL; if ((hiNTDLL = LoadLibrary (TEXT("NTDLL.DLL"))) != NULL) { if (FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, hiNTDLL, code, idLanguage, pszText, cchRESOURCE, NULL)) { rc = TRUE; } FreeLibrary (hiNTDLL); } } return rc; } BOOL TranslateError (LPTSTR pszText, ULONG status) { BOOL rc; if ((rc = TranslateErrorFunc (pszText, status, 0)) == TRUE) { wsprintf (&pszText[ lstrlen(pszText) ], TEXT(" (0x%08lX)"), status); } else { wsprintf (pszText, TEXT("0x%08lX"), status); } return rc; } /*** FormatBytes() - formats L1048576 as "1.0 MB" (etc) * * Wherever "%1" appears is where the number will show up (formatted * according to {pszFormat}). * */ void FormatBytes (LPTSTR pszTarget, LPTSTR pszFormatUser, double cb) { TCHAR szFormat[ cchRESOURCE ]; TCHAR szTemplate[ cchRESOURCE ]; TCHAR szJustTheNumber[ cchRESOURCE ]; LPTSTR pszOut; // // The first task is to generate a useful {pszFormat}; it may // come in looking like "%-15.15s" or "%0.2b" or "%lu" --who knows. // We'll need it to produce a formatted string from a DWORD, so // make sure it ends in "lf". // if (*pszFormatUser != TEXT('%')) { lstrcpy (szFormat, TEXT("%.2lf")); // sigh--totally bogus format string. } else { lstrcpy (szFormat, pszFormatUser); for (LPTSTR pch = &szFormat[1]; *pch; ++pch) { if (!isfmtgarbage(*pch)) { lstrcpy (pch, TEXT("lf")); break; } } if (!*pch) { lstrcat (pch, TEXT("lf")); } } // // The next step is to generate just the number portion of the // output; that will look like "0.90" or "5.3", or whatever. // FormatDouble (szJustTheNumber, szFormat, cb/1048576.0); lstrcpy (szTemplate, TEXT("%1 MB")); // // Cheesy bit: if all we have are 0's and "."'s, just // make the number read "0". // for (TCHAR *pch = szJustTheNumber; *pch; ++pch) { if (*pch != TEXT('0') && *pch != TEXT('.')) break; } if (!*pch) { lstrcpy (szJustTheNumber, TEXT("0")); } // // Not particularly hard. Now let FormatString() generate // the final output, by tacking on the necessary units. // if ((pszOut = FormatString (szTemplate, TEXT("%s"), szJustTheNumber)) == NULL) { // whoops--out of memory? // well, just give it back in bytes--we can still do that. // FormatDouble (pszTarget, szFormat, cb); } else { lstrcpy (pszTarget, pszOut); FreeString (pszOut); } } /*** FormatLargeInt() - formats a large int as "hidword,,lodword" * */ void FormatLargeInt (LPTSTR pszTarget, LPTSTR pszFormatUser, LARGE_INTEGER *pldw) { // Generate just the number portion of the output; // that will look like "###,,###" // TCHAR szJustTheNumber[ cchRESOURCE ]; wsprintf (szJustTheNumber, TEXT("%lu,,%lu"), (DWORD)pldw->HighPart, (DWORD)pldw->LowPart); // Not particularly hard. Now let FormatString() generate // the final output, by tacking on the necessary units // description. // wsprintf (pszTarget, FixFormatString (pszFormatUser), szJustTheNumber); } /*** FormatDouble() - like wsprintf(), but for one argument (a double) * * since {wsprintf} doesn't understand "%lf", "%f", "%g", "%e", etc, * use this function instead. {pszFormat} should consist of nothing * except the formatting string: thus, "%3.15lf" is okay, but "hi %lf there" * isn't. * */ void FormatDouble (LPTSTR pszTarget, LPTSTR pszFormat, double lfValue) { BOOL fPlusUpFront = FALSE; BOOL fZeroUpFront = FALSE; int cchMin = 0; int cchMax = 8; // First, parse the format string: look for "%{zero|plus}{min}.{max}", // and ignore any other characters. // while (*pszFormat && *pszFormat != TEXT('%')) ++pszFormat; while (*pszFormat == TEXT('%') || *pszFormat == TEXT('0') || *pszFormat == TEXT('+') || *pszFormat == TEXT('-') ) { if (*pszFormat == TEXT('0')) { fZeroUpFront = TRUE; } else if (*pszFormat == TEXT('+')) { fPlusUpFront = TRUE; } ++pszFormat; } cchMin = _ttol( pszFormat ); while (isdigit(*pszFormat)) ++pszFormat; if (*pszFormat == TEXT('.')) { ++pszFormat; cchMax = _ttol( pszFormat ); } // Then generate the output string, based on what we just learned // and what {lfValue} is. // if (lfValue >= 0 && fPlusUpFront) { *pszTarget++ = TEXT('+'); } else if (lfValue < 0) { *pszTarget++ = TEXT('-'); lfValue = 0 - lfValue; } if (lfValue >= 1 || fZeroUpFront) { unsigned long ulValue = (unsigned long)lfValue; wsprintf (pszTarget, TEXT("%lu"), ulValue); pszTarget = &pszTarget[ lstrlen(pszTarget) ]; lfValue -= (double)ulValue; } if (cchMax != 0) { unsigned long ulValue; TCHAR szTemp[ cchRESOURCE ]; LPTSTR pszDecimalPoint = pszTarget; *pszTarget++ = TEXT('.'); for (int ii = 0; ii < cchMax; ++ii) { lfValue *= 10.0; *pszTarget++ = TEXT('0'); } ulValue = (unsigned long)lfValue; wsprintf (szTemp, TEXT("%lu"), ulValue); szTemp[ cchMax ] = 0; lstrcpy (&pszDecimalPoint[ 1 + cchMax - lstrlen(szTemp) ], szTemp); pszTarget = &pszTarget[ lstrlen(pszTarget) ]; } *pszTarget = TEXT('\0'); } void lstrupr (LPTSTR psz) { for ( ; psz && *psz; psz = CharNext (psz)) *psz = _ttoupper (*psz); } LPCTSTR lstrchr (LPCTSTR pszTarget, TCHAR ch) { for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget)) { if (*pszTarget == ch) return pszTarget; } return NULL; } LPCTSTR lstrrchr (LPCTSTR pszTarget, TCHAR ch) { LPCTSTR pszLast = NULL; for ( ; pszTarget && *pszTarget; pszTarget = CharNext(pszTarget)) { if (*pszTarget == ch) pszLast = pszTarget; } return pszLast; } int lstrncmpi (LPCTSTR pszA, LPCTSTR pszB, size_t cch) { if (!pszA || !pszB) { return (!pszB) - (!pszA); // A,!B:1, !A,B:-1, !A,!B:0 } for ( ; cch > 0; cch--, pszA = CharNext(pszA), pszB = CharNext(pszB)) { TCHAR chA = _ttoupper( *pszA ); TCHAR chB = _ttoupper( *pszB ); if (!chA || !chB) return (!chB) - (!chA); // A,!B:1, !A,B:-1, !A,!B:0 if (chA != chB) return (int)(chA) - (int)(chB); // -1:AB } return 0; // no differences before told to stop comparing, so A==B } void lstrncpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch) { size_t ich; for (ich = 0; ich < cch; ich++) { if ((pszTarget[ich] = pszSource[ich]) == TEXT('\0')) break; } } void lstrzcpy (LPTSTR pszTarget, LPCTSTR pszSource, size_t cch) { cch = min(cch, (size_t)(1+lstrlen(pszSource))); lstrncpy (pszTarget, pszSource, cch-1); pszTarget[ cch-1 ] = TEXT('\0'); } void lsplitpath (LPCTSTR pszSource, LPTSTR pszDrive, LPTSTR pszPath, LPTSTR pszName, LPTSTR pszExt) { LPCTSTR pszLastSlash = NULL; LPCTSTR pszLastDot = NULL; LPCTSTR pch; size_t cchCopy; /* * NOTE: This routine was snitched out of USERPRI.LIB 'cause the * one in there doesn't split the extension off the name properly. * * We assume that the path argument has the following form, where any * or all of the components may be missing. * * * * and each of the components has the following expected form(s) * * drive: * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a * ':' * dir: * 0 to _MAX_DIR-1 characters in the form of an absolute path * (leading '/' or '\') or relative path, the last of which, if * any, must be a '/' or '\'. E.g - * absolute path: * \top\next\last\ ; or * /top/next/last/ * relative path: * top\next\last\ ; or * top/next/last/ * Mixed use of '/' and '\' within a path is also tolerated * fname: * 0 to _MAX_FNAME-1 characters not including the '.' character * ext: * 0 to _MAX_EXT-1 characters where, if any, the first must be a * '.' * */ // extract drive letter and :, if any // if (*(pszSource + _MAX_DRIVE - 2) == TEXT(':')) { if (pszDrive) { lstrncpy (pszDrive, pszSource, _MAX_DRIVE-1); pszDrive[ _MAX_DRIVE-1 ] = TEXT('\0'); } pszSource += _MAX_DRIVE-1; } else if (pszDrive) { *pszDrive = TEXT('\0'); } // extract path string, if any. pszSource now points to the first // character of the path, if any, or the filename or extension, if // no path was specified. Scan ahead for the last occurence, if // any, of a '/' or '\' path separator character. If none is found, // there is no path. We will also note the last '.' character found, // if any, to aid in handling the extension. // for (pch = pszSource; *pch != TEXT('\0'); pch++) { if (*pch == TEXT('/') || *pch == TEXT('\\')) pszLastSlash = pch; else if (*pch == TEXT('.')) pszLastDot = pch; } // if we found a '\\' or '/', fill in pszPath // if (pszLastSlash) { if (pszPath) { cchCopy = min( _MAX_DIR -1, pszLastSlash -pszSource +1 ); lstrncpy (pszPath, pszSource, cchCopy); pszPath[ cchCopy ] = 0; } pszSource = pszLastSlash +1; } else if (pszPath) { *pszPath = TEXT('\0'); } // extract file name and extension, if any. Path now points to // the first character of the file name, if any, or the extension // if no file name was given. Dot points to the '.' beginning the // extension, if any. // if (pszLastDot && (pszLastDot >= pszSource)) { // found the marker for an extension - // copy the file name up to the '.'. // if (pszName) { cchCopy = min( _MAX_DIR-1, pszLastDot -pszSource ); lstrncpy (pszName, pszSource, cchCopy); pszName[ cchCopy ] = 0; } // now we can get the extension // if (pszExt) { lstrncpy (pszExt, pszLastDot, _MAX_EXT -1); pszExt[ _MAX_EXT-1 ] = TEXT('\0'); } } else { // found no extension, give empty extension and copy rest of // string into fname. // if (pszName) { lstrncpy (pszName, pszSource, _MAX_FNAME -1); pszName[ _MAX_FNAME -1 ] = TEXT('\0'); } if (pszExt) { *pszExt = TEXT('\0'); } } } LPCTSTR FindExtension (LPCTSTR name) { LPCTSTR pch; if ((pch = lstrchr (FindBaseFileName(name), TEXT('.'))) != NULL) return pch; return &name[ lstrlen(name) ]; } LPCTSTR FindBaseFileName (LPCTSTR name) { LPCTSTR pszBase; if ((pszBase = lstrrchr (name, TEXT('\\'))) != NULL) return CharNext(pszBase); else return name; } void ChangeExtension (LPTSTR pszOut, LPCTSTR pszIn, LPCTSTR pszExt, BOOL fForce) { LPCTSTR pch; if (pszOut != pszIn) lstrcpy (pszOut, pszIn); if ( (*(pch = FindExtension (pszOut)) != TEXT('\0')) && (!fForce) ) return; if (pszExt[0] == TEXT('.') || pszExt[0] == TEXT('\0')) { lstrcpy ((LPTSTR)pch, pszExt); } else { lstrcpy ((LPTSTR)pch, TEXT(".")); lstrcat ((LPTSTR)pch, pszExt); } } void CopyBaseFileName (LPTSTR pszTarget, LPCTSTR pszSource) { lstrcpy (pszTarget, FindBaseFileName (pszSource)); LPTSTR pszExt; if ((pszExt = (LPTSTR)FindExtension(pszTarget)) != NULL) *pszExt = TEXT('\0'); } /* * ANSI/UNICODE _______________________________________________________________ * */ void CopyUnicodeToAnsi (LPSTR pszTargetA, LPCWSTR pszOriginalW, size_t cchMax) { static size_t cchBufferW = 0; static LPWSTR pszBufferW = NULL; size_t cchSource = min( cchMax, (size_t)lstrlenW(pszOriginalW)+1 ); if (cchSource > cchBufferW) { if (pszBufferW) FreeString (pszBufferW); pszBufferW = AllocateUnicode (cchSource); cchBufferW = cchSource; } memcpy ((LPBYTE)pszBufferW, (LPBYTE)pszOriginalW, cchSource * sizeof(WCHAR)); pszBufferW[ cchSource-1 ] = L'\0'; UINT cpTarget = CP_ACP; BOOL fDefault = FALSE; size_t cchOut = WideCharToMultiByte (cpTarget, 0, pszOriginalW, cchSource-1, pszTargetA, cchMax * 2, TEXT(" "), &fDefault); pszTargetA[ cchOut ] = 0; } void CopyAnsiToUnicode (LPWSTR pszTargetW, LPCSTR pszOriginalA, size_t cchMax) { static size_t cchBufferA = 0; static LPSTR pszBufferA = NULL; cchMax = min( cchMax, (size_t)lstrlenA(pszOriginalA)+1 ); if (cchMax > cchBufferA) { if (pszBufferA) FreeString (pszBufferA); pszBufferA = AllocateAnsi (cchMax); cchBufferA = cchMax; } memcpy ((LPBYTE)pszBufferA, (LPBYTE)pszOriginalA, cchMax); pszBufferA[ cchMax-1 ] = L'\0'; wsprintfW (pszTargetW, L"%hs", pszBufferA); } void CopyAnsiToString (LPTSTR pszTarget, LPCSTR pszOriginalA, size_t cchMax) { #ifdef UNICODE CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA, cchMax); #else lstrzcpy ((LPSTR)pszTarget, pszOriginalA, cchMax); #endif } void CopyUnicodeToString (LPTSTR pszTarget, LPCWSTR pszOriginalW, size_t cchMax) { #ifdef UNICODE lstrzcpy ((LPWSTR)pszTarget, pszOriginalW, cchMax); #else CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW, cchMax); #endif } void CopyStringToUnicode (LPWSTR pszTargetW, LPCTSTR pszOriginal, size_t cchMax) { #ifdef UNICODE lstrzcpy (pszTargetW, (LPWSTR)pszOriginal, cchMax); #else CopyAnsiToUnicode (pszTargetW, (LPSTR)pszOriginal, cchMax); #endif } void CopyStringToAnsi (LPSTR pszTargetA, LPCTSTR pszOriginal, size_t cchMax) { #ifdef UNICODE CopyUnicodeToAnsi (pszTargetA, (LPWSTR)pszOriginal, cchMax); #else lstrzcpy (pszTargetA, (LPSTR)pszOriginal, cchMax); #endif } void FreeString (LPCVOID pszString, LPCVOID pszOriginalString) { if ( (pszString != NULL) && (pszString != pszOriginalString) ) { Free ((PVOID)pszString); } } LPSTR StringToAnsi (LPCTSTR pszOriginal) { #ifndef UNICODE return (LPSTR)pszOriginal; #else if (!pszOriginal) return NULL; LPSTR pszTargetA; if ((pszTargetA = AllocateAnsi (1+lstrlen(pszOriginal))) != NULL) CopyUnicodeToAnsi (pszTargetA, pszOriginal); return pszTargetA; #endif } LPTSTR AnsiToString (LPCSTR pszOriginalA) { #ifndef UNICODE return (LPTSTR)pszOriginalA; #else if (!pszOriginalA) return NULL; LPTSTR pszTarget; if ((pszTarget = AllocateString (1+lstrlenA(pszOriginalA))) != NULL) CopyAnsiToUnicode (pszTarget, pszOriginalA); return pszTarget; #endif } LPWSTR StringToUnicode (LPCTSTR pszOriginal) { #ifdef UNICODE return (LPWSTR)pszOriginal; #else if (!pszOriginal) return NULL; LPWSTR pszTargetW; if ((pszTargetW = AllocateUnicode (1+lstrlen(pszOriginal))) != NULL) CopyAnsiToUnicode (pszTargetW, pszOriginal); return pszTargetW; #endif } LPTSTR UnicodeToString (LPCWSTR pszOriginalW) { #ifdef UNICODE return (LPTSTR)pszOriginalW; #else if (!pszOriginalW) return NULL; LPTSTR pszTarget; if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL) CopyUnicodeToAnsi (pszTarget, pszOriginalW); return pszTarget; #endif } LPWSTR AnsiToUnicode (LPCSTR pszOriginalA) { if (!pszOriginalA) return NULL; LPWSTR pszTargetW; if ((pszTargetW = AllocateUnicode (1+lstrlenA(pszOriginalA))) != NULL) CopyAnsiToUnicode (pszTargetW, pszOriginalA); return pszTargetW; } LPSTR UnicodeToAnsi (LPCWSTR pszOriginalW) { if (!pszOriginalW) return NULL; LPSTR pszTargetA; if ((pszTargetA = AllocateAnsi (1+lstrlenW(pszOriginalW))) != NULL) CopyUnicodeToAnsi (pszTargetA, pszOriginalW); return pszTargetA; } LPTSTR CloneAnsi (LPSTR pszOriginalA) { if (!pszOriginalA) return NULL; LPTSTR pszTarget; if ((pszTarget = AllocateString (1+lstrlenA(pszOriginalA))) != NULL) { #ifdef UNICODE CopyAnsiToUnicode ((LPWSTR)pszTarget, pszOriginalA); #else lstrcpyA ((LPSTR)pszTarget, pszOriginalA); #endif } return pszTarget; } LPTSTR CloneUnicode (LPWSTR pszOriginalW) { if (!pszOriginalW) return NULL; LPTSTR pszTarget; if ((pszTarget = AllocateString (1+lstrlenW(pszOriginalW))) != NULL) { #ifdef UNICODE lstrcpyW ((LPWSTR)pszTarget, pszOriginalW); #else CopyUnicodeToAnsi ((LPSTR)pszTarget, pszOriginalW); #endif } return pszTarget; } LPTSTR CloneMultiString (LPCTSTR mszOriginal) { if (!mszOriginal) return NULL; size_t cchOld = 1; // for the second NUL-terminator for (LPCTSTR psz = mszOriginal; psz && *psz; psz += 1+lstrlen(psz)) cchOld += lstrlen(psz) +1; LPTSTR pszTarget; if ((pszTarget = AllocateString (cchOld)) != NULL) memcpy (pszTarget, mszOriginal, cchOld); return pszTarget; } LPTSTR CloneString (LPTSTR pszOriginal) { if (!pszOriginal) return NULL; LPTSTR pszTarget; if ((pszTarget = AllocateString (1+lstrlen(pszOriginal))) != NULL) lstrcpy (pszTarget, pszOriginal); return pszTarget; } LPTSTR FixFormatString (LPTSTR pszFormat) { static TCHAR szFormat[ 256 ]; lstrcpy (szFormat, TEXT("%s")); if (*pszFormat == TEXT('%')) { lstrcpy (szFormat, pszFormat); for (LPTSTR pch = &szFormat[1]; *pch; ++pch) { if (!isfmtgarbage(*pch)) { lstrcpy (pch, TEXT("s")); break; } } if (!*pch) { lstrcat (pch, TEXT("s")); } } return szFormat; }