HTMLSCAN.CXX
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:18k
源码类别:

Windows编程

开发平台:

Visual C++

  1. //+---------------------------------------------------------------------------
  2. //
  3. //  Copyright (C) 1992 - 1997 Microsoft Corporation.
  4. //
  5. //  File:       htmlscan.cxx
  6. //
  7. //  Contents:   Scanner for html files
  8. //
  9. //  Classes:    CHtmlScanner
  10. //
  11. //----------------------------------------------------------------------------
  12. #include <pch.cxx>
  13. #pragma hdrstop
  14. #include <htmlguid.hxx>
  15. #include <charhash.hxx>
  16. #include <htmlfilt.hxx>
  17. //+-------------------------------------------------------------------------
  18. //
  19. //  Method:     CToken::IsMatchProperty
  20. //
  21. //  Synopsis:   Does the token's property match the given property ?
  22. //
  23. //  Arguments:  [propSpec]   -- Property to match
  24. //
  25. //--------------------------------------------------------------------------
  26. BOOL CToken::IsMatchProperty( CFullPropSpec& propSpec )
  27. {
  28.     if ( propSpec.IsPropertyPropid()
  29.          && propSpec.GetPropSet() == _guidPropset
  30.          && propSpec.GetPropertyPropid() == _propid )
  31.     {
  32.         return TRUE;
  33.     }
  34.     else
  35.         return FALSE;
  36. }
  37. //+-------------------------------------------------------------------------
  38. //
  39. //  Method:     CHtmlScanner::CHtmlScanner
  40. //
  41. //  Synopsis:   Constructor
  42. //
  43. //  Arguments:  [htmlIFilter]    -- Reference to Html filter
  44. //              [serialStream]   -- Reference to input stream to scan
  45. //
  46. //--------------------------------------------------------------------------
  47. CHtmlScanner::CHtmlScanner( CHtmlIFilter& htmlIFilter,
  48.                             CSerialStream& serialStream )
  49.     : _htmlIFilter(htmlIFilter),
  50.       _serialStream(serialStream),
  51.       _uLenTagBuf(TAG_BUFFER_SIZE),
  52.       _cTagCharsRead(0)
  53. {
  54.     _pwcTagBuf = newk(mtNewX, NULL) WCHAR[ TAG_BUFFER_SIZE ];
  55. }
  56. //+-------------------------------------------------------------------------
  57. //
  58. //  Method:     CHtmlScanner::~CHtmlScanner
  59. //
  60. //  Synopsis:   Destructor
  61. //
  62. //--------------------------------------------------------------------------
  63. CHtmlScanner::~CHtmlScanner()
  64. {
  65.     delete[] _pwcTagBuf;
  66. }
  67. //+-------------------------------------------------------------------------
  68. //
  69. //  Method:     CHtmlScanner::GetBlockOfChars
  70. //
  71. //  Synopsis:   Returns a block of chars upto the size requested by user. If
  72. //              any Html tag is encountered, it stops scanning, and returns the
  73. //              token found.
  74. //
  75. //  Arguments:  [cCharsNeeded]   -- Maximum # chars to scan
  76. //              [awcBuffer]      -- Buffer to fill with scanned chars
  77. //              [cCharsScanned]  -- # chars actually scanned
  78. //              [token]          -- Token found (if any)
  79. //
  80. //--------------------------------------------------------------------------
  81. void CHtmlScanner::GetBlockOfChars( ULONG cCharsNeeded,
  82.                                     WCHAR *awcBuffer,
  83.                                     ULONG& cCharsScanned,
  84.                                     CToken& token )
  85. {
  86.     cCharsScanned = 0;
  87.     while ( cCharsNeeded > 0 )
  88.     {
  89.         if ( _serialStream.Eof() )
  90.         {
  91.             token.SetTokenType( EofToken );
  92.             return;
  93.         }
  94.         WCHAR wch = _serialStream.GetChar();
  95.         if ( wch == L'<' )
  96.         {
  97.             //
  98.             // Html tag encountered
  99.             //
  100.             ScanTag( token );
  101.             return;
  102.         }
  103.         else
  104.         {
  105.             //
  106.             // &lt; and &gt; were mapped to Unicode chars from private use area
  107.             // to avoid collision with '<' and '>' chars in Html tags. Map them
  108.             // back to '<' and '>'.
  109.             //
  110.             if ( wch == PRIVATE_USE_MAPPING_FOR_LT )
  111.                 wch = L'<';
  112.             else if ( wch == PRIVATE_USE_MAPPING_FOR_GT )
  113.                 wch = L'>';
  114.             awcBuffer[cCharsScanned++] = wch;
  115.             cCharsNeeded--;
  116.         }
  117.     }
  118. }
  119. //+-------------------------------------------------------------------------
  120. //
  121. //  Method:     CHtmlScanner::SkipCharsUntilNextRelevantToken
  122. //
  123. //  Synopsis:   Skips characters in input until EOF or an interesting token
  124. //              is found. The list of properties that were asked to be filtered
  125. //              as part of the IFilter::Init call determines whether a token is
  126. //              interesting or not.
  127. //
  128. //  Arguments:  [fFilterContents]     -- Are contents filtered ?
  129. //              [fFilterProperties]   -- Are properties filtered ?
  130. //              [cAttributes]          -- Count of properties
  131. //              [pAttributes]         -- List of properties to be filtered
  132. //
  133. //--------------------------------------------------------------------------
  134. void CHtmlScanner::SkipCharsUntilNextRelevantToken( CToken& token )
  135. {
  136.     //
  137.     // Loop until we find a stop token or end of file
  138.     //
  139.     for (;;)
  140.     {
  141.         if ( _serialStream.Eof() )
  142.         {
  143.             token.SetTokenType( EofToken );
  144.             return;
  145.         }
  146.         WCHAR wch = _serialStream.GetChar();
  147.         if ( wch == L'<' )
  148.         {
  149.             ScanTag( token );
  150.             if ( token.GetTokenType() == EofToken
  151.                  || _htmlIFilter.IsStopToken( token ) )
  152.             {
  153.                 return;
  154.             }
  155.             else
  156.             {
  157.                 //
  158.                 // Uninteresting tag, hence skip tag
  159.                 //
  160.                 EatTag();
  161.             }
  162.         }
  163.         else
  164.         {
  165.             //
  166.             // Vanilla text
  167.             //
  168.             if ( _htmlIFilter.FFilterContent() )
  169.             {
  170.                 _serialStream.UnGetChar( wch );
  171.                 token.SetTokenType( TextToken );
  172.                 return;
  173.             }
  174.             else
  175.                 EatText();
  176.         }
  177.     }
  178. }
  179. //+-------------------------------------------------------------------------
  180. //
  181. //  Method:     CHtmlScanner::ScanTag
  182. //
  183. //  Synopsis:   Scans a Html tag from input
  184. //
  185. //  Arguments:  [token]  -- Token info returned here
  186. //
  187. //--------------------------------------------------------------------------
  188. void CHtmlScanner::ScanTag( CToken& token )
  189. {
  190.     EatBlanks();
  191.     if ( _serialStream.Eof() )
  192.     {
  193.         token.SetTokenType( EofToken );
  194.         return;
  195.     }
  196.     WCHAR wch = _serialStream.GetChar();
  197.     token.SetStartTokenFlag( TRUE );
  198.     if ( wch == L'/' )
  199.     {
  200.         //
  201.         // This is an end tag
  202.         //
  203.         token.SetStartTokenFlag( FALSE );
  204.         EatBlanks();
  205.         if ( _serialStream.Eof() )
  206.         {
  207.             token.SetTokenType( EofToken );
  208.             return;
  209.         }
  210.         wch = _serialStream.GetChar();
  211.     }
  212.     WCHAR awcTagName[MAX_TAG_LENGTH+1];
  213.     unsigned uLenTag = 0;
  214.     //
  215.     // Scan the tag name into szTagName. We scan MAX_TAG_LENGTH
  216.     // characters only, because anything longer is most probably
  217.     // a bogus tag.
  218.     //
  219.     while ( !iswspace(wch)
  220.             && wch != L'>'
  221.             && uLenTag < MAX_TAG_LENGTH )
  222.     {
  223.         awcTagName[uLenTag++] = wch;
  224.         if ( _serialStream.Eof() )
  225.             break;
  226.         wch = _serialStream.GetChar();
  227.     }
  228.     awcTagName[uLenTag] = 0;
  229.     if ( _serialStream.Eof() )
  230.     {
  231.         token.SetTokenType( EofToken );
  232.         return;
  233.     }
  234.     else if ( wch == L'>' || uLenTag == MAX_TAG_LENGTH )
  235.     {
  236.         //
  237.         // Push char back into input stream because a subsequent GetChar()
  238.         // will be expecting to see the char in the input
  239.         //
  240.         _serialStream.UnGetChar( wch );
  241.     }
  242.     TagNameToToken( awcTagName, token );
  243. }
  244. //+-------------------------------------------------------------------------
  245. //
  246. //  Method:     CHtmlScanner::ReadTagIntoBuffer
  247. //
  248. //  Synopsis:   Reads the rest of Html tag into the internal buffer
  249. //
  250. //--------------------------------------------------------------------------
  251. void CHtmlScanner::ReadTagIntoBuffer()
  252. {
  253.     _cTagCharsRead = 0;
  254.     if ( _serialStream.Eof() )
  255.         return;
  256.     WCHAR wch = _serialStream.GetChar();
  257.     while ( wch != L'>' )
  258.     {
  259.         if ( _cTagCharsRead >= _uLenTagBuf )
  260.             GrowTagBuffer();
  261.         Win4Assert( _cTagCharsRead < _uLenTagBuf );
  262.         _pwcTagBuf[_cTagCharsRead++] = wch;
  263.         if ( _serialStream.Eof() )
  264.             return;
  265.         wch = _serialStream.GetChar();
  266.     }
  267. }
  268. //+-------------------------------------------------------------------------
  269. //
  270. //  Method:     CHtmlScanner::ScanTagBuffer
  271. //
  272. //  Synopsis:   Scans the internal tag buffer for a given name, and returns the
  273. //              corresponding value
  274. //
  275. //  Arguments:  [awcName]   -- Pattern to match
  276. //              [pwcValue]  -- Start position of value returned here
  277. //              [uLenValue  -- Length of value field
  278. //
  279. //--------------------------------------------------------------------------
  280. void CHtmlScanner::ScanTagBuffer( WCHAR *awcName,
  281.                                   WCHAR * & pwcValue,
  282.                                   unsigned& uLenValue )
  283. {
  284.     unsigned uLenName = wcslen( awcName );
  285.     if ( _cTagCharsRead <= uLenName )
  286.     {
  287.         //
  288.         // Pattern to match is longer than scanned tag
  289.         //
  290.         pwcValue = 0;
  291.         uLenValue = 0;
  292.         return;
  293.     }
  294.     for ( unsigned i=0; i<_cTagCharsRead-uLenName; i++ )
  295.     {
  296.         BOOL fMatch = TRUE;
  297.         for ( unsigned j=0; j<uLenName; j++ )
  298.         {
  299.             //
  300.             // Case insensitive match
  301.             //
  302.             if ( towlower(awcName[j]) != towlower(_pwcTagBuf[i+j]) )
  303.             {
  304.                 fMatch = FALSE;
  305.                 break;
  306.             }
  307.         }
  308.         if ( fMatch )
  309.         {
  310.             unsigned k = i + uLenName;
  311.             while ( _pwcTagBuf[k] != L'"' && k < _cTagCharsRead )
  312.                 k++;
  313.             uLenValue = k - (i + uLenName);
  314.             pwcValue = &_pwcTagBuf[i+uLenName];
  315.             return;
  316.         }
  317.     }
  318.     uLenValue = 0;
  319.     pwcValue = 0;
  320. }
  321. //+-------------------------------------------------------------------------
  322. //
  323. //  Method:     CHtmlScanner::EatTag
  324. //
  325. //  Synopsis:   Skips characters in input until the '>' char, which demarcates
  326. //              the end of the tag
  327. //
  328. //--------------------------------------------------------------------------
  329. void CHtmlScanner::EatTag()
  330. {
  331.     if ( _serialStream.Eof() )
  332.         return;
  333.     WCHAR wch = _serialStream.GetChar();
  334.     while ( wch != L'>' &&  !_serialStream.Eof() )
  335.         wch = _serialStream.GetChar();
  336. }
  337. //+-------------------------------------------------------------------------
  338. //
  339. //  Method:     CHtmlScanner::EatText
  340. //
  341. //  Synopsis:   Skips characters in input until a '<', ie a tag is encountered
  342. //
  343. //--------------------------------------------------------------------------
  344. void CHtmlScanner::EatText()
  345. {
  346.     if ( _serialStream.Eof() )
  347.         return;
  348.     WCHAR wch = _serialStream.GetChar();
  349.     while ( wch != L'<' && !_serialStream.Eof() )
  350.         wch = _serialStream.GetChar();
  351.     if ( wch == L'<' )
  352.         _serialStream.UnGetChar( wch );
  353. }
  354. //+-------------------------------------------------------------------------
  355. //
  356. //  Method:     CHtmlScanner::EatBlanks
  357. //
  358. //  Synopsis:   Skips generic white space characters in input
  359. //
  360. //--------------------------------------------------------------------------
  361. void CHtmlScanner::EatBlanks()
  362. {
  363.     if ( _serialStream.Eof() )
  364.         return;
  365.     WCHAR wch = _serialStream.GetChar();
  366.     while ( iswspace(wch) && !_serialStream.Eof() )
  367.         wch = _serialStream.GetChar();
  368.     if ( !iswspace(wch) )
  369.         _serialStream.UnGetChar( wch );
  370. }
  371. //+-------------------------------------------------------------------------
  372. //
  373. //  Method:     CHtmlScanner::TagNameToToken
  374. //
  375. //  Synopsis:   Maps a tag name to token information
  376. //
  377. //  Arguments:  [awcTagName]  -- Tag name to map
  378. //              [token]       -- Token information returned here
  379. //
  380. //--------------------------------------------------------------------------
  381. void CHtmlScanner::TagNameToToken( WCHAR *awcTagName, CToken& token )
  382. {
  383.     //
  384.     // The number of interesting Html tags will be small, hence no need for
  385.     // a table lookup
  386.     //
  387.     switch( awcTagName[0] )
  388.     {
  389.     case L'a':
  390.     case L'A':
  391.         if ( _wcsicmp( awcTagName, L"a" ) == 0 )
  392.         {
  393.             token.SetTokenType( AnchorToken );
  394.             token.SetPropset( CLSID_HtmlInformation );
  395.             token.SetPropid( PID_HREF );
  396.         }
  397.         else if ( _wcsicmp( awcTagName, L"address" ) == 0 )
  398.             token.SetTokenType( BreakToken );
  399.         else
  400.             token.SetTokenType( GenericToken );
  401.         break;
  402.     case L'b':
  403.     case L'B':
  404.         if ( _wcsicmp( awcTagName, L"br" ) == 0
  405.              || _wcsicmp( awcTagName, L"blockquote" ) == 0 )
  406.         {
  407.             token.SetTokenType( BreakToken );
  408.         }
  409.         else
  410.             token.SetTokenType( GenericToken );
  411.         break;
  412.     case L'd':
  413.     case L'D':
  414.         if ( _wcsicmp( awcTagName, L"dd" ) == 0
  415.              || _wcsicmp( awcTagName, L"dl" ) == 0
  416.              || _wcsicmp( awcTagName, L"dt" ) == 0 )
  417.         {
  418.             token.SetTokenType( BreakToken );
  419.         }
  420.         else
  421.             token.SetTokenType( GenericToken );
  422.         break;
  423.     case L'f':
  424.     case L'F':
  425.         if ( _wcsicmp( awcTagName, L"form" ) == 0 )
  426.             token.SetTokenType( BreakToken );
  427.         else
  428.             token.SetTokenType( GenericToken );
  429.         break;
  430.     case L'h':
  431.     case L'H':
  432.         if ( _wcsicmp( awcTagName, L"h1" ) == 0 )
  433.         {
  434.             token.SetTokenType( Heading1Token );
  435.             token.SetPropset( CLSID_HtmlInformation );
  436.             token.SetPropid( PID_HEADING_1 );
  437.         }
  438.         else if ( _wcsicmp( awcTagName, L"h2" ) == 0 )
  439.         {
  440.             token.SetTokenType( Heading2Token );
  441.             token.SetPropset( CLSID_HtmlInformation );
  442.             token.SetPropid( PID_HEADING_2 );
  443.         }
  444.         else if ( _wcsicmp( awcTagName, L"h3" ) == 0 )
  445.         {
  446.             token.SetTokenType( Heading3Token );
  447.             token.SetPropset( CLSID_HtmlInformation );
  448.             token.SetPropid( PID_HEADING_3 );
  449.         }
  450.         else if ( _wcsicmp( awcTagName, L"h4" ) == 0 )
  451.         {
  452.             token.SetTokenType( Heading4Token );
  453.             token.SetPropset( CLSID_HtmlInformation );
  454.             token.SetPropid( PID_HEADING_4 );
  455.         }
  456.         else if ( _wcsicmp( awcTagName, L"h5" ) == 0 )
  457.         {
  458.             token.SetTokenType( Heading5Token );
  459.             token.SetPropset( CLSID_HtmlInformation );
  460.             token.SetPropid( PID_HEADING_5 );
  461.         }
  462.         else if ( _wcsicmp( awcTagName, L"h6" ) == 0 )
  463.         {
  464.             token.SetTokenType( Heading6Token );
  465.             token.SetPropset( CLSID_HtmlInformation );
  466.             token.SetPropid( PID_HEADING_6 );
  467.         }
  468.         else
  469.             token.SetTokenType( GenericToken );
  470.         break;
  471.     case L'i':
  472.     case L'I':
  473.         if ( _wcsicmp( awcTagName, L"input" ) == 0 )
  474.             token.SetTokenType( InputToken );
  475.         else if ( _wcsicmp( awcTagName, L"img" ) == 0 )
  476.             token.SetTokenType( ImageToken );
  477.         else
  478.             token.SetTokenType( GenericToken );
  479.         break;
  480.     case L'l':
  481.     case L'L':
  482.         if ( _wcsicmp( awcTagName, L"li" ) == 0 )
  483.             token.SetTokenType( BreakToken );
  484.         else
  485.             token.SetTokenType( GenericToken );
  486.         break;
  487.     case L'm':
  488.     case L'M':
  489.         if ( _wcsicmp( awcTagName, L"math" ) == 0 )
  490.             token.SetTokenType( BreakToken );
  491.         else if (  _wcsicmp( awcTagName, L"meta" ) == 0 )
  492.             token.SetTokenType( MetaToken );
  493.         else
  494.             token.SetTokenType( GenericToken );
  495.         break;
  496.     case L'o':
  497.     case L'O':
  498.         if ( _wcsicmp( awcTagName, L"ol" ) == 0 )
  499.             token.SetTokenType( BreakToken );
  500.         else
  501.             token.SetTokenType( GenericToken );
  502.         break;
  503.     case L'p':
  504.     case L'P':
  505.         if ( _wcsicmp( awcTagName, L"p" ) == 0 )
  506.             token.SetTokenType( BreakToken );
  507.         else
  508.             token.SetTokenType( GenericToken );
  509.         break;
  510.     case L's':
  511.     case L'S':
  512.         if ( _wcsicmp( awcTagName, L"script" ) == 0 )
  513.             token.SetTokenType( ScriptToken );
  514.         else
  515.             token.SetTokenType( GenericToken );
  516.         break;
  517.     case L't':
  518.     case L'T':
  519.         if ( _wcsicmp( awcTagName, L"title" ) == 0 )
  520.         {
  521.             token.SetTokenType( TitleToken );
  522.             token.SetPropset( CLSID_SummaryInformation );
  523.             token.SetPropid( PID_TITLE );
  524.         }
  525.         else if ( _wcsicmp( awcTagName, L"table" ) == 0
  526.                   || _wcsicmp( awcTagName, L"th" ) == 0
  527.                   || _wcsicmp( awcTagName, L"tr" ) == 0
  528.                   || _wcsicmp( awcTagName, L"td" ) == 0 )
  529.             token.SetTokenType( BreakToken );
  530.         else
  531.             token.SetTokenType( GenericToken );
  532.         break;
  533.     case L'u':
  534.     case L'U':
  535.         if ( _wcsicmp( awcTagName, L"ul" ) == 0 )
  536.             token.SetTokenType( BreakToken );
  537.         else
  538.             token.SetTokenType( GenericToken );
  539.         break;
  540.     case L'!':
  541.         if  ( _wcsicmp( awcTagName, L"!--" ) == 0 )
  542.             token.SetTokenType( CommentToken );
  543.         else
  544.             token.SetTokenType( GenericToken );
  545.         break;
  546.     default:
  547.         //
  548.         // It's an uninteresting tag
  549.         //
  550.         token.SetTokenType( GenericToken );
  551.     }
  552. }
  553. //+-------------------------------------------------------------------------
  554. //
  555. //  Method:     CHtmlScanner::GrowTagBuffer
  556. //
  557. //  Synopsis:   Grow internal tag buffer to twice its current size
  558. //
  559. //--------------------------------------------------------------------------
  560. void CHtmlScanner::GrowTagBuffer()
  561. {
  562.     WCHAR *pwcNewTagBuf = newk(mtNewX, NULL) WCHAR[2 * _uLenTagBuf];
  563.     RtlCopyMemory( pwcNewTagBuf,
  564.                    _pwcTagBuf,
  565.                    _uLenTagBuf * sizeof(WCHAR) );
  566.     delete[] _pwcTagBuf;
  567.     _uLenTagBuf *= 2;
  568.     _pwcTagBuf = pwcNewTagBuf;
  569. }