You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
452 lines
10 KiB
452 lines
10 KiB
/*
|
|
* X E M I T . C P P
|
|
*
|
|
* XML emitter processing
|
|
*
|
|
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
|
|
*/
|
|
|
|
#include "_xml.h"
|
|
#include <szsrc.h>
|
|
|
|
// class CXNode - Emitting ---------------------------------------------------
|
|
//
|
|
// Our own version of WideCharToMultiByte(CP_UTF8, ...)
|
|
//
|
|
// UTF-8 multi-byte encoding. See Appendix A.2 of the Unicode book for
|
|
// more info.
|
|
//
|
|
// Unicode value 1st byte 2nd byte 3rd byte
|
|
// 000000000xxxxxxx 0xxxxxxx
|
|
// 00000yyyyyxxxxxx 110yyyyy 10xxxxxx
|
|
// zzzzyyyyyyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx
|
|
//
|
|
inline
|
|
VOID WideCharToUTF8Chars (WCHAR wch, BYTE * pb, UINT * pib)
|
|
{
|
|
Assert (pb);
|
|
Assert (pib);
|
|
|
|
UINT ib = *pib;
|
|
|
|
// single-byte: 0xxxxxxx
|
|
//
|
|
if (wch < 0x80)
|
|
{
|
|
pb[ib] = static_cast<BYTE>(wch);
|
|
}
|
|
//
|
|
// two-byte: 110xxxxx 10xxxxxx
|
|
//
|
|
else if (wch < 0x800)
|
|
{
|
|
// Because we alloc'd two extra-bytes,
|
|
// we know there is room at the tail of
|
|
// the buffer for the overflow...
|
|
//
|
|
pb[ib++] = static_cast<BYTE>((wch >> 6) | 0xC0);
|
|
pb[ib] = static_cast<BYTE>((wch & 0x3F) | 0x80);
|
|
}
|
|
//
|
|
// three-byte: 1110xxxx 10xxxxxx 10xxxxxx
|
|
//
|
|
else
|
|
{
|
|
// Because we alloc'd two extra-bytes,
|
|
// we know there is room at the tail of
|
|
// the buffer for the overflow...
|
|
//
|
|
pb[ib++] = static_cast<BYTE>((wch >> 12) | 0xE0);
|
|
pb[ib++] = static_cast<BYTE>(((wch >> 6) & 0x3F) | 0x80);
|
|
pb[ib] = static_cast<BYTE>((wch & 0x3F) | 0x80);
|
|
}
|
|
|
|
*pib = ib;
|
|
}
|
|
|
|
// CXMLEmitter helper functions ----------------------------------------------
|
|
//
|
|
SCODE
|
|
ScGetPropNode (
|
|
/* [in] */ CEmitterNode& enItem,
|
|
/* [in] */ ULONG hsc,
|
|
/* [out] */ CEmitterNode& enPropStat,
|
|
/* [out] */ CEmitterNode& enProp)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
// <DAV:propstat> node
|
|
//
|
|
sc = enItem.ScAddNode (gc_wszPropstat, enPropStat);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// <DAV:status> node
|
|
//
|
|
sc = ScAddStatus (&enPropStat, hsc);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// <DAV:prop> node
|
|
//
|
|
sc = enPropStat.ScAddNode (gc_wszProp, enProp);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
// CXNode helper functions ---------------------------------------------------
|
|
//
|
|
SCODE
|
|
ScSetEscapedValue (CXNode* pxn, LPCWSTR pcwsz, UINT cch, BOOL fHandleStoragePathEscaping)
|
|
{
|
|
SCODE sc = S_OK;
|
|
CStackBuffer<WCHAR> lpwsz;
|
|
|
|
// Argh! We need to have a buffer to fill that is
|
|
// at least 3 bytes long for the odd occurrence of a
|
|
// single unicode char with significant bits above
|
|
// 0x7f.
|
|
//
|
|
UINT cb = min (cch + 2, CB_XMLBODYPART_SIZE);
|
|
|
|
// Make sure there is always room to terminate and allocate
|
|
// an extra byte.
|
|
// NOTE: cb is not an actual count of bytes
|
|
// because of this. it does not include the NULL termination.
|
|
//
|
|
// We really can handle zero bytes being sloughed into
|
|
// the buffer.
|
|
//
|
|
UINT ib;
|
|
UINT iwch;
|
|
CStackBuffer<BYTE> pb;
|
|
if (NULL == pb.resize (cb+1))
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (fHandleStoragePathEscaping)
|
|
{
|
|
// $REVIEW: this might cause a stack overflow for exceptionally
|
|
// large values of cch! but this branch should only be executed
|
|
// on the case for urls, so perhaps it's not possible...
|
|
//
|
|
if (NULL == lpwsz.resize((cch + 1) * sizeof(WCHAR)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
CopyMemory(lpwsz.get(), pcwsz, (cch * sizeof(WCHAR)));
|
|
lpwsz[cch] = L'\0';
|
|
|
|
cch = static_cast<UINT>(wcslen(lpwsz.get()));
|
|
pcwsz = lpwsz.get();
|
|
}
|
|
|
|
for (iwch = 0; iwch < cch; )
|
|
{
|
|
auto_heap_ptr<CHAR> pszEscaped;
|
|
|
|
// While there are more characters to convert
|
|
// and we have enough buffer space left for one UTF8 character
|
|
// (max of 3 bytes). the NULL termination is not included in
|
|
// cb, so it is already accounted for.
|
|
//
|
|
|
|
for (ib = 0;
|
|
(ib < cb - 2) && (iwch < cch);
|
|
ib++, iwch++)
|
|
{
|
|
WideCharToUTF8Chars (pcwsz[iwch], pb.get(), &ib);
|
|
}
|
|
|
|
// Terminate
|
|
//
|
|
pb[ib] = 0;
|
|
|
|
// Escape the bytes
|
|
//
|
|
HttpUriEscape (reinterpret_cast<LPSTR>(pb.get()), pszEscaped);
|
|
sc = pxn->ScSetUTF8Value (pszEscaped, static_cast<UINT>(strlen(pszEscaped)));
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
SCODE
|
|
ScEmitRawStoragePathValue (CXNode* pxn, LPCWSTR pcwsz, UINT cch)
|
|
{
|
|
return pxn->ScSetValue (pcwsz, cch);
|
|
}
|
|
|
|
// CEmitterNode helper functions ---------------------------------------------
|
|
//
|
|
VOID __fastcall
|
|
FormatStatus (ULONG hsc, LPSTR sz, UINT cb)
|
|
{
|
|
UINT cch = CchConstString(gc_szHTTP_1_1);
|
|
|
|
// Construct a status line from the HSC
|
|
//
|
|
memcpy (sz, gc_szHTTP_1_1, cch);
|
|
|
|
// Add in a space
|
|
//
|
|
*(sz + cch++) = ' ';
|
|
|
|
// Add in the HSC
|
|
//
|
|
_itoa (hsc, sz + cch, 10);
|
|
Assert (cch + 3 == strlen (sz));
|
|
cch += 3;
|
|
|
|
// Add in a space
|
|
//
|
|
*(sz + cch++) = ' ';
|
|
|
|
// Add the description text
|
|
// Note, status line is not localized
|
|
//
|
|
//$REVIEW: Now that status line is not localized, do we still need to go through
|
|
//$REVIEW: CResourceStringCache ?
|
|
//
|
|
LpszLoadString (hsc, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), sz + cch, cb - cch);
|
|
}
|
|
|
|
SCODE __fastcall
|
|
ScAddStatus (CEmitterNode* pen, ULONG hsc)
|
|
{
|
|
CHAR sz[MAX_PATH];
|
|
CEmitterNode enStatus;
|
|
|
|
FormatStatus (hsc, sz, sizeof(sz));
|
|
return pen->ScAddMultiByteNode (gc_wszStatus, enStatus, sz);
|
|
}
|
|
|
|
SCODE __fastcall
|
|
ScAddError (CEmitterNode* pen, LPCWSTR pwszErrMsg)
|
|
{
|
|
CEmitterNode en;
|
|
return pen->ScAddNode (gc_wszErrorMessage, en, gc_wszErrorMessage);
|
|
}
|
|
|
|
// class CStatusCache ------------------------------------------------------
|
|
//
|
|
BOOL
|
|
CStatusCache::EmitStatusNodeOp::operator()(
|
|
const CHsc& key, const auto_ref_ptr<CPropNameArray>& pna )
|
|
{
|
|
SCODE sc = S_OK;
|
|
UINT iProp;
|
|
CEmitterNode enPropStat;
|
|
CEmitterNode enProp;
|
|
|
|
sc = ScGetPropNode (m_enParent,
|
|
key.m_hsc,
|
|
enPropStat,
|
|
enProp);
|
|
// Add prop names
|
|
//
|
|
for (iProp = 0; iProp < pna->CProps(); iProp++)
|
|
{
|
|
CEmitterNode en;
|
|
|
|
// Add one prop
|
|
//
|
|
sc = enProp.ScAddNode (pna->PwszProp(iProp), en);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
return sc == S_OK;
|
|
}
|
|
|
|
SCODE
|
|
CStatusCache::ScAddErrorStatus (ULONG hsc, LPCWSTR pwszProp)
|
|
{
|
|
SCODE sc = E_OUTOFMEMORY;
|
|
auto_ref_ptr<CPropNameArray> pna;
|
|
auto_ref_ptr<CPropNameArray> * ppna = NULL;
|
|
|
|
// Lookup in the cache for the array for the specific hsc
|
|
//
|
|
ppna = m_cache.Lookup (hsc);
|
|
|
|
// Add a new propname array if not exist
|
|
//
|
|
if (!ppna)
|
|
{
|
|
// Create new propname array object
|
|
//
|
|
pna.take_ownership (new CPropNameArray());
|
|
if (!pna.get())
|
|
goto ret;
|
|
|
|
// Add it to the cache
|
|
//
|
|
if (!m_cache.FAdd (hsc, pna))
|
|
goto ret;
|
|
}
|
|
else
|
|
pna = *ppna;
|
|
|
|
// Persist the prop name string
|
|
//
|
|
pwszProp = m_csbPropNames.AppendWithNull (pwszProp);
|
|
if (!pwszProp)
|
|
goto ret;
|
|
|
|
// Add it to prop name array
|
|
//
|
|
sc = pna->ScAddPropName (pwszProp);
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
SCODE
|
|
CStatusCache::ScEmitErrorStatus (CEmitterNode& enParent)
|
|
{
|
|
EmitStatusNodeOp op (enParent);
|
|
|
|
//$REVIEW: Currently, ForEach does not return an error code
|
|
//$REVIEW: even when it stops in the middle. we may want to
|
|
//$REVIEW: have it return at least a boolean to allow caller
|
|
//$REVIEW: to tell whether to continue
|
|
//
|
|
m_cache.ForEach(op);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Property name escaping ----------------------------------------------------
|
|
//
|
|
DEC_CONST char gc_szEscape[] = "_xnnnn";
|
|
|
|
__inline WCHAR
|
|
WchFromEscape (const LPCWSTR wsz)
|
|
{
|
|
WCHAR wch = 0;
|
|
|
|
if ((L'x' == *(wsz + 1)) || (L'X' == *(wsz + 1)))
|
|
{
|
|
// Convert the hex value into a wchar
|
|
//
|
|
LPWSTR wszEnd;
|
|
wch = static_cast<WCHAR>(wcstoul(wsz + 2, &wszEnd, 16 /* hexidecimal */));
|
|
|
|
// If the length of the sequence is not correct,
|
|
// or the terminating character was not an underscore,
|
|
// then we there was no escape sequence.
|
|
//
|
|
if (((wszEnd - wsz) != CchConstString(gc_szEscape)) || (L'_' != *wszEnd))
|
|
wch = 0;
|
|
}
|
|
return wch;
|
|
}
|
|
|
|
__inline BOOL
|
|
FIsXmlAllowedChar (WCHAR wch, BOOL fFirstChar)
|
|
{
|
|
if (fFirstChar)
|
|
return isStartNameChar (wch);
|
|
else
|
|
return isNameChar (wch);
|
|
}
|
|
|
|
SCODE
|
|
ScEscapePropertyName (LPCWSTR wszProp, UINT cchProp, LPWSTR wszEscaped, UINT* pcch, BOOL fRestrictFirstCharacter)
|
|
{
|
|
Assert (wszProp);
|
|
Assert (wszEscaped);
|
|
Assert (pcch);
|
|
|
|
LPCWSTR wszStart = wszProp;
|
|
SCODE sc = S_OK;
|
|
UINT cch = 0;
|
|
UINT cchLeft = cchProp;
|
|
|
|
// The first character of an xml prop tag has different rules
|
|
// regarding what is allowable (only characters and underscores
|
|
// are allowed).
|
|
//
|
|
BOOL fFirstCharOfTag = TRUE;
|
|
|
|
// However, if the caller doesn't want us to impose the additional
|
|
// restrictions on the first character, treat the first character
|
|
// as no different from any other.
|
|
//
|
|
if (!fRestrictFirstCharacter) fFirstCharOfTag = FALSE;
|
|
|
|
while (wszProp < (wszStart + cchProp))
|
|
{
|
|
// If this is a supported character in a XML tag name,
|
|
// copy it over now...
|
|
//
|
|
if (FIsXmlAllowedChar(*wszProp, fFirstCharOfTag))
|
|
{
|
|
// If there is room, copy it over.
|
|
//
|
|
if (cch < *pcch)
|
|
*wszEscaped = *wszProp;
|
|
}
|
|
//
|
|
// ... or if the chararacter is an underscore that does not
|
|
// look like it preceeds an escape sequence, copy it over
|
|
// now...
|
|
//
|
|
else if ((L'_' == *wszProp) &&
|
|
((cchLeft <= CchConstString(gc_szEscape)) ||
|
|
(0 == WchFromEscape(wszProp))))
|
|
{
|
|
// If there is room, copy it over.
|
|
//
|
|
if (cch < *pcch)
|
|
*wszEscaped = *wszProp;
|
|
}
|
|
//
|
|
// ... and everything else gets escaped.
|
|
//
|
|
else
|
|
{
|
|
// Adjust the byte count as if there were room for all
|
|
// but one of the characters in the escape sequence.
|
|
//
|
|
cch += CchConstString(gc_szEscape);
|
|
|
|
// If there is room, insert the escape
|
|
// sequence.
|
|
//
|
|
if (cch < *pcch)
|
|
{
|
|
wsprintfW (wszEscaped, L"_x%04x_", *wszProp);
|
|
wszEscaped += CchConstString(gc_szEscape);
|
|
}
|
|
}
|
|
|
|
// Account for the last character copied over
|
|
//
|
|
wszEscaped += 1;
|
|
wszProp += 1;
|
|
cch += 1;
|
|
cchLeft--;
|
|
fFirstCharOfTag = FALSE;
|
|
}
|
|
|
|
// If there was not room to escape the whole thing, then
|
|
// pass back S_FALSE.
|
|
//
|
|
if (cch > *pcch)
|
|
sc = S_FALSE;
|
|
|
|
// Tell the caller how long the result is, and return
|
|
//
|
|
*pcch = cch;
|
|
return sc;
|
|
}
|