mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 10:22:40 +01:00
1603 lines
36 KiB
C++
1603 lines
36 KiB
C++
|
/* Copyright (c) Microsoft Corporation.
|
||
|
Licensed under the MIT License. */
|
||
|
|
||
|
/***************************************************************************
|
||
|
Author: ShonK
|
||
|
Project: Kauai
|
||
|
Copyright (c) Microsoft Corporation
|
||
|
|
||
|
String manipulation.
|
||
|
WARNING: Must be in a fixed (pre-loaded) seg on Mac.
|
||
|
|
||
|
***************************************************************************/
|
||
|
#include "util.h"
|
||
|
ASSERTNAME
|
||
|
|
||
|
#include "chtrans.h"
|
||
|
|
||
|
|
||
|
const achar vrgchHex[] = PszLit("0123456789ABCDEF");
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Constructor for a string based on another string.
|
||
|
***************************************************************************/
|
||
|
STN::STN(STN &stnSrc)
|
||
|
{
|
||
|
AssertPo(&stnSrc, 0);
|
||
|
|
||
|
CopyPb(stnSrc._rgch, _rgch, (stnSrc.Cch() + 2) * size(achar));
|
||
|
AssertThis(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Constructor for a string based on an sz.
|
||
|
***************************************************************************/
|
||
|
STN::STN(PSZ pszSrc)
|
||
|
{
|
||
|
long cch = LwBound(CchSz(pszSrc), 0, kcchMaxStn + 1);
|
||
|
|
||
|
AssertIn(cch, 0, kcchMaxStn + 1);
|
||
|
CopyPb(pszSrc, _rgch + 1, cch * size(achar));
|
||
|
_rgch[0] = (achar)cch;
|
||
|
_rgch[cch + 1] = 0;
|
||
|
AssertThis(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Assignment of one string to another.
|
||
|
***************************************************************************/
|
||
|
STN & STN::operator=(STN &stnSrc)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(&stnSrc, 0);
|
||
|
|
||
|
CopyPb(stnSrc._rgch, _rgch, (stnSrc.Cch() + 2) * size(achar));
|
||
|
AssertThis(0);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Set the string to the given array of characters.
|
||
|
***************************************************************************/
|
||
|
void STN::SetRgch(achar *prgchSrc, long cch)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cch, 0, kcbMax);
|
||
|
AssertPvCb(prgchSrc, cch * size(achar));
|
||
|
|
||
|
if (cch > kcchMaxStn)
|
||
|
cch = kcchMaxStn;
|
||
|
|
||
|
if (cch > 0)
|
||
|
CopyPb(prgchSrc, _rgch + 1, cch * size(achar));
|
||
|
else
|
||
|
cch = 0; // for safety
|
||
|
|
||
|
_rgch[0] = (achar)cch;
|
||
|
_rgch[cch + 1] = 0;
|
||
|
AssertThis(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Put the zero terminated short character string into the STN.
|
||
|
***************************************************************************/
|
||
|
void STN::SetSzs(PSZS pszsSrc)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(pszsSrc);
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
#ifdef WIN
|
||
|
//REVIEW shonk: is this correct?
|
||
|
long cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszsSrc, -1,
|
||
|
_rgch + 1, kcchMaxStn);
|
||
|
|
||
|
AssertIn(cch, 1, kcchMaxStn + 1);
|
||
|
_rgch[0] = (achar)(cch - 1);
|
||
|
_rgch[cch] = 0;
|
||
|
#endif //WIN
|
||
|
#ifdef MAC
|
||
|
RawRtn(); //REVIEW shonk: Mac: implement
|
||
|
#endif //MAC
|
||
|
#else //!UNICODE
|
||
|
SetSz(pszsSrc);
|
||
|
#endif //UNICODE
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Delete (at most) cch characters starting at position ich.
|
||
|
***************************************************************************/
|
||
|
void STN::Delete(long ich, long cch)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cch, 0, kcbMax);
|
||
|
long cchCur = Cch();
|
||
|
|
||
|
if (!FIn(ich, 0, cchCur))
|
||
|
{
|
||
|
Assert(ich == cchCur, "Bad character position to delete from");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ich + cch >= cchCur)
|
||
|
{
|
||
|
// delete to the end of the string
|
||
|
_rgch[0] = (achar)ich;
|
||
|
_rgch[ich + 1] = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BltPb(_rgch + ich + cch + 1, _rgch + ich + 1,
|
||
|
(cchCur - ich - cch) * size(achar));
|
||
|
_rgch[0] = (achar)(cchCur - cch);
|
||
|
_rgch[cchCur - cch + 1] = 0;
|
||
|
}
|
||
|
AssertThis(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Append some characters to the end of the string.
|
||
|
***************************************************************************/
|
||
|
bool STN::FAppendRgch(achar *prgchSrc, long cch)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cch, 0, kcbMax);
|
||
|
AssertPvCb(prgchSrc, cch * size(achar));
|
||
|
bool fRet = fTrue;
|
||
|
long cchCur = Cch();
|
||
|
|
||
|
if (cch > kcchMaxStn - cchCur)
|
||
|
{
|
||
|
cch = kcchMaxStn - cchCur;
|
||
|
fRet = fFalse;
|
||
|
}
|
||
|
|
||
|
if (cch > 0)
|
||
|
{
|
||
|
CopyPb(prgchSrc, _rgch + cchCur + 1, cch * size(achar));
|
||
|
_rgch[0] = (achar)(cchCur + cch);
|
||
|
_rgch[cchCur + cch + 1] = 0;
|
||
|
}
|
||
|
|
||
|
AssertThis(0);
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Insert some characters into the middle of a string.
|
||
|
***************************************************************************/
|
||
|
bool STN::FInsertRgch(long ich, achar *prgchSrc, long cch)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cch, 0, kcbMax);
|
||
|
AssertPvCb(prgchSrc, cch * size(achar));
|
||
|
bool fRet = fTrue;
|
||
|
long cchCur = Cch();
|
||
|
|
||
|
ich = LwBound(ich, 0, cchCur + 1);
|
||
|
if (cch > kcchMaxStn - ich)
|
||
|
{
|
||
|
cchCur = ich;
|
||
|
cch = kcchMaxStn - ich;
|
||
|
fRet = fFalse;
|
||
|
}
|
||
|
else if (cch > kcchMaxStn - cchCur)
|
||
|
{
|
||
|
cchCur = kcchMaxStn - cch - ich;
|
||
|
fRet = fFalse;
|
||
|
}
|
||
|
|
||
|
Assert(cchCur + cch <= kcchMaxStn, 0);
|
||
|
if (cch > 0)
|
||
|
{
|
||
|
if (cchCur > ich)
|
||
|
{
|
||
|
BltPb(_rgch + ich + 1, _rgch + ich + cch + 1,
|
||
|
(cchCur - ich) * size(achar));
|
||
|
}
|
||
|
CopyPb(prgchSrc, _rgch + ich + 1, cch * size(achar));
|
||
|
_rgch[0] = (achar)(cchCur + cch);
|
||
|
_rgch[cchCur + cch + 1] = 0;
|
||
|
}
|
||
|
|
||
|
AssertThis(0);
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Test whether the given rgch is equal to this string. This does bytewise
|
||
|
compare - not user level comparison.
|
||
|
***************************************************************************/
|
||
|
bool STN::FEqualRgch(achar *prgch, long cch)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cch, 0, kcbMax);
|
||
|
AssertPvCb(prgch, cch * size(achar));
|
||
|
|
||
|
return cch == Cch() && FEqualRgb(_rgch + 1, prgch, cch * size(achar));
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Do user level string equality testing with the options given in grfstn.
|
||
|
***************************************************************************/
|
||
|
bool STN::FEqualUserRgch(achar *prgch, long cch, ulong grfstn)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cch, 0, kcbMax);
|
||
|
AssertPvCb(prgch, cch * size(achar));
|
||
|
|
||
|
return ::FEqualUserRgch(Prgch(), Cch(), prgch, cch, grfstn);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return the buffer size needed by GetData, or the block size needed
|
||
|
by STN::FWrite.
|
||
|
***************************************************************************/
|
||
|
long STN::CbData(void)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
|
||
|
return size(short) + (Cch() + 2) * size(achar);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Get the streamed data for the stn. pv should point to a buffer
|
||
|
CbData() bytes long.
|
||
|
***************************************************************************/
|
||
|
void STN::GetData(void *pv)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPvCb(pv, CbData());
|
||
|
short osk = koskCur;
|
||
|
|
||
|
CopyPb(&osk, pv, size(short));
|
||
|
CopyPb(_rgch, PvAddBv(pv, size(short)), (Cch() + 2) * size(achar));
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Writes the string data to the given block starting at position ib.
|
||
|
***************************************************************************/
|
||
|
bool STN::FWrite(PBLCK pblck, long ib)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pblck, 0);
|
||
|
long cbWrite = CbData();
|
||
|
long cbTot = pblck->Cb();
|
||
|
short osk = koskCur;
|
||
|
|
||
|
if (!FIn(ib, 0, cbTot - cbWrite + 1))
|
||
|
{
|
||
|
Bug("BLCK is not big enough");
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
if (!pblck->FWriteRgb(&osk, size(osk), ib))
|
||
|
return fFalse;
|
||
|
if (!pblck->FWriteRgb(_rgch, cbWrite - size(short), ib + size(short)))
|
||
|
return fFalse;
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Set the string from the given data.
|
||
|
***************************************************************************/
|
||
|
bool STN::FSetData(void *pv, long cbMax, long *pcbRead)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cbMax, 0, kcbMax);
|
||
|
AssertPvCb(pv, cbMax);
|
||
|
AssertNilOrVarMem(pcbRead);
|
||
|
long cch, ich, ibT, cbT;
|
||
|
wchar chw;
|
||
|
short osk;
|
||
|
|
||
|
ibT = 0;
|
||
|
if (cbMax < size(short) + ibT)
|
||
|
goto LFail;
|
||
|
CopyPb(pv, &osk, size(short));
|
||
|
ibT += size(short);
|
||
|
|
||
|
if (osk == koskCur)
|
||
|
{
|
||
|
// no translation needed - read the length prefix
|
||
|
if (cbMax < ibT + size(achar))
|
||
|
goto LFail;
|
||
|
CopyPb(PvAddBv(pv, ibT), &_rgch[0], size(achar));
|
||
|
ibT += size(achar);
|
||
|
cch = (long)(uchar)_rgch[0];
|
||
|
if (!FIn(cch, 0, kcchMaxStn + 1))
|
||
|
goto LFail;
|
||
|
|
||
|
// read the rest of the string
|
||
|
cbT = size(achar) * (cch + 1);
|
||
|
if (cbMax < ibT + cbT)
|
||
|
goto LFail;
|
||
|
CopyPb(PvAddBv(pv, ibT), _rgch + 1, cbT);
|
||
|
ibT += cbT;
|
||
|
|
||
|
// make sure the terminating zero is there
|
||
|
if (_rgch[cch + 1] != 0)
|
||
|
goto LFail;
|
||
|
|
||
|
goto LCheck;
|
||
|
}
|
||
|
|
||
|
switch (CbCharOsk(osk))
|
||
|
{
|
||
|
case size(schar):
|
||
|
if (ibT + 2 > cbMax)
|
||
|
goto LFail;
|
||
|
|
||
|
cch = (long)(byte)*(schar *)PvAddBv(pv, ibT++);
|
||
|
if (cch > kcchMaxStn || ibT + cch >= cbMax ||
|
||
|
*(schar *)PvAddBv(pv, ibT + cch) != 0)
|
||
|
{
|
||
|
goto LFail;
|
||
|
}
|
||
|
_rgch[0] = (achar)CchTranslateRgb(PvAddBv(pv, ibT), cch, osk,
|
||
|
_rgch + 1, kcchMaxStn);
|
||
|
ibT += cch + 1;
|
||
|
|
||
|
cch = (long)(uchar)_rgch[0];
|
||
|
_rgch[cch + 1] = 0;
|
||
|
goto LCheck;
|
||
|
|
||
|
case size(wchar):
|
||
|
if (ibT + 2 * size(wchar) > cbMax)
|
||
|
goto LFail;
|
||
|
CopyPb(PvAddBv(pv, ibT), &chw, size(wchar));
|
||
|
ibT += size(wchar);
|
||
|
|
||
|
if (osk == MacWin(koskUniWin, koskUniMac))
|
||
|
SwapBytesRgsw(&chw, 1);
|
||
|
cch = (long)(ushort)chw;
|
||
|
|
||
|
if (cch > kcchMaxStn || ibT + (cch + 1) * size(wchar) > cbMax)
|
||
|
goto LFail;
|
||
|
CopyPb(PvAddBv(pv, ibT + cch * size(wchar)), &chw, size(wchar));
|
||
|
if (chw != 0)
|
||
|
goto LFail;
|
||
|
_rgch[0] = (achar)CchTranslateRgb(PvAddBv(pv, ibT), cch * size(wchar),
|
||
|
osk, _rgch + 1, kcchMaxStn);
|
||
|
ibT += (cch + 1) * size(wchar);
|
||
|
|
||
|
cch = (long)(uchar)_rgch[0];
|
||
|
_rgch[cch + 1] = 0;
|
||
|
|
||
|
LCheck:
|
||
|
// make sure there aren't any other zeros.
|
||
|
for (ich = 1; ich <= cch; ich++)
|
||
|
{
|
||
|
if (_rgch[ich] == 0)
|
||
|
goto LFail;
|
||
|
}
|
||
|
if (pvNil != pcbRead)
|
||
|
*pcbRead = ibT;
|
||
|
else if (ibT != cbMax)
|
||
|
goto LFail;
|
||
|
AssertThis(0);
|
||
|
return fTrue;
|
||
|
|
||
|
default:
|
||
|
LFail:
|
||
|
PushErc(ercStnRead);
|
||
|
Warn("bad STN data");
|
||
|
SetNil();
|
||
|
TrashVar(pcbRead);
|
||
|
return fFalse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Read a string from a block.
|
||
|
***************************************************************************/
|
||
|
bool STN::FRead(PBLCK pblck, long ib, long *pcbRead)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pblck, 0);
|
||
|
AssertNilOrVarMem(pcbRead);
|
||
|
|
||
|
long cch, ich, ibT, cbT, cbMax;
|
||
|
short osk;
|
||
|
schar chs;
|
||
|
wchar chw;
|
||
|
byte rgb[kcbMaxDataStn];
|
||
|
|
||
|
if (!pblck->FUnpackData())
|
||
|
return fFalse;
|
||
|
cbMax = pblck->Cb();
|
||
|
|
||
|
ibT = ib;
|
||
|
if (cbMax < size(short) + ibT || !pblck->FReadRgb(&osk, size(short), ibT))
|
||
|
goto LFail;
|
||
|
ibT += size(short);
|
||
|
|
||
|
if (osk == koskCur)
|
||
|
{
|
||
|
// no translation needed - read the length prefix
|
||
|
if (cbMax < ibT + size(achar) ||
|
||
|
!pblck->FReadRgb(&_rgch[0], size(achar), ibT))
|
||
|
{
|
||
|
goto LFail;
|
||
|
}
|
||
|
ibT += size(achar);
|
||
|
cch = (long)(uchar)_rgch[0];
|
||
|
if (!FIn(cch, 0, kcchMaxStn + 1))
|
||
|
goto LFail;
|
||
|
|
||
|
// read the rest of the string
|
||
|
cbT = size(achar) * (cch + 1);
|
||
|
if (cbMax < ibT + cbT || !pblck->FReadRgb(_rgch + 1, cbT, ibT))
|
||
|
goto LFail;
|
||
|
ibT += cbT;
|
||
|
|
||
|
// make sure the terminating zero is there
|
||
|
if (_rgch[cch + 1] != 0)
|
||
|
goto LFail;
|
||
|
|
||
|
goto LCheck;
|
||
|
}
|
||
|
|
||
|
switch (CbCharOsk(osk))
|
||
|
{
|
||
|
case size(schar):
|
||
|
if (ibT + 2 > cbMax || !pblck->FReadRgb(&chs, 1, ibT++))
|
||
|
goto LFail;
|
||
|
cch = (long)(byte)chs;
|
||
|
if (cch > kcchMaxStn || ibT + cch >= cbMax ||
|
||
|
!pblck->FReadRgb(rgb, cch + 1, ibT) || rgb[cch] != 0)
|
||
|
{
|
||
|
goto LFail;
|
||
|
}
|
||
|
ibT += cch + 1;
|
||
|
_rgch[0] = (achar)CchTranslateRgb(rgb, cch, osk, _rgch + 1, kcchMaxStn);
|
||
|
|
||
|
cch = (long)(uchar)_rgch[0];
|
||
|
_rgch[cch + 1] = 0;
|
||
|
goto LCheck;
|
||
|
|
||
|
case size(wchar):
|
||
|
if (ibT + 2 * size(wchar) > cbMax ||
|
||
|
!pblck->FReadRgb(&chw, size(wchar), ibT))
|
||
|
{
|
||
|
goto LFail;
|
||
|
}
|
||
|
ibT += size(wchar);
|
||
|
|
||
|
if (osk == MacWin(koskUniWin, koskUniMac))
|
||
|
SwapBytesRgsw(&chw, 1);
|
||
|
cch = (long)(ushort)chw;
|
||
|
|
||
|
if (cch > kcchMaxStn || ibT + (cch + 1) * size(wchar) > cbMax ||
|
||
|
!pblck->FReadRgb(rgb, (cch + 1) * size(wchar), ibT) ||
|
||
|
((wchar *)rgb)[cch] != 0)
|
||
|
{
|
||
|
goto LFail;
|
||
|
}
|
||
|
ibT += (cch + 1) * size(wchar);
|
||
|
_rgch[0] = (achar)CchTranslateRgb(rgb, cch * size(wchar), osk,
|
||
|
_rgch + 1, kcchMaxStn);
|
||
|
|
||
|
cch = (long)(uchar)_rgch[0];
|
||
|
_rgch[cch + 1] = 0;
|
||
|
|
||
|
LCheck:
|
||
|
// make sure there aren't any other zeros.
|
||
|
for (ich = 1; ich <= cch; ich++)
|
||
|
{
|
||
|
if (_rgch[ich] == 0)
|
||
|
goto LFail;
|
||
|
}
|
||
|
if (pvNil != pcbRead)
|
||
|
*pcbRead = ibT;
|
||
|
else if (ibT != cbMax)
|
||
|
goto LFail;
|
||
|
AssertThis(0);
|
||
|
return fTrue;
|
||
|
|
||
|
default:
|
||
|
LFail:
|
||
|
PushErc(ercStnRead);
|
||
|
Warn("bad STN data or read failure");
|
||
|
SetNil();
|
||
|
TrashVar(pcbRead);
|
||
|
return fFalse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Get a zero terminated short string from this string.
|
||
|
***************************************************************************/
|
||
|
void STN::GetSzs(PSZS pszs)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPvCb(pszs, kcchTotSz);
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
#ifdef WIN
|
||
|
long cchs = WideCharToMultiByte(CP_ACP, 0, _rgch + 1, -1, pszs, kcchMaxSz,
|
||
|
pvNil, pvNil);
|
||
|
|
||
|
pszs[cchs] = 0;
|
||
|
#endif //WIN
|
||
|
#ifdef MAC
|
||
|
RawRtn(); //REVIEW shonk: Mac: implement
|
||
|
pszs[0] = 0;
|
||
|
#endif //MAC
|
||
|
#else //!UNICODE
|
||
|
CopyPb(_rgch + 1, pszs, Cch() + 1);
|
||
|
#endif //UNICODE
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Format this string using the given template string and any additional
|
||
|
parameters (ala sprintf). All parameters are assumed to be 4 bytes long.
|
||
|
|
||
|
Returns false if the string ended up being too long to fit in an stn.
|
||
|
The following controls are supported:
|
||
|
|
||
|
%c (long)achar
|
||
|
%s pstn
|
||
|
%z psz
|
||
|
%d signed decimal (long)
|
||
|
%u unsigned decimal (long)
|
||
|
%x hex
|
||
|
%f long as a 4 character value: 'xxxx' (ala FTG and CTG values)
|
||
|
%% a percent sign
|
||
|
|
||
|
Supports the following options, in this order:
|
||
|
|
||
|
Argument reordering ('<', 0 based decimal number, '>')
|
||
|
Left justify ('-')
|
||
|
Explicit plus sign ('+')
|
||
|
Zero padding instead of space padding ('0')
|
||
|
Minimum field width (decimal number)
|
||
|
|
||
|
These all go between the '%' and the control letter. Note that argument
|
||
|
reordering affects everything after the reordered arg in the control
|
||
|
string. Eg, "%<1>d %<0>d %d %d" will use arguments in the order
|
||
|
{ 1, 0, 1, 2 }. If you just want to switch two arguments, the one
|
||
|
following needs a number also. So the above example would be
|
||
|
"%<1>d %<0>d %<2>d %d", producing { 1, 0, 2, 3 }.
|
||
|
|
||
|
WARNING: all arguments should be 4 bytes long.
|
||
|
***************************************************************************/
|
||
|
bool STN::FFormat(PSTN pstnFormat, ...)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pstnFormat, 0);
|
||
|
|
||
|
return FFormatRgch(pstnFormat->Prgch(), pstnFormat->Cch(),
|
||
|
(ulong *)(&pstnFormat + 1));
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
See comments for STN::FFormat
|
||
|
***************************************************************************/
|
||
|
bool STN::FFormatSz(PSZ pszFormat, ...)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertSz(pszFormat);
|
||
|
|
||
|
return FFormatRgch(pszFormat, CchSz(pszFormat), (ulong *)(&pszFormat + 1));
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Core routine for sprintf functionality.
|
||
|
***************************************************************************/
|
||
|
bool STN::FFormatRgch(achar *prgchFormat, long cchFormat, ulong *prgluData)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cchFormat, 0, kcchMaxStn + 1);
|
||
|
AssertPvCb(prgchFormat, cchFormat * size(achar));
|
||
|
AssertVarMem(prgluData);
|
||
|
|
||
|
//Data Write Order - these dwo values are pcode for when to add what
|
||
|
enum
|
||
|
{
|
||
|
dwoPadFirst = 0x0321,
|
||
|
dwoPadSecond = 0x0312,
|
||
|
dwoPadLast = 0x0132
|
||
|
};
|
||
|
achar *pchOut, *pchOutLim;
|
||
|
achar *pchIn, *pchInLim;
|
||
|
long cch;
|
||
|
long cchMin;
|
||
|
long ivArg;
|
||
|
ulong lu, luRad;
|
||
|
achar ch;
|
||
|
achar rgchT[kcchMaxStn];
|
||
|
achar *prgchTerm;
|
||
|
achar chSign, chPad;
|
||
|
ulong dwo;
|
||
|
PSTN pstn;
|
||
|
bool fRet = fFalse;
|
||
|
|
||
|
pchInLim = (pchIn = prgchFormat) + cchFormat;
|
||
|
pchOutLim = (pchOut = _rgch + 1) + kcchMaxStn;
|
||
|
ivArg = 0;
|
||
|
while (pchIn < pchInLim && pchOut < pchOutLim)
|
||
|
{
|
||
|
if ((ch = *pchIn++) != ChLit('%'))
|
||
|
{
|
||
|
*pchOut++ = ch;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//pre-fetch the next character
|
||
|
if (pchIn >= pchInLim)
|
||
|
goto LBug;
|
||
|
ch = *pchIn++;
|
||
|
if (ch == ChLit('%'))
|
||
|
{
|
||
|
*pchOut++ = ChLit('%');
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
dwo = dwoPadFirst;
|
||
|
chSign = 0;
|
||
|
chPad = kchSpace;
|
||
|
|
||
|
if (ch == ChLit('<'))
|
||
|
{
|
||
|
ivArg = 0;
|
||
|
for (;;)
|
||
|
{
|
||
|
if (ch < ChLit('0') || ch > ChLit('9'))
|
||
|
break;
|
||
|
ivArg = ivArg * 10 + ch - ChLit('0');
|
||
|
|
||
|
//pre-fetch the next character
|
||
|
if (pchIn >= pchInLim)
|
||
|
goto LBug;
|
||
|
ch = *pchIn++;
|
||
|
}
|
||
|
if (ch != ChLit('>'))
|
||
|
goto LBug;
|
||
|
}
|
||
|
|
||
|
//get qualifiers (print sign, left justify, etc)
|
||
|
for (;;)
|
||
|
{
|
||
|
switch (ch)
|
||
|
{
|
||
|
default:
|
||
|
goto LGetCchMin;
|
||
|
case ChLit('-'):
|
||
|
dwo = dwoPadLast;
|
||
|
break;
|
||
|
case ChLit('+'):
|
||
|
chSign = ChLit('+');
|
||
|
break;
|
||
|
case ChLit('0'):
|
||
|
chPad = ChLit('0');
|
||
|
dwo = dwoPadSecond;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//pre-fetch the next character
|
||
|
if (pchIn >= pchInLim)
|
||
|
goto LBug;
|
||
|
ch = *pchIn++;
|
||
|
}
|
||
|
|
||
|
LGetCchMin:
|
||
|
cchMin = 0;
|
||
|
for (;;)
|
||
|
{
|
||
|
if (ch < ChLit('0') || ch > ChLit('9'))
|
||
|
break;
|
||
|
cchMin = cchMin * 10 + ch - ChLit('0');
|
||
|
|
||
|
//pre-fetch the next character
|
||
|
if (pchIn >= pchInLim)
|
||
|
goto LBug;
|
||
|
ch = *pchIn++;
|
||
|
}
|
||
|
|
||
|
//code after the switch assumes that prgchTerm points to the
|
||
|
//characters to add to the stream and cch is the number of characters
|
||
|
AssertPvCb(prgluData, LwMul(ivArg + 1, size(ulong)));
|
||
|
lu = prgluData[ivArg++];
|
||
|
prgchTerm = rgchT;
|
||
|
switch (ch)
|
||
|
{
|
||
|
case ChLit('c'):
|
||
|
rgchT[0] = (achar)lu;
|
||
|
cch = 1;
|
||
|
break;
|
||
|
|
||
|
case ChLit('s'):
|
||
|
pstn = (PSTN)lu;
|
||
|
AssertPo(pstn, 0);
|
||
|
prgchTerm = pstn->Prgch();
|
||
|
cch = pstn->Cch();
|
||
|
break;
|
||
|
|
||
|
case ChLit('z'):
|
||
|
AssertSz((achar *)lu);
|
||
|
prgchTerm = (achar *)lu;
|
||
|
cch = CchSz(prgchTerm);
|
||
|
break;
|
||
|
|
||
|
case ChLit('f'):
|
||
|
for (cch = 4; cch-- > 0; lu >>= 8)
|
||
|
{
|
||
|
ch = (achar)(byte)lu;
|
||
|
if (0 == ch)
|
||
|
ch = 1;
|
||
|
rgchT[cch] = ch;
|
||
|
}
|
||
|
cch = 4;
|
||
|
break;
|
||
|
|
||
|
case ChLit('x'):
|
||
|
//if cchMin is not 0, don't make it longer than cchMin
|
||
|
if (cchMin > 0 && cchMin < 8)
|
||
|
lu &= (1L << (cchMin * 4)) - 1;
|
||
|
luRad = 16;
|
||
|
goto LUnsigned;
|
||
|
|
||
|
case ChLit('d'):
|
||
|
if ((long)lu < 0)
|
||
|
{
|
||
|
chSign = ChLit('-');
|
||
|
lu = -(long)lu;
|
||
|
}
|
||
|
luRad = 10;
|
||
|
goto LUnsigned;
|
||
|
|
||
|
case ChLit('u'):
|
||
|
luRad = 10;
|
||
|
LUnsigned:
|
||
|
prgchTerm = rgchT + CvFromRgv(rgchT);
|
||
|
cch = 0;
|
||
|
do
|
||
|
{
|
||
|
*--prgchTerm = vrgchHex[lu % luRad];
|
||
|
cch++;
|
||
|
lu /= luRad;
|
||
|
}
|
||
|
while (lu != 0);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
LBug:
|
||
|
Bug("bad format string");
|
||
|
goto LFail;
|
||
|
}
|
||
|
|
||
|
//set cchMin to the number of characters to pad
|
||
|
cchMin = LwMax(0, cchMin - cch - (chSign != 0));
|
||
|
if (pchOutLim - pchOut <= cch + cchMin + (chSign != 0))
|
||
|
{
|
||
|
//overflowed the output buffer
|
||
|
goto LFail;
|
||
|
}
|
||
|
|
||
|
//arrange the sign, padding and rgch according to dwo
|
||
|
while (dwo != 0)
|
||
|
{
|
||
|
switch (dwo & 0x0F)
|
||
|
{
|
||
|
case 1: //add padding
|
||
|
for ( ; cchMin > 0; cchMin--)
|
||
|
*pchOut++ = chPad;
|
||
|
break;
|
||
|
|
||
|
case 2: //add the sign
|
||
|
if (chSign != 0)
|
||
|
*pchOut++ = chSign;
|
||
|
break;
|
||
|
|
||
|
case 3: //add the text
|
||
|
CopyPb(prgchTerm, pchOut, cch * size(achar));
|
||
|
pchOut += cch;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
BugVar("bad dwo value", &dwo);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
dwo >>= 4;
|
||
|
}
|
||
|
|
||
|
Assert(pchOut <= pchOutLim, "bad logic above - overflowed the rgch");
|
||
|
}
|
||
|
|
||
|
fRet = pchIn == pchInLim;
|
||
|
LFail:
|
||
|
cch = pchOut - _rgch - 1;
|
||
|
AssertIn(cch, 0, kcchMaxStn + 1);
|
||
|
_rgch[0] = (achar)cch;
|
||
|
_rgch[cch + 1] = 0;
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Parses the STN as a number. If lwBase is 0, automatically determines
|
||
|
the base as one of 10, 8 or 16 (as in standard C) and allows leading
|
||
|
spaces, '+' and '-' signs, and trailing spaces. Doesn't deal with
|
||
|
overflow.
|
||
|
***************************************************************************/
|
||
|
bool STN::FGetLw(long *plw, long lwBase)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(plw);
|
||
|
AssertIn(lwBase, 0, 36);
|
||
|
Assert(lwBase != 1, "base can't be 1");
|
||
|
long lwDigit;
|
||
|
achar ch;
|
||
|
bool fNegative = fFalse;
|
||
|
PSZ psz = Psz();
|
||
|
|
||
|
if (lwBase < 2)
|
||
|
{
|
||
|
for ( ; ; psz++)
|
||
|
{
|
||
|
switch (*psz)
|
||
|
{
|
||
|
case ChLit('-'):
|
||
|
fNegative = !fNegative;
|
||
|
continue;
|
||
|
|
||
|
case ChLit('+'):
|
||
|
case kchSpace:
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*plw = 0;
|
||
|
if (*psz == 0)
|
||
|
goto LFail;
|
||
|
|
||
|
//determine the base if lwBase is zero
|
||
|
if (0 == lwBase)
|
||
|
{
|
||
|
//determine the base
|
||
|
if (ChLit('0') == psz[0])
|
||
|
{
|
||
|
psz++;
|
||
|
if (ChLit('x') == psz[0] || ChLit('X') == psz[0])
|
||
|
{
|
||
|
psz++;
|
||
|
lwBase = 16;
|
||
|
}
|
||
|
else
|
||
|
lwBase = 8;
|
||
|
}
|
||
|
else
|
||
|
lwBase = 10;
|
||
|
}
|
||
|
|
||
|
while (0 != (ch = *psz++))
|
||
|
{
|
||
|
if (FIn(ch, ChLit('0'), ChLit('9') + 1))
|
||
|
lwDigit = ch - ChLit('0');
|
||
|
else
|
||
|
{
|
||
|
if (FIn(ch, ChLit('A'), ChLit('Z') + 1))
|
||
|
ch += ChLit('a') - ChLit('A');
|
||
|
else if (!FIn(ch, ChLit('a'), ChLit('z') + 1))
|
||
|
{
|
||
|
// not a letter, so only spaces should follow
|
||
|
for ( ; ch != 0; ch = *psz++)
|
||
|
{
|
||
|
if (ch != kchSpace)
|
||
|
goto LFail;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
lwDigit = ch - ChLit('a') + 10;
|
||
|
}
|
||
|
|
||
|
if (lwDigit > lwBase)
|
||
|
{
|
||
|
LFail:
|
||
|
TrashVar(plw);
|
||
|
return fFalse;
|
||
|
}
|
||
|
*plw = *plw * lwBase + lwDigit;
|
||
|
}
|
||
|
|
||
|
if (fNegative)
|
||
|
*plw = -*plw;
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Doubles any backslash characters in the string and replaces " literals
|
||
|
with \".
|
||
|
***************************************************************************/
|
||
|
bool STN::FExpandControls(void)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
achar rgch[kcchMaxStn];
|
||
|
achar *pchSrc, *pchDst, *pchLim, ch;
|
||
|
long cch;
|
||
|
bool fAny;
|
||
|
bool fRet = fFalse;
|
||
|
|
||
|
fAny = fFalse;
|
||
|
pchSrc = Psz();
|
||
|
pchDst = rgch;
|
||
|
pchLim = pchDst + kcchMaxStn;
|
||
|
for (cch = Cch(); cch-- > 0; )
|
||
|
{
|
||
|
if (pchDst >= pchLim)
|
||
|
goto LFail;
|
||
|
ch = *pchSrc++;
|
||
|
switch (ch)
|
||
|
{
|
||
|
case kchTab:
|
||
|
ch = ChLit('t');
|
||
|
goto LExpand;
|
||
|
case kchReturn:
|
||
|
ch = ChLit('n');
|
||
|
goto LExpand;
|
||
|
case ChLit('\\'):
|
||
|
case ChLit('"'):
|
||
|
LExpand:
|
||
|
if (pchDst + 1 >= pchLim)
|
||
|
goto LFail;
|
||
|
*pchDst++ = ChLit('\\');
|
||
|
fAny = fTrue;
|
||
|
break;
|
||
|
}
|
||
|
*pchDst++ = ch;
|
||
|
}
|
||
|
fRet = fTrue;
|
||
|
|
||
|
LFail:
|
||
|
if (fAny)
|
||
|
SetRgch(rgch, pchDst - rgch);
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/***************************************************************************
|
||
|
Assert the validity of a STN.
|
||
|
***************************************************************************/
|
||
|
void STN::AssertValid(ulong grf)
|
||
|
{
|
||
|
AssertThisMem();
|
||
|
|
||
|
achar *pch;
|
||
|
long cch = (long)(uchar)_rgch[0];
|
||
|
|
||
|
AssertIn(cch, 0, kcchMaxStn + 1);
|
||
|
Assert(_rgch[cch + 1] == 0, "missing termination byte");
|
||
|
|
||
|
for (pch = _rgch + 1; *pch != 0; pch++)
|
||
|
;
|
||
|
|
||
|
Assert(pch - _rgch == cch + 1, "internal null characters");
|
||
|
}
|
||
|
#endif //DEBUG
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Check the validity of an st (make sure no zeros are in it).
|
||
|
***************************************************************************/
|
||
|
bool FValidSt(PST pst)
|
||
|
{
|
||
|
AssertVarMem(pst);
|
||
|
achar *pch;
|
||
|
long cch = (long)(uchar)pst[0];
|
||
|
|
||
|
if (!FIn(cch, 0, kcchMaxSt + 1))
|
||
|
return fFalse;
|
||
|
|
||
|
AssertPvCb(pst, (cch + kcchExtraSt) * size(achar));
|
||
|
for (pch = PrgchSt(pst); cch > 0 && *pch != 0; cch--, pch++)
|
||
|
;
|
||
|
return (cch == 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Check the validity of an stz
|
||
|
***************************************************************************/
|
||
|
bool FValidStz(PSTZ pstz)
|
||
|
{
|
||
|
AssertVarMem(pstz);
|
||
|
return FValidSt(pstz) && 0 == PszStz(pstz)[CchSt(pstz)];
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Find the length of a zero terminated string.
|
||
|
***************************************************************************/
|
||
|
long CchSz(PSZ psz)
|
||
|
{
|
||
|
//WARNING: don't call AssertSz, since AssertSz calls CchSz!
|
||
|
AssertVarMem(psz);
|
||
|
achar *pch;
|
||
|
|
||
|
for (pch = psz; *pch != 0; pch++)
|
||
|
;
|
||
|
Assert(pch - psz <= kcchMaxSz, "sz too long");
|
||
|
AssertPvCb(psz, (pch - psz + 1) * size(achar));
|
||
|
return pch - psz;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Do string equality testing. This does byte-wise comparison for
|
||
|
internal (non-user) use only!
|
||
|
***************************************************************************/
|
||
|
bool FEqualRgch(achar *prgch1, long cch1, achar *prgch2, long cch2)
|
||
|
{
|
||
|
AssertIn(cch1, 0, kcbMax);
|
||
|
AssertPvCb(prgch1, cch1 * size(achar));
|
||
|
AssertIn(cch2, 0, kcbMax);
|
||
|
AssertPvCb(prgch2, cch2 * size(achar));
|
||
|
|
||
|
return cch1 == cch2 && FEqualRgb(prgch1, prgch2, cch1 * size(achar));
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Do string comparison for sorting. This is byte-wise for internal
|
||
|
(non-user) sorting only! The sorting is byte-order independent.
|
||
|
fcmpLt means that string 1 is less than string 2.
|
||
|
***************************************************************************/
|
||
|
ulong FcmpCompareRgch(achar *prgch1, long cch1, achar *prgch2, long cch2)
|
||
|
{
|
||
|
AssertIn(cch1, 0, kcbMax);
|
||
|
AssertPvCb(prgch1, cch1 * size(achar));
|
||
|
AssertIn(cch2, 0, kcbMax);
|
||
|
AssertPvCb(prgch2, cch2 * size(achar));
|
||
|
long ich, cbTot, cbMatch;
|
||
|
|
||
|
if (cch1 < cch2)
|
||
|
return fcmpLt;
|
||
|
if (cch1 > cch2)
|
||
|
return fcmpGt;
|
||
|
|
||
|
cbTot = cch1 * size(achar);
|
||
|
cbMatch = CbEqualRgb(prgch1, prgch2, cbTot);
|
||
|
AssertIn(cbMatch, 0, cbTot + 1);
|
||
|
if (cbTot == cbMatch)
|
||
|
return fcmpEq;
|
||
|
|
||
|
ich = cbMatch / size(achar);
|
||
|
Assert(prgch1[ich] != prgch2[ich], 0);
|
||
|
|
||
|
return prgch1[ich] < prgch2[ich] ? fcmpLt : fcmpGt;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
User level equality testing of strings.
|
||
|
***************************************************************************/
|
||
|
bool FEqualUserRgch(achar *prgch1, long cch1, achar *prgch2, long cch2,
|
||
|
ulong grfstn)
|
||
|
{
|
||
|
AssertIn(cch1, 0, kcbMax);
|
||
|
AssertPvCb(prgch1, cch1 * size(achar));
|
||
|
AssertIn(cch2, 0, kcbMax);
|
||
|
AssertPvCb(prgch2, cch2 * size(achar));
|
||
|
long cchBuf;
|
||
|
achar rgch1[kcchMaxStn];
|
||
|
achar rgch2[kcchMaxStn];
|
||
|
|
||
|
//REVIEW shonk: implement for real
|
||
|
if (!(grfstn & fstnIgnoreCase))
|
||
|
return cch1 == cch2 && FEqualRgb(prgch1, prgch2, cch1 * size(achar));
|
||
|
|
||
|
if (cch1 != cch2)
|
||
|
return fFalse;
|
||
|
|
||
|
while (cch1 > 0)
|
||
|
{
|
||
|
cchBuf = LwMin(kcchMaxStn, cch1);
|
||
|
CopyPb(prgch1, rgch1, cchBuf * size(achar));
|
||
|
CopyPb(prgch2, rgch2, cchBuf * size(achar));
|
||
|
#ifdef WIN
|
||
|
CharUpperBuff(rgch1, cchBuf);
|
||
|
CharUpperBuff(rgch2, cchBuf);
|
||
|
#else //!WIN
|
||
|
RawRtn(); //REVIEW shonk: Mac: implement
|
||
|
#endif //!WIN
|
||
|
if (!FEqualRgb(rgch1, rgch2, cchBuf * size(achar)))
|
||
|
return fFalse;
|
||
|
prgch1 += cchBuf;
|
||
|
prgch2 += cchBuf;
|
||
|
cch1 -= cchBuf;
|
||
|
}
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Do user level string comparison with the given options.
|
||
|
***************************************************************************/
|
||
|
ulong FcmpCompareUserRgch(achar *prgch1, long cch1, achar *prgch2, long cch2,
|
||
|
ulong grfstn)
|
||
|
{
|
||
|
AssertIn(cch1, 0, kcbMax);
|
||
|
AssertPvCb(prgch1, cch1 * size(achar));
|
||
|
AssertIn(cch2, 0, kcbMax);
|
||
|
AssertPvCb(prgch2, cch2 * size(achar));
|
||
|
|
||
|
#ifdef WIN
|
||
|
long lw;
|
||
|
|
||
|
lw = CompareString(LOCALE_USER_DEFAULT,
|
||
|
(grfstn & fstnIgnoreCase) ? NORM_IGNORECASE : 0,
|
||
|
prgch1, cch1, prgch2, cch2);
|
||
|
switch (lw)
|
||
|
{
|
||
|
case 1:
|
||
|
return fcmpLt;
|
||
|
case 2:
|
||
|
return fcmpEq;
|
||
|
case 3:
|
||
|
return fcmpGt;
|
||
|
default:
|
||
|
Bug("why did CompareString fail?");
|
||
|
return FcmpCompareRgch(prgch1, cch1, prgch2, cch2);
|
||
|
}
|
||
|
#else //!WIN
|
||
|
RawRtn(); //REVIEW shonk: Mac: implement for real
|
||
|
return FcmpCompareRgch(prgch1, cch1, prgch2, cch2);
|
||
|
#endif //!WIN
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Map an array of short characters to upper case equivalents.
|
||
|
***************************************************************************/
|
||
|
void UpperRgchs(schar *prgchs, long cchs)
|
||
|
{
|
||
|
AssertIn(cchs, 0, kcbMax);
|
||
|
AssertPvCb(prgchs, cchs);
|
||
|
long ichs;
|
||
|
static bool _fInited;
|
||
|
|
||
|
if (!_fInited)
|
||
|
{
|
||
|
for (ichs = 0; ichs < 256; ichs++)
|
||
|
_mpchschsUpper[ichs] = (byte)ichs;
|
||
|
MacWin(
|
||
|
UppercaseText(_mpchschsUpper, 256, smSystemScript),
|
||
|
CharUpperBuffA(_mpchschsUpper, 256));
|
||
|
_fInited = fTrue;
|
||
|
}
|
||
|
|
||
|
for ( ; cchs-- != 0; prgchs++)
|
||
|
*prgchs = _mpchschsUpper[(byte)*prgchs];
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Map an array of characters to lower case equivalents.
|
||
|
***************************************************************************/
|
||
|
void LowerRgchs(schar *prgchs, long cchs)
|
||
|
{
|
||
|
AssertIn(cchs, 0, kcbMax);
|
||
|
AssertPvCb(prgchs, cchs);
|
||
|
long ichs;
|
||
|
static bool _fInited;
|
||
|
|
||
|
if (!_fInited)
|
||
|
{
|
||
|
for (ichs = 0; ichs < 256; ichs++)
|
||
|
_mpchschsLower[ichs] = (byte)ichs;
|
||
|
MacWin(
|
||
|
LowercaseText(_mpchschsLower, 256, smSystemScript),
|
||
|
CharLowerBuffA(_mpchschsLower, 256));
|
||
|
_fInited = fTrue;
|
||
|
}
|
||
|
|
||
|
for ( ; cchs-- != 0; prgchs++)
|
||
|
*prgchs = _mpchschsLower[(byte)*prgchs];
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Map an array of unicode characters to upper case equivalents.
|
||
|
***************************************************************************/
|
||
|
void UpperRgchw(wchar *prgchw, long cchw)
|
||
|
{
|
||
|
AssertIn(cchw, 0, kcbMax);
|
||
|
AssertPvCb(prgchw, cchw * size(wchar));
|
||
|
|
||
|
#ifdef WIN
|
||
|
CharUpperBuffW(prgchw, cchw);
|
||
|
#else //!WIN
|
||
|
RawRtn(); //REVIEW shonk: Mac: implement UpperRgchw
|
||
|
#endif //!WIN
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Map an array of unicode characters to lower case equivalents.
|
||
|
***************************************************************************/
|
||
|
void LowerRgchw(wchar *prgchw, long cchw)
|
||
|
{
|
||
|
AssertIn(cchw, 0, kcbMax);
|
||
|
AssertPvCb(prgchw, cchw * size(wchar));
|
||
|
|
||
|
AssertIn(cchw, 0, kcbMax);
|
||
|
AssertPvCb(prgchw, cchw * size(wchar));
|
||
|
|
||
|
#ifdef WIN
|
||
|
CharLowerBuffW(prgchw, cchw);
|
||
|
#else //!WIN
|
||
|
RawRtn(); //REVIEW shonk: Mac: implement UpperRgchw
|
||
|
#endif //!WIN
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Translate text from the indicated source character set to koskCur.
|
||
|
***************************************************************************/
|
||
|
long CchTranslateRgb(void *pvSrc, long cbSrc, short oskSrc,
|
||
|
achar *prgchDst, long cchMaxDst)
|
||
|
{
|
||
|
AssertPvCb(pvSrc, cbSrc);
|
||
|
AssertOsk(oskSrc);
|
||
|
AssertPvCb(prgchDst, cchMaxDst * size(achar));
|
||
|
long cchT;
|
||
|
|
||
|
#ifdef WIN
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
switch (oskSrc)
|
||
|
{
|
||
|
default:
|
||
|
return 0;
|
||
|
|
||
|
case koskSbWin:
|
||
|
case koskSbMac:
|
||
|
return MultiByteToWideChar(oskSrc == koskSbWin ? CP_ACP : CP_MACCP,
|
||
|
MB_PRECOMPOSED, (LPSTR)pvSrc, cbSrc, prgchDst, cchMaxDst);
|
||
|
|
||
|
case koskUniMac:
|
||
|
if (cbSrc % size(wchar) != 0)
|
||
|
return 0;
|
||
|
|
||
|
cchT = cbSrc / size(wchar);
|
||
|
if (0 == cchMaxDst)
|
||
|
return cchT;
|
||
|
if (cchT > cchMaxDst)
|
||
|
return 0;
|
||
|
|
||
|
CopyPb(pvSrc, prgchDst, cchT * size(achar));
|
||
|
SwapBytesRgsw(prgchDst, cchT);
|
||
|
return cchT;
|
||
|
}
|
||
|
|
||
|
#else //!UNICODE
|
||
|
|
||
|
achar *pchSrc, *pchDst;
|
||
|
|
||
|
switch (oskSrc)
|
||
|
{
|
||
|
default:
|
||
|
return 0;
|
||
|
|
||
|
case koskSbMac:
|
||
|
if (0 == cchMaxDst)
|
||
|
return cbSrc;
|
||
|
if (cbSrc > cchMaxDst)
|
||
|
return 0;
|
||
|
|
||
|
pchSrc = (achar *)pvSrc;
|
||
|
for (pchDst = prgchDst, cchT = cbSrc; cchT > 0; cchT--)
|
||
|
{
|
||
|
byte bT = (byte)*pchSrc++;
|
||
|
if (bT >= (byte)0x80)
|
||
|
*pchDst++ = _mpchschsMacToWin[bT - (byte)0x80];
|
||
|
else
|
||
|
*pchDst++ = (achar)bT;
|
||
|
}
|
||
|
return cbSrc;
|
||
|
|
||
|
case koskUniWin:
|
||
|
case koskUniMac:
|
||
|
if (cbSrc % size(wchar) != 0)
|
||
|
return 0;
|
||
|
|
||
|
// swap byte order
|
||
|
if (oskSrc == koskUniMac)
|
||
|
SwapBytesRgsw(pvSrc, cbSrc / size(wchar));
|
||
|
|
||
|
cchT = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)pvSrc, cbSrc / size(wchar),
|
||
|
prgchDst, cchMaxDst, pvNil, pvNil);
|
||
|
|
||
|
if (oskSrc == koskUniMac)
|
||
|
SwapBytesRgsw(pvSrc, cbSrc / size(wchar));
|
||
|
return cchT;
|
||
|
}
|
||
|
|
||
|
#endif //!UNICODE
|
||
|
#endif //WIN
|
||
|
|
||
|
#ifdef MAC
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
long cchDst;
|
||
|
schar *pchsSrc;
|
||
|
achar *pchDst;
|
||
|
|
||
|
switch (oskSrc)
|
||
|
{
|
||
|
default:
|
||
|
return 0;
|
||
|
|
||
|
case koskSbWin:
|
||
|
if (0 == cchMaxDst)
|
||
|
return cbSrc;
|
||
|
if (cbSrc > cchMaxDst)
|
||
|
return 0;
|
||
|
|
||
|
pchsSrc = (schar *)pvSrc;
|
||
|
for (pchDst = prgch, cchT = cbSrc; cchT > 0; cchT--)
|
||
|
*pchDst++ = (achar)(byte)*pchsSrc++;
|
||
|
return cbSrc;
|
||
|
|
||
|
case koskSbMac:
|
||
|
if (0 == cchMaxDst)
|
||
|
return cbSrc;
|
||
|
if (cbSrc > cchMaxDst)
|
||
|
return 0;
|
||
|
|
||
|
pchsSrc = (schar *)pvSrc;
|
||
|
for (pchDst = prgch, cchT = cbSrc; cchT > 0; cchT--)
|
||
|
pchsSrc = (schar *)pvSrc;
|
||
|
{
|
||
|
byte bT = (byte)*pchsSrc++;
|
||
|
if (bT >= (byte)0x80)
|
||
|
*pchDst++ = (achar)_mpchschsMacToWin[bT - (byte)0x80];
|
||
|
else
|
||
|
*pchDst++ = (achar)bT;
|
||
|
}
|
||
|
return cbSrc;
|
||
|
|
||
|
case koskUniWin:
|
||
|
if (cbSrc % size(wchar) != 0)
|
||
|
return 0;
|
||
|
|
||
|
cchT = cbSrc / size(wchar);
|
||
|
if (0 == cchMaxDst)
|
||
|
return cchT;
|
||
|
if (cchT > cchMaxDst)
|
||
|
return 0;
|
||
|
|
||
|
CopyPb(pvSrc, prgchDst, cchT * size(achar));
|
||
|
SwapBytesRgsw(prgchDst, cchT);
|
||
|
return cchT;
|
||
|
}
|
||
|
|
||
|
#else //!UNICODE
|
||
|
|
||
|
achar *pchSrc, *pchDst;
|
||
|
|
||
|
switch (oskSrc)
|
||
|
{
|
||
|
default:
|
||
|
return fFalse;
|
||
|
|
||
|
case koskSbWin:
|
||
|
if (0 == cchMaxDst)
|
||
|
return cbSrc;
|
||
|
if (cbSrc > cchMaxDst)
|
||
|
return 0;
|
||
|
|
||
|
pchSrc = (achar *)pvSrc;
|
||
|
for (pchDst = prgchDst, cchT = cbSrc; cchT > 0; cchT--)
|
||
|
{
|
||
|
byte bT = (byte)*pchSrc++;
|
||
|
if (bT >= (byte)0x80)
|
||
|
*pchDst++ = _mpchschsWinToMac[bT - (byte)0x80];
|
||
|
else
|
||
|
*pchDst++ = (achar)bT;
|
||
|
}
|
||
|
return cbSrc;
|
||
|
|
||
|
case koskUniWin:
|
||
|
case koskUniMac:
|
||
|
RawRtn(); //REVIEW shonk: Mac: implement koskUniWin, koskUniMac -> koskSbMac
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif //!UNICODE
|
||
|
#endif //MAC
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Translates a string between the current platform and another. If fToCur
|
||
|
is true, the translation is from osk to koskCur, otherwise from koskCur
|
||
|
to osk.
|
||
|
***************************************************************************/
|
||
|
void TranslateRgch(achar *prgch, long cch, short osk, bool fToCur)
|
||
|
{
|
||
|
AssertPvCb(prgch, cch);
|
||
|
AssertOsk(osk);
|
||
|
Assert(osk == koskMac || osk == koskWin,
|
||
|
"TranslateRgch can't handle this osk");
|
||
|
|
||
|
if (koskCur == osk)
|
||
|
return;
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
// for unicode, we just have to change the byte ordering
|
||
|
SwapBytesRgsw(prgch, cch);
|
||
|
#else //!UNICODE
|
||
|
achar *pmpchschs = MacWin(!fToCur, fToCur) ? _mpchschsMacToWin :
|
||
|
_mpchschsWinToMac;
|
||
|
|
||
|
for ( ; cch > 0; cch--, prgch++)
|
||
|
{
|
||
|
if ((byte)*prgch >= (byte)0x80)
|
||
|
*prgch = pmpchschs[(byte)*prgch - (byte)0x80];
|
||
|
}
|
||
|
#endif //!UNICODE
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return the type of the character.
|
||
|
This must follow these implications:
|
||
|
fchBreak -> fchMayBreak
|
||
|
fchBreak -> fchControl
|
||
|
fchIgnore -> fchControl
|
||
|
|
||
|
REVIEW shonk: make GrfchFromCh handle all unicode characters.
|
||
|
***************************************************************************/
|
||
|
ulong GrfchFromCh(achar ch)
|
||
|
{
|
||
|
switch ((uchar)ch)
|
||
|
{
|
||
|
case kchReturn:
|
||
|
return fchBreak | fchMayBreak | fchControl;
|
||
|
|
||
|
case kchLineFeed:
|
||
|
return fchIgnore | fchControl;
|
||
|
|
||
|
case kchTab:
|
||
|
return fchTab | fchMayBreak | fchControl;
|
||
|
|
||
|
case kchSpace:
|
||
|
return fchWhiteOverhang | fchMayBreak;
|
||
|
|
||
|
#ifdef REVIEW //shonk: implement correctly once we get the tables from MSKK.
|
||
|
#ifdef UNICODE
|
||
|
//REVIEW shonk: others? 148
|
||
|
case (uchar)',':
|
||
|
case (uchar)'.':
|
||
|
case (uchar)')':
|
||
|
case (uchar)']':
|
||
|
case (uchar)'}':
|
||
|
case (uchar)':':
|
||
|
case (uchar)';':
|
||
|
case (uchar)'?':
|
||
|
case (uchar)'!':
|
||
|
case (uchar)'.':
|
||
|
case (uchar)',':
|
||
|
return fchTestBreak;
|
||
|
#endif //UNICODE
|
||
|
#endif //REVIEW
|
||
|
|
||
|
case 0x7F:
|
||
|
return fchControl;
|
||
|
|
||
|
default:
|
||
|
if ((uchar)ch < kchSpace)
|
||
|
return fchControl;
|
||
|
|
||
|
return fchNil;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/***************************************************************************
|
||
|
Validate an osk.
|
||
|
***************************************************************************/
|
||
|
void AssertOsk(short osk)
|
||
|
{
|
||
|
switch (osk)
|
||
|
{
|
||
|
case koskSbMac:
|
||
|
case koskSbWin:
|
||
|
case koskUniMac:
|
||
|
case koskUniWin:
|
||
|
break;
|
||
|
default:
|
||
|
BugVar("bad osk", &osk);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Check the validity of an st (make sure no zeros are in it).
|
||
|
***************************************************************************/
|
||
|
void AssertSt(PST pst)
|
||
|
{
|
||
|
Assert(FValidSt(pst), "bad st");
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Check the validity of an stz.
|
||
|
***************************************************************************/
|
||
|
void AssertStz(PSTZ pstz)
|
||
|
{
|
||
|
Assert(FValidStz(pstz), "bad stz");
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Make sure the sz isn't too long.
|
||
|
***************************************************************************/
|
||
|
void AssertSz(PSZ psz)
|
||
|
{
|
||
|
// CchSz does all the asserting we need
|
||
|
long cch = CchSz(psz);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Check the validity of an st, nil is allowed.
|
||
|
***************************************************************************/
|
||
|
void AssertNilOrSt(PST pst)
|
||
|
{
|
||
|
if (pst != pvNil)
|
||
|
AssertSt(pst);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Check the validity of an stz, nil is allowed.
|
||
|
***************************************************************************/
|
||
|
void AssertNilOrStz(PSTZ pstz)
|
||
|
{
|
||
|
if (pstz != pvNil)
|
||
|
AssertStz(pstz);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Check the validity of an sz, nil is allowed.
|
||
|
***************************************************************************/
|
||
|
void AssertNilOrSz(PSZ psz)
|
||
|
{
|
||
|
if (psz != pvNil)
|
||
|
AssertSz(psz);
|
||
|
}
|
||
|
#endif //DEBUG
|