tagparse.cpp
上传用户:dangjiwu
上传日期:2013-07-19
资源大小:42019k
文件大小:17k
源码类别:

Symbian

开发平台:

Visual C++

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Source last modified: $Id: tagparse.cpp,v 1.4.36.3 2004/07/09 01:44:10 hubbe Exp $
  3.  * 
  4.  * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
  5.  * 
  6.  * The contents of this file, and the files included with this file,
  7.  * are subject to the current version of the RealNetworks Public
  8.  * Source License (the "RPSL") available at
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed
  10.  * the file under the current version of the RealNetworks Community
  11.  * Source License (the "RCSL") available at
  12.  * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
  13.  * will apply. You may also obtain the license terms directly from
  14.  * RealNetworks.  You may not use this file except in compliance with
  15.  * the RPSL or, if you have a valid RCSL with RealNetworks applicable
  16.  * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
  17.  * the rights, obligations and limitations governing use of the
  18.  * contents of the file.
  19.  * 
  20.  * Alternatively, the contents of this file may be used under the
  21.  * terms of the GNU General Public License Version 2 or later (the
  22.  * "GPL") in which case the provisions of the GPL are applicable
  23.  * instead of those above. If you wish to allow use of your version of
  24.  * this file only under the terms of the GPL, and not to allow others
  25.  * to use your version of this file under the terms of either the RPSL
  26.  * or RCSL, indicate your decision by deleting the provisions above
  27.  * and replace them with the notice and other provisions required by
  28.  * the GPL. If you do not delete the provisions above, a recipient may
  29.  * use your version of this file under the terms of any one of the
  30.  * RPSL, the RCSL or the GPL.
  31.  * 
  32.  * This file is part of the Helix DNA Technology. RealNetworks is the
  33.  * developer of the Original Code and owns the copyrights in the
  34.  * portions it created.
  35.  * 
  36.  * This file, and the files included with this file, is distributed
  37.  * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
  38.  * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
  39.  * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
  40.  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
  41.  * ENJOYMENT OR NON-INFRINGEMENT.
  42.  * 
  43.  * Technology Compatibility Kit Test Suite(s) Location:
  44.  *    http://www.helixcommunity.org/content/tck
  45.  * 
  46.  * Contributor(s):
  47.  * 
  48.  * ***** END LICENSE BLOCK ***** */
  49. /*
  50.  *      XML tag parser
  51.  *
  52.  * Not to be confused with the full XML parser, this parser will only
  53.  *      tell you what tags appear in a document, and will parse their
  54.  *      attributes for you. It will not perform any validation.
  55.  */
  56. #include <ctype.h>
  57. #include "hxcom.h"
  58. #include "hxtypes.h"
  59. #include "hxstrutl.h"
  60. #include "hxmap.h"
  61. #include "xmlencod.h"
  62. #include "looseprs.h"
  63. #include "tagparse.h"
  64. #include "hxheap.h"
  65. #ifdef _DEBUG
  66. #undef HX_THIS_FILE
  67. static const char HX_THIS_FILE[] = __FILE__;
  68. #endif
  69. XMLTagParser::XMLTagParser(const char* pEncoding)
  70.     : m_comment_state(0)
  71.     , m_comment_get_arg(0)
  72.     , m_comment_pos(0)
  73. {
  74.     if(pEncoding)
  75.     {
  76. m_pEncoding = new_string(pEncoding);
  77.     }
  78.     else
  79.     {
  80. m_pEncoding = new_string("US-ASCII"); // default encoding
  81.     }
  82. }
  83. XMLTagParser::~XMLTagParser()
  84. {
  85.     HX_DELETE(m_pEncoding);
  86. }
  87. XMLParseResult
  88. XMLTagParser::Parse(const char*& buf, 
  89.     UINT32 len, 
  90.     XMLTag*& tag)
  91. {
  92.     const char* open;
  93.     const char* close;
  94.     const char* cur;
  95.     const char* afterclose;
  96.     tag = NULL;
  97.     if(m_comment_state > 0)
  98.     {
  99. FindCommentClose(buf, buf, buf+len);
  100. if(m_comment_state != 0)
  101. {
  102.     return XMLPNoClose;
  103. }
  104. else if(m_comment_get_arg != 3)
  105. {
  106.     tag = new XMLTag(FALSE);
  107.     tag->new_attribute()->value = new_string("");   // dummy tag
  108.     return XMLPComment;
  109. }
  110. // Got a comment command
  111. tag = new XMLTag(FALSE);
  112. tag->new_attribute()->value = new_string(m_comment_arg);
  113. tag->m_cur_attribute->name = new_string(m_comment_command);
  114. return XMLPComment;
  115.     }
  116.     if(*buf != '<')
  117.     {
  118. // If there isn't a tag right away, tell the user there's just plain
  119. // text here.
  120. cur = buf;
  121. while(((UINT32)(cur - buf) < len) && (*cur != '<'))
  122. {
  123.     cur++;
  124. }
  125. tag = new XMLTag(FALSE);
  126. char* pText = new char[cur - buf + 1];
  127. strncpy(pText, buf, cur - buf); /* Flawfinder: ignore */
  128. pText[cur - buf] = '';
  129. tag->new_attribute()->value = new_string(pText);
  130. delete [] pText;
  131. buf = cur;
  132. return XMLPPlainText;
  133.     }
  134.     open = buf;
  135.     BOOL   bInDoubleQuote = FALSE;
  136.     BOOL   bInSingleQuote = FALSE;
  137.     BOOL   bInComment   = FALSE;
  138.     BOOL   bInDeclaration = FALSE;
  139.     UINT16 nCommentDepth  = 0;
  140.     if(*(open+1) && *(open+1) == '!' &&
  141.        *(open+2) && *(open+2) == '-' && 
  142.        *(open+3) && *(open+3) == '-')
  143.     {
  144. // '<!--' starts a comment
  145. bInComment = TRUE;
  146.     }
  147.     for(close = open; close < buf+len; close++)
  148.     {
  149. if(*close == '"' && !bInComment)
  150. {
  151.     if(!bInSingleQuote)
  152.     {
  153. if(bInDoubleQuote)
  154. {
  155.     bInDoubleQuote = FALSE;
  156. }
  157. else
  158. {
  159.     bInDoubleQuote = TRUE;
  160. }
  161.     }
  162. }
  163. else if(*close == ''' && !bInComment)
  164. {
  165.     if(!bInDoubleQuote)
  166.     {
  167. if(bInSingleQuote)
  168. {
  169.     bInSingleQuote = FALSE;
  170. }
  171. else
  172. {
  173.     bInSingleQuote = TRUE;
  174. }
  175.     }
  176. }
  177. else if(*close == '[' && !bInDeclaration)
  178. {
  179.     bInDeclaration = TRUE;
  180. }
  181. else if(*close == ']' && bInDeclaration)
  182. {
  183.     bInDeclaration = FALSE;
  184. }
  185. // Increase the depth if we find a comment within a comment
  186. else if(*(close) == '<' && bInComment)
  187. {
  188.     if(*(close+1) && *(close+1) == '!' &&
  189.        *(close+2) && *(close+2) == '-' && 
  190.        *(close+3) && *(close+3) == '-')
  191.     {
  192. // '<!--' starts a comment
  193. nCommentDepth++;
  194.     }
  195. }
  196. else if(*close == '>')
  197. {
  198.     // If we are in a comment, we should only stop at a comment end
  199.     // (Comments must end with "-->")
  200.     if (bInComment)
  201.     {
  202. if ((close - open) > 5 && 
  203.     *(close-1) == '-'  && 
  204.     *(close-2) == '-')
  205. {
  206.     nCommentDepth--;
  207.     if (!nCommentDepth)
  208.     {
  209. break;
  210.     }
  211. }
  212.     }
  213.     else
  214.     {
  215. if (!bInDoubleQuote && !bInSingleQuote && !bInDeclaration)
  216. {
  217.     break;
  218. }
  219.     }
  220. }
  221.     }
  222.     if(*close != '>')
  223.     {
  224. buf = open;
  225. return XMLPNoClose;
  226.     }
  227.     afterclose = close+1;
  228.     if(*(open+1) == '!')
  229.     {
  230. if(*(open+2) == '-' && *(open+3) == '-')
  231. {
  232.     // '<!--' starts a comment
  233.     m_comment_state = 1;
  234.     m_comment_start = TRUE;
  235.     FindCommentClose(buf, open+4, buf + len);
  236.     if(m_comment_state != 0)
  237.     {
  238. return XMLPNoClose;
  239.     }
  240.     else if(m_comment_get_arg != 3)
  241.     {
  242. tag = new XMLTag(FALSE);
  243. const char* pBeginComment = open + 4;
  244. int commentLen = buf - pBeginComment - 3;
  245. tag->new_attribute()->value = new_string(pBeginComment, commentLen);
  246. return XMLPComment;
  247.     }
  248.     // Got a comment command
  249.     tag = new XMLTag(FALSE);
  250.     tag->new_attribute()->value = new_string(m_comment_arg);
  251.     tag->m_cur_attribute->name = new_string(m_comment_command);
  252.     return XMLPComment;
  253. }
  254. XMLParseResult rc = ParseTag(open+1, close, XMLDirectiveTag, tag);
  255. if(XMLPTag == rc)
  256. {
  257.     buf = afterclose;
  258.     return XMLPDirective;
  259. }
  260. buf = afterclose;
  261. return XMLPBadDirective;
  262.     }
  263.     
  264.     if(*(open + 1) == '?')
  265.     {
  266. // A Processing Instruction
  267. XMLParseResult rc = ParseTag(open+1, close, XMLProcInstTag, tag);
  268. if(XMLPTag == rc)
  269. {
  270.     buf = afterclose;
  271.     return XMLPProcInst;
  272. }
  273. return XMLPBadProcInst;
  274.     }
  275.     // Just a plain old tag
  276.     XMLParseResult rc = ParseTag(open, close, XMLPlainTag, tag);
  277.     if(XMLPTag == rc)
  278.     {
  279. buf = afterclose;
  280. return XMLPTag;
  281.     }
  282.     return rc;
  283. }
  284. XMLParseResult
  285. XMLTagParser::ParseTag(const char* open, 
  286.        const char* close, 
  287.        XMLTagType tType, 
  288.        XMLTag*& tag)
  289. {
  290.     const char* cur = open+1;
  291.     BOOL bHasAttributeNames = TRUE;
  292.     BOOL bUseNonQuotedValues = TRUE;
  293.     BOOL bHasDirectives = FALSE;
  294.     tag = new XMLTag(FALSE);
  295.     switch(tType)
  296.     {
  297. case XMLPlainTag:
  298. {
  299.     if(*(close - 1) == '/')
  300.     {
  301. tag->m_need_close = FALSE;
  302. close--;
  303.     }
  304. }
  305. break;
  306. case XMLProcInstTag:
  307. {
  308.     tag->m_need_close = FALSE;
  309.     if(*(close - 1) == '?')
  310.     {
  311. close--;
  312.     }
  313. }
  314. break;
  315. case XMLDirectiveTag:
  316. {
  317.     bHasAttributeNames = FALSE;
  318.     bUseNonQuotedValues = TRUE;
  319.     bHasDirectives = TRUE;
  320.     tag->m_need_close = FALSE;
  321. }
  322. break;
  323. default:
  324. {
  325.     tag->m_need_close = FALSE;
  326. }
  327. break;
  328.     }
  329.     tag->m_type = tType;
  330.     GetStringResult res = GetString(cur, close, tag->m_name, TagType);
  331.     if(res == GSEndTag)
  332.     {
  333. tag->m_type = XMLEndTag;
  334. tag->m_need_close = FALSE;
  335. return XMLPTag;
  336.     }
  337.     else if(res == GSMissingQuote)
  338.     {
  339. delete tag;
  340. tag = NULL;
  341. return XMLPAttributeValueNotQuoted;
  342.     }
  343.     if(GSFoundExpected != res)
  344.     {
  345. delete tag;
  346. tag = NULL;
  347. return XMLPNoTagType;
  348.     }
  349.     else
  350.     {
  351. while(cur < close)
  352. {
  353.     if(bHasAttributeNames)
  354.     {
  355. GetStringResult res = GetString(cur, close, 
  356. tag->new_attribute()->name,
  357. AttributeName);
  358. if(res == GSNoValue)
  359. {
  360.     delete tag->m_cur_attribute;
  361.     tag->m_numAttributes--;
  362.     break;
  363. }
  364. switch(res)
  365. {
  366.     case GSValueOnly:
  367. // The user of this parser will fill in the name of this
  368. // attribute
  369. tag->m_cur_attribute->value = tag->m_cur_attribute->name;
  370. tag->m_cur_attribute->name = NULL;
  371. continue;
  372.     case GSFoundExpected:
  373. break;
  374.     default:
  375. delete tag;
  376. tag = NULL;
  377. return XMLPBadAttribute;
  378. }
  379.     }
  380.     else
  381.     {
  382. tag->new_attribute()->name = 0;
  383.     }
  384.     if(bUseNonQuotedValues)
  385.     {
  386. if(bHasDirectives)
  387. {
  388.     res = GetString(cur, close,
  389.     tag->m_cur_attribute->value,
  390.     AttributeValueDirective);
  391. }
  392. else
  393. {
  394.     res = GetString(cur, close,
  395.     tag->m_cur_attribute->value,
  396.     AttributeValueNoQuote);
  397. }
  398.     }
  399.     else
  400.     {
  401. res = GetString(cur, close,
  402. tag->m_cur_attribute->value,
  403. AttributeValue);
  404.     }
  405.     if(res == GSMissingQuote)
  406.     {
  407. delete tag;
  408. tag = NULL;
  409. return XMLPAttributeValueNotQuoted;
  410.     }
  411.     else if(res != GSFoundExpected)
  412.     {
  413. delete tag;
  414. tag = NULL;
  415. return XMLPBadAttribute;
  416.     }
  417. }
  418.     }
  419.     return XMLPTag;
  420. }
  421. GetStringResult
  422. XMLTagParser::GetString(const char*& ptr, 
  423. const char* end, 
  424.         char*& val, 
  425. UINT32 type)
  426. {
  427.     GetStringResult retval = GSInvalid;
  428.     CHXXMLEncode xmlStr(m_pEncoding, (BYTE*)ptr, end - ptr);
  429.     UINT16 uLen = 0;
  430.     ptr = (const char*)xmlStr.GetNextChar(uLen);
  431.     while(isspace(*ptr) && ptr < end)
  432.     {
  433. ptr = (const char*)xmlStr.GetNextChar(uLen);
  434.     }
  435.     if((const char*)ptr >= end)
  436.     {
  437. return GSNoValue;
  438.     }
  439.     if(*ptr == '>')
  440.     {
  441. ptr = (const char*)xmlStr.GetNextChar(uLen);
  442. return GSNoValue;
  443.     }
  444.     if(*ptr == '/' && *(ptr + 1) == '>')
  445.     {
  446. xmlStr += 2;
  447. ptr = (const char*)xmlStr++;
  448. return GSNoValue;
  449.     }
  450.     // temp buffer to copy string value
  451.     char* pVal = new char[end - ptr + 1];
  452.     char* pValPtr = pVal;
  453.     char* pValStartPtr = pVal;
  454.     switch(type)
  455.     {
  456. case TagType:
  457. {
  458.     // The main tag name, delimited by space
  459.     if(*ptr == '/')
  460.     {
  461. retval = GSEndTag;
  462. pValStartPtr++;
  463.     }
  464.     while(!isspace(*ptr) && *ptr != '>' && ptr < end)
  465.     {
  466. *pValPtr++ = *ptr;
  467. if(uLen == 2)
  468. {
  469.     *pValPtr++ = *(ptr + 1);
  470. }
  471. ptr = (const char*)xmlStr.GetNextChar(uLen);
  472.     }
  473.     break;
  474. }
  475. case AttributeName:
  476. {
  477.     // Delimited by whitespace or =
  478.     while(!isspace(*ptr) && *ptr != '=' && *ptr != '>' && ptr < end)
  479.     {
  480. *pValPtr++ = *ptr;
  481. if(uLen == 2)
  482. {
  483.     *pValPtr++ = *(ptr + 1);
  484. }
  485. ptr = (const char*)xmlStr.GetNextChar(uLen);
  486.     }
  487.     BOOL foundequals = FALSE;
  488.     if(ptr < end)
  489.     {
  490. // Set the ptr to past the =
  491. while((isspace(*ptr) || *ptr == '=') && ptr < end)
  492. {
  493.     if(*ptr == '=')
  494. foundequals=TRUE;
  495.     ptr = (const char*)xmlStr.GetNextChar(uLen);
  496. }
  497.     }
  498.     if(!foundequals)
  499.     {
  500. retval = GSValueOnly;
  501.     }
  502.     break;
  503. }
  504. case AttributeValue:
  505. case AttributeValueNoQuote:
  506. case AttributeValueDirective:
  507. {
  508.     if(*ptr == '"')
  509.     {
  510. ptr = (const char*)xmlStr.GetNextChar(uLen);
  511. while(*ptr != '"' && ptr < end)
  512. {
  513.     if(*ptr == '&')
  514.     {
  515. *pValPtr = GetEscapeMacro(ptr, end);
  516. pValPtr++;
  517. xmlStr.SetCurrent((BYTE*)ptr);
  518.     }
  519.     else
  520.     {
  521. *pValPtr++ = *ptr;
  522. if(uLen == 2)
  523. {
  524.     *pValPtr++ = *(ptr + 1);
  525. }
  526.     }
  527.     ptr = (const char*)xmlStr.GetNextChar(uLen);
  528. }
  529. if(*ptr != '"')
  530. {
  531.     return GSMissingQuote;
  532. }
  533. /* Skip the quote */
  534. ptr = (const char*)xmlStr.GetNextChar(uLen);
  535.     }
  536.     else if(*ptr == ''')
  537.     {
  538. ptr = (const char*)xmlStr.GetNextChar(uLen);
  539. while(*ptr != ''' && ptr < end)
  540. {
  541.     if(*ptr == '&')
  542.     {
  543. *pValPtr = GetEscapeMacro(ptr, end);
  544. pValPtr++;
  545. xmlStr.SetCurrent((BYTE*)ptr);
  546.     }
  547.     else
  548.     {
  549. *pValPtr++ = *ptr;
  550. if(uLen == 2)
  551. {
  552.     *pValPtr++ = *(ptr + 1);
  553. }
  554.     }
  555.     ptr = (const char*)xmlStr.GetNextChar(uLen);
  556. }
  557. if(*ptr != ''')
  558. {
  559.     delete [] pVal;
  560.     return GSMissingQuote;
  561. }
  562. /* Skip the quote */
  563. ptr = (const char*)xmlStr.GetNextChar(uLen);
  564.     }
  565.     else if(*ptr == '[' && type == AttributeValueDirective)
  566.     {
  567. ptr = (const char*)xmlStr.GetNextChar(uLen);
  568. while(*ptr != ']' && ptr < end)
  569. {
  570.     *pValPtr++ = *ptr;
  571.     if(uLen == 2)
  572.     {
  573. *pValPtr++ = *(ptr + 1);
  574.     }
  575.     ptr = (const char*)xmlStr.GetNextChar(uLen);
  576. }
  577. if(*ptr != ']')
  578. {
  579.     delete[] pVal;
  580.     return GSMissingQuote;
  581. }
  582. /* skip the ']' */
  583. ptr = (const char*)xmlStr.GetNextChar(uLen);
  584.     }
  585.     else
  586.     {
  587. /* don't care!!! */
  588. while(!isspace(*ptr) && *ptr != '>' && ptr < end)
  589. {
  590.     *pValPtr++ = *ptr;
  591.     if(uLen == 2)
  592.     {
  593. *pValPtr++ = *(ptr + 1);
  594.     }
  595.     ptr = (const char*)xmlStr.GetNextChar(uLen);
  596. }
  597.     }
  598.     break;
  599. }
  600.     }
  601.     *pValPtr = '';
  602.     val = new_string(pValStartPtr);
  603.     delete [] pVal;
  604.     if(retval == GSInvalid)
  605. return GSFoundExpected;
  606.     else
  607. return retval;
  608. }
  609. void
  610. XMLTagParser::FindCommentClose(const char*& buf, 
  611.        const char* start,
  612.        const char* end)
  613. {
  614.     UINT16 nCommentDepth = 1;
  615.     CHXXMLEncode xmlStr(m_pEncoding, (BYTE*)start, end - start);
  616.     UINT16 uLen = 0;
  617.     const char* pos = (const char*)xmlStr.GetNextChar(uLen);
  618.     while(pos < end && m_comment_state > 0)
  619.     {
  620. switch(m_comment_state)
  621. {
  622.     case 1:
  623. if(*pos == '-')
  624.     m_comment_state = 2;
  625. else if (*pos == '<')
  626.     m_comment_state = 4;
  627. else if(m_comment_start)
  628. {
  629.     if(*pos == '#')
  630.     {
  631. if(end - pos < 8)
  632. {
  633.     buf = pos;
  634.     return;
  635. }
  636. pos = (const char*)xmlStr.GetNextChar(uLen);
  637. if(strncasecmp(pos, "include", 7) == 0)
  638. {
  639.     pos += 7;
  640.     m_comment_get_arg = 1;
  641.     m_comment_pos = 0;
  642.     strcpy(m_comment_command, "include"); /* Flawfinder: ignore */
  643. }
  644.     }
  645. }
  646. break;
  647.     case 2:
  648. if(*pos == '-')
  649.     m_comment_state = 3;
  650. else
  651.     m_comment_state = 1;
  652. break;
  653.     case 3:
  654. if(*pos == '>')
  655. {
  656.     nCommentDepth--;
  657.     if (nCommentDepth == 0)
  658.     {
  659. m_comment_state = 0;
  660. buf = (const char*)xmlStr.GetNextChar(uLen);
  661.     }
  662.     else
  663. m_comment_state = 1;
  664. }
  665. else
  666.     m_comment_state = 1;
  667. break;
  668.     case 4:
  669. // Ignore nested comments while looking for our end tag
  670. if (*pos == '!')
  671.     m_comment_state = 5;
  672. else
  673.     m_comment_state = 1;
  674. break;
  675.     case 5:
  676. if (*pos == '-')
  677.     m_comment_state = 6;
  678. else
  679.     m_comment_state = 1;
  680. break;
  681.     case 6:
  682. if (*pos == '-')
  683. {
  684.     nCommentDepth++;
  685. }
  686. m_comment_state = 1;
  687. break;
  688. }
  689. if(m_comment_state > 0)
  690. {
  691.     switch(m_comment_get_arg)
  692.     {
  693. case 1:
  694.     if(*pos != '"' && !isspace(*pos))
  695. m_comment_get_arg = 0;
  696.     else if(*pos == '"')
  697. m_comment_get_arg = 2;
  698.     break;
  699. case 2:
  700.     if(*pos != '"')
  701. if (m_comment_pos < 1023) m_comment_arg[m_comment_pos++] = *pos;
  702.     else
  703.     {
  704. if (m_comment_pos < 1024) m_comment_arg[m_comment_pos] = 0;
  705. m_comment_get_arg = 3;
  706.     }
  707.     break;
  708. default:
  709.     break;
  710.     }
  711. }
  712. pos = (const char*)xmlStr.GetNextChar(uLen);
  713.     }
  714. }
  715. char
  716. XMLTagParser::GetEscapeMacro(const char*& ptr, const char* end)
  717. {
  718.     char returnCh;
  719.     if(*ptr != '&')
  720.     {
  721. returnCh = *ptr;
  722.     }
  723.     else
  724.     {
  725. int maxLen = end - ptr;
  726. if((maxLen > 5) && strncmp(ptr, "&apos;", 6) == 0)
  727. {
  728.     returnCh = ''';
  729.     ptr += 6;
  730. }
  731. else if((maxLen > 5) && strncmp(ptr, "&quot;", 6) == 0)
  732. {
  733.     returnCh = '"';
  734.     ptr += 6;
  735. }
  736. else if((maxLen > 3) && strncmp(ptr, "&lt;", 4) == 0)
  737. {
  738.     returnCh = '<';
  739.     ptr += 4;
  740. }
  741. else if((maxLen > 3) && strncmp(ptr, "&gt;", 4) == 0)
  742. {
  743.     returnCh = '>';
  744.     ptr += 4;
  745. }
  746. else if((maxLen > 4) && strncmp(ptr, "&amp;", 5) == 0)
  747. {
  748.     returnCh = '&';
  749.     ptr += 5;
  750. }
  751. else
  752. {
  753.     returnCh = '&';
  754.     ptr++;
  755. }
  756.     }
  757.     return returnCh;
  758. }