summaryrefslogtreecommitdiff
path: root/xfa/src/fde/css/fde_cssstylesheet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xfa/src/fde/css/fde_cssstylesheet.cpp')
-rw-r--r--xfa/src/fde/css/fde_cssstylesheet.cpp509
1 files changed, 509 insertions, 0 deletions
diff --git a/xfa/src/fde/css/fde_cssstylesheet.cpp b/xfa/src/fde/css/fde_cssstylesheet.cpp
new file mode 100644
index 0000000000..9b18dd2552
--- /dev/null
+++ b/xfa/src/fde/css/fde_cssstylesheet.cpp
@@ -0,0 +1,509 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "xfa/src/fde/css/fde_cssstylesheet.h"
+
+#include "xfa/src/fde/css/fde_cssdatatable.h"
+#include "xfa/src/fde/css/fde_csssyntax.h"
+#include "xfa/src/fgas/crt/fgas_codepage.h"
+
+IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadHTMLStandardStyleSheet() {
+ static const FX_WCHAR* s_pStyle =
+ L"html,address,blockquote,body,dd,div,dl,dt,fieldset,form,frame,frameset,"
+ L"h1,h2,h3,h4,h5,h6,noframes,ol,p,ul,center,dir,hr,menu,pre{display:"
+ L"block}"
+ L"li{display:list-item}head{display:none}table{display:table}tr{display:"
+ L"table-row}thead{display:table-header-group}tbody{display:table-row-"
+ L"group}tfoot{display:table-footer-group}"
+ L"col{display:table-column}colgroup{display:table-column-group}td,th{"
+ L"display:table-cell}caption{display:table-caption}th{font-weight:bolder;"
+ L"text-align:center}caption{text-align:center}"
+ L"body{margin:0}h1{font-size:2em;margin:.67em "
+ L"0}h2{font-size:1.5em;margin:.75em 0}h3{font-size:1.17em;margin:.83em "
+ L"0}h4,p,blockquote,ul,fieldset,form,ol,dl,dir,menu{margin:1.12em 0}"
+ L"h5{font-size:.83em;margin:1.5em 0}h6{font-size:.75em;margin:1.67em "
+ L"0}h1,h2,h3,h4,h5,h6,b,strong{font-weight:bolder}blockquote{margin-left:"
+ L"40px;margin-right:40px}i,cite,em,var,address{font-style:italic}"
+ L"pre,tt,code,kbd,samp{font-family:monospace}pre{white-space:pre}button,"
+ L"textarea,input,select{display:inline-block}big{font-size:1.17em}small,"
+ L"sub,sup{font-size:.83em}sub{vertical-align:sub}"
+ L"sup{vertical-align:super}table{border-spacing:2px}thead,tbody,tfoot{"
+ L"vertical-align:middle}td,th,tr{vertical-align:inherit}s,strike,del{"
+ L"text-decoration:line-through}hr{border:1px inset silver}"
+ L"ol,ul,dir,menu,dd{margin-left:40px}ol{list-style-type:decimal}ol ul,ul "
+ L"ol,ul ul,ol "
+ L"ol{margin-top:0;margin-bottom:0}u,ins{text-decoration:underline}center{"
+ L"text-align:center}"
+ L"ruby{display:ruby}rt{display:ruby-text;font-size:.5em}rb{display:ruby-"
+ L"base}rbc{display:ruby-base-group}rtc{display:ruby-text-group}"
+ L"q:before{content:open-quote}q:after{content:close-quote}"
+ L"rp{display:none}";
+ return IFDE_CSSStyleSheet::LoadFromBuffer(
+ CFX_WideString(), s_pStyle, FXSYS_wcslen(s_pStyle), FX_CODEPAGE_UTF8);
+}
+
+IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadFromStream(
+ const CFX_WideString& szUrl,
+ IFX_Stream* pStream,
+ FX_WORD wCodePage,
+ FX_DWORD dwMediaList) {
+ CFDE_CSSStyleSheet* pStyleSheet = new CFDE_CSSStyleSheet(dwMediaList);
+ if (!pStyleSheet->LoadFromStream(szUrl, pStream, wCodePage)) {
+ pStyleSheet->Release();
+ pStyleSheet = NULL;
+ }
+ return pStyleSheet;
+}
+IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadFromBuffer(
+ const CFX_WideString& szUrl,
+ const FX_WCHAR* pBuffer,
+ int32_t iBufSize,
+ FX_WORD wCodePage,
+ FX_DWORD dwMediaList) {
+ CFDE_CSSStyleSheet* pStyleSheet = new CFDE_CSSStyleSheet(dwMediaList);
+ if (!pStyleSheet->LoadFromBuffer(szUrl, pBuffer, iBufSize, wCodePage)) {
+ pStyleSheet->Release();
+ pStyleSheet = NULL;
+ }
+ return pStyleSheet;
+}
+CFDE_CSSStyleSheet::CFDE_CSSStyleSheet(FX_DWORD dwMediaList)
+ : m_wCodePage(FX_CODEPAGE_UTF8),
+ m_wRefCount(1),
+ m_dwMediaList(dwMediaList),
+ m_pAllocator(NULL) {
+ FXSYS_assert(m_dwMediaList > 0);
+}
+CFDE_CSSStyleSheet::~CFDE_CSSStyleSheet() {
+ Reset();
+}
+void CFDE_CSSStyleSheet::Reset() {
+ for (int32_t i = m_RuleArray.GetSize() - 1; i >= 0; --i) {
+ IFDE_CSSRule* pRule = m_RuleArray.GetAt(i);
+ switch (pRule->GetType()) {
+ case FDE_CSSRULETYPE_Style:
+ ((CFDE_CSSStyleRule*)pRule)->~CFDE_CSSStyleRule();
+ break;
+ case FDE_CSSRULETYPE_Media:
+ ((CFDE_CSSMediaRule*)pRule)->~CFDE_CSSMediaRule();
+ break;
+ case FDE_CSSRULETYPE_FontFace:
+ ((CFDE_CSSFontFaceRule*)pRule)->~CFDE_CSSFontFaceRule();
+ break;
+ default:
+ FXSYS_assert(FALSE);
+ break;
+ }
+ }
+ m_RuleArray.RemoveAll();
+ m_Selectors.RemoveAll();
+ m_StringCache.RemoveAll();
+ if (m_pAllocator) {
+ m_pAllocator->Release();
+ m_pAllocator = NULL;
+ }
+}
+FX_DWORD CFDE_CSSStyleSheet::AddRef() {
+ return ++m_wRefCount;
+}
+FX_DWORD CFDE_CSSStyleSheet::Release() {
+ FX_DWORD dwRefCount = --m_wRefCount;
+ if (dwRefCount == 0) {
+ delete this;
+ }
+ return dwRefCount;
+}
+int32_t CFDE_CSSStyleSheet::CountRules() const {
+ return m_RuleArray.GetSize();
+}
+IFDE_CSSRule* CFDE_CSSStyleSheet::GetRule(int32_t index) {
+ return m_RuleArray.GetAt(index);
+}
+FX_BOOL CFDE_CSSStyleSheet::LoadFromStream(const CFX_WideString& szUrl,
+ IFX_Stream* pStream,
+ FX_WORD wCodePage) {
+ FXSYS_assert(pStream != NULL);
+ IFDE_CSSSyntaxParser* pSyntax = IFDE_CSSSyntaxParser::Create();
+ if (pSyntax == NULL) {
+ return FALSE;
+ }
+ if (pStream->GetCodePage() != wCodePage) {
+ pStream->SetCodePage(wCodePage);
+ }
+ FX_BOOL bRet = pSyntax->Init(pStream, 4096) && LoadFromSyntax(pSyntax);
+ pSyntax->Release();
+ m_wCodePage = wCodePage;
+ m_szUrl = szUrl;
+ return bRet;
+}
+FX_BOOL CFDE_CSSStyleSheet::LoadFromBuffer(const CFX_WideString& szUrl,
+ const FX_WCHAR* pBuffer,
+ int32_t iBufSize,
+ FX_WORD wCodePage) {
+ FXSYS_assert(pBuffer != NULL && iBufSize > 0);
+ IFDE_CSSSyntaxParser* pSyntax = IFDE_CSSSyntaxParser::Create();
+ if (pSyntax == NULL) {
+ return FALSE;
+ }
+ FX_BOOL bRet = pSyntax->Init(pBuffer, iBufSize) && LoadFromSyntax(pSyntax);
+ pSyntax->Release();
+ m_wCodePage = wCodePage;
+ m_szUrl = szUrl;
+ return bRet;
+}
+FX_BOOL CFDE_CSSStyleSheet::LoadFromSyntax(IFDE_CSSSyntaxParser* pSyntax) {
+ Reset();
+ m_pAllocator = FX_CreateAllocator(FX_ALLOCTYPE_Static, 1024, 0);
+ if (m_pAllocator == NULL) {
+ return FALSE;
+ }
+ FDE_CSSSYNTAXSTATUS eStatus;
+ do {
+ switch (eStatus = pSyntax->DoSyntaxParse()) {
+ case FDE_CSSSYNTAXSTATUS_StyleRule:
+ eStatus = LoadStyleRule(pSyntax, m_RuleArray);
+ break;
+ case FDE_CSSSYNTAXSTATUS_MediaRule:
+ eStatus = LoadMediaRule(pSyntax);
+ break;
+ case FDE_CSSSYNTAXSTATUS_FontFaceRule:
+ eStatus = LoadFontFaceRule(pSyntax, m_RuleArray);
+ break;
+ case FDE_CSSSYNTAXSTATUS_ImportRule:
+ eStatus = LoadImportRule(pSyntax);
+ break;
+ case FDE_CSSSYNTAXSTATUS_PageRule:
+ eStatus = LoadPageRule(pSyntax);
+ break;
+ default:
+ break;
+ }
+ } while (eStatus >= FDE_CSSSYNTAXSTATUS_None);
+ m_Selectors.RemoveAll();
+ m_StringCache.RemoveAll();
+ return eStatus != FDE_CSSSYNTAXSTATUS_Error;
+}
+FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadMediaRule(
+ IFDE_CSSSyntaxParser* pSyntax) {
+ FX_DWORD dwMediaList = 0;
+ CFDE_CSSMediaRule* pMediaRule = NULL;
+ for (;;) {
+ switch (pSyntax->DoSyntaxParse()) {
+ case FDE_CSSSYNTAXSTATUS_MediaType: {
+ int32_t iLen;
+ const FX_WCHAR* psz = pSyntax->GetCurrentString(iLen);
+ FDE_LPCCSSMEDIATYPETABLE pMediaType =
+ FDE_GetCSSMediaTypeByName(psz, iLen);
+ if (pMediaType != NULL) {
+ dwMediaList |= pMediaType->wValue;
+ }
+ } break;
+ case FDE_CSSSYNTAXSTATUS_StyleRule:
+ if (pMediaRule == NULL) {
+ SkipRuleSet(pSyntax);
+ } else {
+ FDE_CSSSYNTAXSTATUS eStatus =
+ LoadStyleRule(pSyntax, pMediaRule->GetArray());
+ if (eStatus < FDE_CSSSYNTAXSTATUS_None) {
+ return eStatus;
+ }
+ }
+ break;
+ case FDE_CSSSYNTAXSTATUS_DeclOpen:
+ if ((dwMediaList & m_dwMediaList) > 0 && pMediaRule == NULL) {
+ pMediaRule =
+ FXTARGET_NewWith(m_pAllocator) CFDE_CSSMediaRule(dwMediaList);
+ m_RuleArray.Add(pMediaRule);
+ }
+ break;
+ case FDE_CSSSYNTAXSTATUS_DeclClose:
+ return FDE_CSSSYNTAXSTATUS_None;
+ FDE_CSSSWITCHDEFAULTS();
+ }
+ }
+}
+FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadStyleRule(
+ IFDE_CSSSyntaxParser* pSyntax,
+ CFDE_CSSRuleArray& ruleArray) {
+ m_Selectors.RemoveAt(0, m_Selectors.GetSize());
+ CFDE_CSSStyleRule* pStyleRule = NULL;
+ const FX_WCHAR* pszValue = NULL;
+ int32_t iValueLen = 0;
+ FDE_CSSPROPERTYARGS propertyArgs;
+ propertyArgs.pStaticStore = m_pAllocator;
+ propertyArgs.pStringCache = &m_StringCache;
+ propertyArgs.pProperty = NULL;
+ CFX_WideString wsName;
+ for (;;) {
+ switch (pSyntax->DoSyntaxParse()) {
+ case FDE_CSSSYNTAXSTATUS_Selector: {
+ pszValue = pSyntax->GetCurrentString(iValueLen);
+ IFDE_CSSSelector* pSelector =
+ CFDE_CSSSelector::FromString(m_pAllocator, pszValue, iValueLen);
+ if (pSelector != NULL) {
+ m_Selectors.Add(pSelector);
+ }
+ } break;
+ case FDE_CSSSYNTAXSTATUS_PropertyName:
+ pszValue = pSyntax->GetCurrentString(iValueLen);
+ propertyArgs.pProperty = FDE_GetCSSPropertyByName(pszValue, iValueLen);
+ if (propertyArgs.pProperty == NULL) {
+ wsName = CFX_WideStringC(pszValue, iValueLen);
+ }
+ break;
+ case FDE_CSSSYNTAXSTATUS_PropertyValue:
+ if (propertyArgs.pProperty != NULL) {
+ pszValue = pSyntax->GetCurrentString(iValueLen);
+ if (iValueLen > 0) {
+ pStyleRule->GetDeclImp().AddProperty(&propertyArgs, pszValue,
+ iValueLen);
+ }
+ } else if (iValueLen > 0) {
+ pszValue = pSyntax->GetCurrentString(iValueLen);
+ if (iValueLen > 0) {
+ pStyleRule->GetDeclImp().AddProperty(
+ &propertyArgs, wsName, wsName.GetLength(), pszValue, iValueLen);
+ }
+ }
+ break;
+ case FDE_CSSSYNTAXSTATUS_DeclOpen:
+ if (pStyleRule == NULL && m_Selectors.GetSize() > 0) {
+ pStyleRule = FXTARGET_NewWith(m_pAllocator) CFDE_CSSStyleRule;
+ pStyleRule->SetSelector(m_pAllocator, m_Selectors);
+ ruleArray.Add(pStyleRule);
+ } else {
+ SkipRuleSet(pSyntax);
+ return FDE_CSSSYNTAXSTATUS_None;
+ }
+ break;
+ case FDE_CSSSYNTAXSTATUS_DeclClose:
+ if (pStyleRule != NULL &&
+ pStyleRule->GetDeclImp().GetStartPosition() == NULL) {
+ pStyleRule->~CFDE_CSSStyleRule();
+ ruleArray.RemoveLast(1);
+ }
+ return FDE_CSSSYNTAXSTATUS_None;
+ FDE_CSSSWITCHDEFAULTS();
+ }
+ }
+}
+FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadFontFaceRule(
+ IFDE_CSSSyntaxParser* pSyntax,
+ CFDE_CSSRuleArray& ruleArray) {
+ CFDE_CSSFontFaceRule* pFontFaceRule = NULL;
+ const FX_WCHAR* pszValue = NULL;
+ int32_t iValueLen = 0;
+ FDE_CSSPROPERTYARGS propertyArgs;
+ propertyArgs.pStaticStore = m_pAllocator;
+ propertyArgs.pStringCache = &m_StringCache;
+ propertyArgs.pProperty = NULL;
+ for (;;) {
+ switch (pSyntax->DoSyntaxParse()) {
+ case FDE_CSSSYNTAXSTATUS_PropertyName:
+ pszValue = pSyntax->GetCurrentString(iValueLen);
+ propertyArgs.pProperty = FDE_GetCSSPropertyByName(pszValue, iValueLen);
+ break;
+ case FDE_CSSSYNTAXSTATUS_PropertyValue:
+ if (propertyArgs.pProperty != NULL) {
+ pszValue = pSyntax->GetCurrentString(iValueLen);
+ if (iValueLen > 0) {
+ pFontFaceRule->GetDeclImp().AddProperty(&propertyArgs, pszValue,
+ iValueLen);
+ }
+ }
+ break;
+ case FDE_CSSSYNTAXSTATUS_DeclOpen:
+ if (pFontFaceRule == NULL) {
+ pFontFaceRule = FXTARGET_NewWith(m_pAllocator) CFDE_CSSFontFaceRule;
+ ruleArray.Add(pFontFaceRule);
+ }
+ break;
+ case FDE_CSSSYNTAXSTATUS_DeclClose:
+ return FDE_CSSSYNTAXSTATUS_None;
+ FDE_CSSSWITCHDEFAULTS();
+ }
+ }
+ return FDE_CSSSYNTAXSTATUS_None;
+}
+FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadImportRule(
+ IFDE_CSSSyntaxParser* pSyntax) {
+ for (;;) {
+ switch (pSyntax->DoSyntaxParse()) {
+ case FDE_CSSSYNTAXSTATUS_ImportClose:
+ return FDE_CSSSYNTAXSTATUS_None;
+ case FDE_CSSSYNTAXSTATUS_URI:
+ break;
+ FDE_CSSSWITCHDEFAULTS();
+ }
+ }
+}
+FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadPageRule(
+ IFDE_CSSSyntaxParser* pSyntax) {
+ return SkipRuleSet(pSyntax);
+}
+FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::SkipRuleSet(
+ IFDE_CSSSyntaxParser* pSyntax) {
+ for (;;) {
+ switch (pSyntax->DoSyntaxParse()) {
+ case FDE_CSSSYNTAXSTATUS_Selector:
+ case FDE_CSSSYNTAXSTATUS_DeclOpen:
+ case FDE_CSSSYNTAXSTATUS_PropertyName:
+ case FDE_CSSSYNTAXSTATUS_PropertyValue:
+ break;
+ case FDE_CSSSYNTAXSTATUS_DeclClose:
+ return FDE_CSSSYNTAXSTATUS_None;
+ FDE_CSSSWITCHDEFAULTS();
+ }
+ }
+ return FDE_CSSSYNTAXSTATUS_None;
+}
+void CFDE_CSSStyleRule::SetSelector(IFX_MEMAllocator* pStaticStore,
+ const CFDE_CSSSelectorArray& list) {
+ FXSYS_assert(m_ppSelector == NULL);
+ m_iSelectors = list.GetSize();
+ m_ppSelector = (IFDE_CSSSelector**)pStaticStore->Alloc(
+ m_iSelectors * sizeof(IFDE_CSSSelector*));
+ for (int32_t i = 0; i < m_iSelectors; ++i) {
+ m_ppSelector[i] = list.GetAt(i);
+ }
+}
+CFDE_CSSMediaRule::~CFDE_CSSMediaRule() {
+ for (int32_t i = m_RuleArray.GetSize() - 1; i >= 0; --i) {
+ IFDE_CSSRule* pRule = m_RuleArray.GetAt(i);
+ switch (pRule->GetType()) {
+ case FDE_CSSRULETYPE_Style:
+ ((CFDE_CSSStyleRule*)pRule)->~CFDE_CSSStyleRule();
+ break;
+ default:
+ FXSYS_assert(FALSE);
+ break;
+ }
+ }
+}
+inline FX_BOOL FDE_IsCSSChar(FX_WCHAR wch) {
+ return (wch >= 'a' && wch <= 'z') || (wch >= 'A' && wch <= 'Z');
+}
+int32_t FDE_GetCSSPersudoLen(const FX_WCHAR* psz, const FX_WCHAR* pEnd) {
+ FXSYS_assert(*psz == ':');
+ const FX_WCHAR* pStart = psz;
+ while (psz < pEnd) {
+ FX_WCHAR wch = *psz;
+ if (FDE_IsCSSChar(wch) || wch == ':') {
+ ++psz;
+ } else {
+ break;
+ }
+ }
+ return psz - pStart;
+}
+int32_t FDE_GetCSSNameLen(const FX_WCHAR* psz, const FX_WCHAR* pEnd) {
+ const FX_WCHAR* pStart = psz;
+ while (psz < pEnd) {
+ FX_WCHAR wch = *psz;
+ if (FDE_IsCSSChar(wch) || (wch >= '0' && wch <= '9') || wch == '_' ||
+ wch == '-') {
+ ++psz;
+ } else {
+ break;
+ }
+ }
+ return psz - pStart;
+}
+IFDE_CSSSelector* CFDE_CSSSelector::FromString(IFX_MEMAllocator* pStaticStore,
+ const FX_WCHAR* psz,
+ int32_t iLen) {
+ FXSYS_assert(pStaticStore != NULL && psz != NULL && iLen > 0);
+ const FX_WCHAR* pStart = psz;
+ const FX_WCHAR* pEnd = psz + iLen;
+ for (; psz < pEnd; ++psz) {
+ switch (*psz) {
+ case '>':
+ case '[':
+ case '+':
+ return NULL;
+ }
+ }
+ CFDE_CSSSelector *pFirst = NULL, *pLast = NULL;
+ CFDE_CSSSelector *pPersudoFirst = NULL, *pPersudoLast = NULL;
+ for (psz = pStart; psz < pEnd;) {
+ FX_WCHAR wch = *psz;
+ if (wch == '.' || wch == '#') {
+ if (psz == pStart || psz[-1] == ' ') {
+ CFDE_CSSSelector* p = FXTARGET_NewWith(pStaticStore)
+ CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Element, L"*", 1, TRUE);
+ if (p == NULL) {
+ return NULL;
+ }
+ if (pFirst != NULL) {
+ pFirst->SetType(FDE_CSSSELECTORTYPE_Descendant);
+ p->SetNext(pFirst);
+ }
+ pFirst = pLast = p;
+ }
+ FXSYS_assert(pLast != NULL);
+ int32_t iNameLen = FDE_GetCSSNameLen(++psz, pEnd);
+ if (iNameLen == 0) {
+ return NULL;
+ }
+ FDE_CSSSELECTORTYPE eType =
+ wch == '.' ? FDE_CSSSELECTORTYPE_Class : FDE_CSSSELECTORTYPE_ID;
+ CFDE_CSSSelector* p = FXTARGET_NewWith(pStaticStore)
+ CFDE_CSSSelector(eType, psz, iNameLen, FALSE);
+ if (p == NULL) {
+ return NULL;
+ }
+ p->SetNext(pLast->GetNextSelector());
+ pLast->SetNext(p);
+ pLast = p;
+ psz += iNameLen;
+ } else if (FDE_IsCSSChar(wch) || wch == '*') {
+ int32_t iNameLen = wch == '*' ? 1 : FDE_GetCSSNameLen(psz, pEnd);
+ if (iNameLen == 0) {
+ return NULL;
+ }
+ CFDE_CSSSelector* p = FXTARGET_NewWith(pStaticStore)
+ CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Element, psz, iNameLen, TRUE);
+ if (p == NULL) {
+ return NULL;
+ }
+ if (pFirst == NULL) {
+ pFirst = pLast = p;
+ } else {
+ pFirst->SetType(FDE_CSSSELECTORTYPE_Descendant);
+ p->SetNext(pFirst);
+ pFirst = pLast = p;
+ }
+ psz += iNameLen;
+ } else if (wch == ':') {
+ int32_t iNameLen = FDE_GetCSSPersudoLen(psz, pEnd);
+ if (iNameLen == 0) {
+ return NULL;
+ }
+ CFDE_CSSSelector* p = FXTARGET_NewWith(pStaticStore)
+ CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Persudo, psz, iNameLen, TRUE);
+ if (p == NULL) {
+ return NULL;
+ }
+ if (pPersudoFirst == NULL) {
+ pPersudoFirst = pPersudoLast = p;
+ } else {
+ pPersudoLast->SetNext(p);
+ pPersudoLast = p;
+ }
+ psz += iNameLen;
+ } else if (wch == ' ') {
+ psz++;
+ } else {
+ return NULL;
+ }
+ }
+ if (pPersudoFirst == NULL) {
+ return pFirst;
+ } else {
+ pPersudoLast->SetNext(pFirst);
+ return pPersudoFirst;
+ }
+}