Source code of Windows XP (NT5)
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.

1074 lines
29 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1991 - 2001.
  5. //
  6. // File: TFILT.CXX
  7. //
  8. // Contents: Text Filter
  9. //
  10. // History: 16-Jul-93 AmyA Created
  11. // 23-Feb-94 KyleP Cleaned up
  12. //
  13. //----------------------------------------------------------------------------
  14. #include <pch.cxx>
  15. #pragma hdrstop
  16. #include <scode.h>
  17. #include <tfilt.hxx>
  18. #include <pfilter.hxx>
  19. #include <codepage.hxx>
  20. // has to be COMMON_PAGE_SIZE multiple
  21. const ULONG TEXT_FILTER_CHUNK_SIZE = 1 * COMMON_PAGE_SIZE;
  22. GUID const guidStorage = PSGUID_STORAGE;
  23. extern "C" GUID CLSID_CTextIFilter;
  24. extern ULONG g_cbMaxTextFilter;
  25. //+---------------------------------------------------------------------------
  26. //
  27. // Member: CTextIFilter::CTextIFilter, public
  28. //
  29. // Synopsis: Constructor
  30. //
  31. // History: 16-Jul-93 AmyA Created.
  32. //
  33. //----------------------------------------------------------------------------
  34. CTextIFilter::CTextIFilter( LCID locale )
  35. : _idChunk(1),
  36. _bytesReadFromChunk(0),
  37. _pwszFileName(0),
  38. _pStream(0),
  39. _fUniCode(FALSE),
  40. _fBigEndian(FALSE),
  41. _fContents(FALSE),
  42. _fNSS(FALSE),
  43. _locale(locale),
  44. _fDBCSSplitChar(FALSE)
  45. {
  46. //
  47. // We need a code page for MultiByteToWideChar.
  48. //
  49. _ulCodePage = LocaleToCodepage( locale );
  50. _fDBCSCodePage = IsDBCSCodePage( _ulCodePage );
  51. }
  52. //+---------------------------------------------------------------------------
  53. //
  54. // Member: CTextIFilter::~CTextIFilter, public
  55. //
  56. // Synopsis: Destructor
  57. //
  58. // History: 16-Jul-93 AmyA Created.
  59. //
  60. //----------------------------------------------------------------------------
  61. CTextIFilter::~CTextIFilter()
  62. {
  63. delete [] _pwszFileName;
  64. if ( 0 != _pStream )
  65. _pStream->Release();
  66. }
  67. //+---------------------------------------------------------------------------
  68. //
  69. // Member: CTextIFilter::Init, public
  70. //
  71. // Synopsis: Initializes instance of text filter
  72. //
  73. // Arguments: [grfFlags] -- Flags for filter behavior
  74. // [cAttributes] -- Number of strings in array ppwcsAttributes
  75. // [aAttributes] -- Array of attribute strings
  76. // [pFlags] -- Must be zero for Cairo V1
  77. //
  78. // History: 16-Jul-93 AmyA Created.
  79. // 23-Feb-94 KyleP Cleaned up.
  80. //
  81. //----------------------------------------------------------------------------
  82. SCODE STDMETHODCALLTYPE CTextIFilter::Init( ULONG grfFlags,
  83. ULONG cAttributes,
  84. FULLPROPSPEC const * aAttributes,
  85. ULONG * pFlags )
  86. {
  87. SCODE sc = S_OK;
  88. //
  89. // Text files only support the 'contents' attribute.
  90. //
  91. if( cAttributes > 0 )
  92. {
  93. //
  94. // Known, safe cast
  95. //
  96. CFullPropSpec const * pAttrib = (CFullPropSpec const *)aAttributes;
  97. if ( pAttrib == 0 )
  98. return E_FAIL;
  99. for ( ULONG i = 0; i < cAttributes; i++ )
  100. {
  101. if ( pAttrib[i].IsPropertyPropid() &&
  102. pAttrib[i].GetPropertyPropid() == PID_STG_CONTENTS &&
  103. memcmp( &pAttrib[i].GetPropSet(),
  104. &guidStorage,
  105. sizeof(guidStorage) ) == 0 )
  106. {
  107. break;
  108. }
  109. }
  110. if ( i < cAttributes )
  111. _fContents = TRUE;
  112. else
  113. _fContents = FALSE;
  114. }
  115. else if ( 0 == grfFlags || (grfFlags & IFILTER_INIT_APPLY_INDEX_ATTRIBUTES) )
  116. {
  117. _fContents = TRUE;
  118. }
  119. else
  120. _fContents = FALSE;
  121. //
  122. // Open memory-mapped file
  123. //
  124. if ( 0 != _pwszFileName )
  125. {
  126. if ( _mmStream.Ok() )
  127. {
  128. _mmStreamBuf.Rewind();
  129. }
  130. else
  131. {
  132. if ( !_fNSS )
  133. {
  134. TRY
  135. {
  136. _mmStreamBuf.Rewind();
  137. _mmStream.Close();
  138. _mmStream.Open( _pwszFileName,
  139. GENERIC_READ,
  140. FILE_SHARE_READ,
  141. OPEN_EXISTING );
  142. if ( _mmStream.Ok() )
  143. _mmStreamBuf.Init( &_mmStream );
  144. else
  145. {
  146. sc = ( STATUS_ACCESS_DENIED == GetLastError() ? // Open sets the last error
  147. FILTER_E_PASSWORD :
  148. FILTER_E_ACCESS );
  149. }
  150. }
  151. CATCH( CException, e )
  152. {
  153. if ( e.GetErrorCode() == HRESULT_FROM_WIN32( ERROR_BAD_EXE_FORMAT ) )
  154. {
  155. _fNSS = TRUE;
  156. sc = S_OK;
  157. }
  158. else
  159. {
  160. sc = ( STATUS_ACCESS_DENIED == e.GetErrorCode() ?
  161. FILTER_E_PASSWORD :
  162. FILTER_E_ACCESS );
  163. }
  164. }
  165. END_CATCH;
  166. }
  167. }
  168. }
  169. //
  170. // ...or, Memory mapped stream
  171. //
  172. else if ( 0 != _pStream )
  173. {
  174. if ( _mmIStream.Ok() )
  175. {
  176. _mmStreamBuf.Rewind();
  177. }
  178. else
  179. {
  180. TRY
  181. {
  182. _mmIStream.Close();
  183. _mmIStream.Open( _pStream );
  184. if ( _mmIStream.Ok() )
  185. _mmStreamBuf.Init( &_mmIStream );
  186. else
  187. {
  188. sc = ( STG_E_ACCESSDENIED == GetLastError() ?
  189. FILTER_E_PASSWORD :
  190. FILTER_E_ACCESS );
  191. }
  192. }
  193. CATCH( CException, e )
  194. {
  195. sc = ( STG_E_ACCESSDENIED == e.GetErrorCode() ?
  196. FILTER_E_PASSWORD :
  197. FILTER_E_ACCESS );
  198. }
  199. END_CATCH;
  200. }
  201. }
  202. //
  203. // Might as well try filtering properties.
  204. //
  205. *pFlags = IFILTER_FLAGS_OLE_PROPERTIES;
  206. //
  207. // Re-initialize
  208. //
  209. _idChunk = 1;
  210. _bytesReadFromChunk = 0;
  211. _fDBCSSplitChar = FALSE;
  212. return sc;
  213. }
  214. //+---------------------------------------------------------------------------
  215. //
  216. // Member: CTextIFilter::GetChunk, public
  217. //
  218. // Synopsis: Gets the next chunk and returns chunk information in ppStat
  219. //
  220. // Arguments: [ppStat] -- for chunk information
  221. //
  222. // History: 16-Jul-93 AmyA Created.
  223. //
  224. //----------------------------------------------------------------------------
  225. SCODE STDMETHODCALLTYPE CTextIFilter::GetChunk( STAT_CHUNK * pStat )
  226. {
  227. //
  228. // Error checking
  229. //
  230. if ( _fNSS )
  231. return FILTER_E_END_OF_CHUNKS;
  232. if ( (IsFileBased() && !_mmStream.Ok()) ||
  233. (IsStreamBased() && !_mmIStream.Ok()) )
  234. return FILTER_E_ACCESS;
  235. if ( !_fContents || _mmStreamBuf.Eof() )
  236. return FILTER_E_END_OF_CHUNKS;
  237. // Is the file to big? If so, stop filtering now
  238. if ( (ULONGLONG) _mmStreamBuf.CurrentOffset() > (ULONGLONG) g_cbMaxTextFilter )
  239. return FILTER_E_PARTIALLY_FILTERED;
  240. SCODE sc = S_OK;
  241. TRY
  242. {
  243. _mmStreamBuf.Map( TEXT_FILTER_CHUNK_SIZE );
  244. }
  245. CATCH( CException, e )
  246. {
  247. return FILTER_E_ACCESS;
  248. }
  249. END_CATCH;
  250. _bytesReadFromChunk = 0;
  251. //
  252. // If this is the first time we've touched the file, determine if it
  253. // is a UniCode or ASCII stream. The first two bytes of all UniCode
  254. // plain text streams are 0xff 0xfe
  255. //
  256. WCHAR const wcUniCode = 0xfeff;
  257. WCHAR const wcBigUniCode = 0xfffe;
  258. if ( _idChunk == 1 )
  259. {
  260. //
  261. // Are there at least two bytes in file?
  262. //
  263. if ( _mmStreamBuf.Size() >= 2 )
  264. {
  265. if ( *(WCHAR *)(_mmStreamBuf.Get()) == wcUniCode &&
  266. _mmStreamBuf.Size() % sizeof(WCHAR) == 0 )
  267. {
  268. _fUniCode = TRUE;
  269. _bytesReadFromChunk += sizeof(WCHAR);
  270. }
  271. else if ( *(WCHAR *)(_mmStreamBuf.Get()) == wcBigUniCode &&
  272. _mmStreamBuf.Size() % sizeof(WCHAR) == 0 )
  273. {
  274. _fUniCode = TRUE;
  275. _fBigEndian = TRUE;
  276. _bytesReadFromChunk += sizeof(WCHAR);
  277. }
  278. else
  279. _fUniCode = FALSE;
  280. }
  281. else
  282. _fUniCode = FALSE;
  283. }
  284. pStat->idChunk = _idChunk;
  285. pStat->flags = CHUNK_TEXT;
  286. pStat->locale = _locale;
  287. pStat->attribute.guidPropSet = guidStorage;
  288. pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
  289. pStat->attribute.psProperty.propid = PID_STG_CONTENTS;
  290. pStat->breakType = CHUNK_NO_BREAK;
  291. pStat->idChunkSource = _idChunk;
  292. pStat->cwcStartSource = 0;
  293. pStat->cwcLenSource = 0;
  294. _idChunk++;
  295. return sc;
  296. }
  297. //+---------------------------------------------------------------------------
  298. //
  299. // Member: CTextIFilter::GetText, public
  300. //
  301. // Synopsis: Retrieves text from current chunk
  302. //
  303. // Arguments: [pcwcBuffer] -- count of UniCode characters in buffer
  304. // [awcBuffer] -- buffer for text
  305. //
  306. // History: 16-Jul-93 AmyA Created.
  307. // 30-Aug-94 Bartoszm Rewrote
  308. //
  309. //----------------------------------------------------------------------------
  310. SCODE STDMETHODCALLTYPE CTextIFilter::GetText( ULONG * pcwcOutput,
  311. WCHAR * awcOutput )
  312. {
  313. if ( 0 == awcOutput || 0 == pcwcOutput )
  314. {
  315. return E_INVALIDARG;
  316. }
  317. if ( !_fContents || _fNSS )
  318. {
  319. *pcwcOutput = 0;
  320. return FILTER_E_NO_MORE_TEXT;
  321. }
  322. if ( 0 == *pcwcOutput )
  323. {
  324. if ( _bytesReadFromChunk == _mmStreamBuf.Size() )
  325. {
  326. return FILTER_E_NO_MORE_TEXT;
  327. }
  328. if ( _fUniCode )
  329. {
  330. ULONG ccInput = _mmStreamBuf.Size() - _bytesReadFromChunk;
  331. ccInput /= sizeof(WCHAR);
  332. //
  333. // Handle bogus Unicode file with an odd byte count.
  334. //
  335. if ( 0 == ccInput )
  336. {
  337. return FILTER_E_NO_MORE_TEXT;
  338. }
  339. }
  340. return S_OK;
  341. }
  342. if ( _fDBCSSplitChar )
  343. {
  344. //
  345. // Convert DBCS lead byte from the previous mapping and the trail byte (first
  346. // char) from this mapping to Unicode
  347. //
  348. Win4Assert( IsDBCSLeadByteEx( _ulCodePage, _abDBCSInput[0] ) );
  349. _abDBCSInput[1] = * (BYTE *) _mmStreamBuf.Get();
  350. if ( 1 == *pcwcOutput )
  351. {
  352. //
  353. // In the DBCS case, output buffer must be bigger than one byte
  354. //
  355. return E_INVALIDARG;
  356. }
  357. ULONG cwcActual = MultiByteToWideChar( _ulCodePage,
  358. 0,
  359. (char *) _abDBCSInput,
  360. 2,
  361. awcOutput,
  362. *pcwcOutput );
  363. if ( cwcActual == 0 )
  364. {
  365. //
  366. // Input buffer is 2 bytes, and output buffer is 2k, hence there
  367. // should be ample space
  368. //
  369. Win4Assert( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
  370. return E_FAIL;
  371. }
  372. *pcwcOutput = cwcActual;
  373. _bytesReadFromChunk = 1;
  374. _fDBCSSplitChar = FALSE;
  375. return S_OK;
  376. }
  377. if ( _bytesReadFromChunk == _mmStreamBuf.Size() )
  378. _fDBCSSplitChar = FALSE;
  379. else
  380. {
  381. _fDBCSSplitChar = _fDBCSCodePage
  382. && _bytesReadFromChunk == _mmStreamBuf.Size() - 1
  383. && IsDBCSLeadByteEx( _ulCodePage,
  384. *( (BYTE *) _mmStreamBuf.Get()
  385. + _bytesReadFromChunk ) );
  386. }
  387. if ( _bytesReadFromChunk == _mmStreamBuf.Size()
  388. || _fDBCSSplitChar )
  389. {
  390. if ( _fDBCSSplitChar )
  391. {
  392. //
  393. // Store the DBCS lead byte for conversion as part of next chunk.
  394. // This works across chunks because the chunks are emitted
  395. // with CHUNK_NO_BREAK break type.
  396. //
  397. _abDBCSInput[0] = *( (BYTE *) _mmStreamBuf.Get()
  398. + _bytesReadFromChunk );
  399. }
  400. *pcwcOutput = 0;
  401. return FILTER_E_NO_MORE_TEXT;
  402. }
  403. Win4Assert( _mmStreamBuf.Size() >= _bytesReadFromChunk );
  404. SCODE sc = S_OK;
  405. ULONG ccInput = _mmStreamBuf.Size() - _bytesReadFromChunk;
  406. BYTE* pbInput = (BYTE*) _mmStreamBuf.Get() + _bytesReadFromChunk;
  407. ULONG cwcOutput = *pcwcOutput; // size of the output buffer
  408. if ( _fUniCode )
  409. {
  410. ccInput /= sizeof(WCHAR);
  411. //
  412. // Handle bogus Unicode file with an odd byte count.
  413. //
  414. if ( 0 == ccInput )
  415. {
  416. *pcwcOutput = 0;
  417. return FILTER_E_NO_MORE_TEXT;
  418. }
  419. }
  420. //
  421. // ASCII text must be converted to UniCode.
  422. // UniCode text must be folded into pre-composed characters
  423. // There is no guarantee about how many UniCode characters it takes
  424. // to represent a single multi-byte character. Most of the time, it
  425. // takes 1 UniCode character to represent 1 ASCII character.
  426. //
  427. // MultiByteToWideChar returns 0 and sets LastError to
  428. // ERROR_INSUFFICIENT_BUFFER if all characters in the input buffer cannot
  429. // be translated in the output space provided.
  430. //
  431. // We'll assume a fairly optimistic target count of 1:1 translations
  432. // (7/8 of all characters) and deal with the overflow when it occurs.
  433. //
  434. // Let's try to convert this many characters from the input buffer
  435. ULONG cInputChar = 1 + cwcOutput / 2 + cwcOutput / 4 + cwcOutput / 8;
  436. //
  437. // Don't overflow
  438. //
  439. if (cInputChar > ccInput)
  440. cInputChar = ccInput;
  441. //
  442. // Translate
  443. //
  444. ULONG cwcActual = 0;
  445. do
  446. {
  447. if ( _fUniCode )
  448. {
  449. if ( _fBigEndian )
  450. {
  451. TRY
  452. {
  453. XArray<WCHAR> xTmp( cInputChar );
  454. for ( ULONG i = 0; i < cInputChar; i++ )
  455. xTmp[i] = MAKEWORD( pbInput[ sizeof WCHAR * i + 1 ],
  456. pbInput[ sizeof WCHAR * i ] );
  457. cwcActual = FoldStringW( MAP_PRECOMPOSED,
  458. xTmp.GetPointer(),
  459. cInputChar,
  460. awcOutput,
  461. cwcOutput );
  462. ciDebugOut(( DEB_ITRACE, "before %#x, after %#x\n",
  463. pbInput, awcOutput ));
  464. }
  465. CATCH( CException, e )
  466. {
  467. sc = e.GetErrorCode();
  468. break;
  469. }
  470. END_CATCH
  471. }
  472. else
  473. {
  474. cwcActual = FoldStringW( MAP_PRECOMPOSED,
  475. (WCHAR*) pbInput,
  476. cInputChar,
  477. awcOutput,
  478. cwcOutput );
  479. }
  480. }
  481. else
  482. {
  483. //
  484. // If last char is a DBCS lead byte, then don't convert the last char
  485. //
  486. if ( _fDBCSCodePage
  487. && IsLastCharDBCSLeadByte( pbInput, cInputChar ) )
  488. {
  489. Win4Assert( cInputChar > 1 );
  490. cInputChar--;
  491. }
  492. cwcActual = MultiByteToWideChar( _ulCodePage,
  493. 0,
  494. (char*) pbInput,
  495. cInputChar,
  496. awcOutput,
  497. cwcOutput);
  498. }
  499. if ( 0 == cwcActual )
  500. {
  501. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
  502. cInputChar >= 2 )
  503. {
  504. cInputChar /= 2;
  505. }
  506. else
  507. {
  508. Win4Assert( !"Can't translate single char" );
  509. sc = E_FAIL;
  510. break;
  511. }
  512. }
  513. } while ( cwcActual == 0 );
  514. if ( SUCCEEDED(sc) )
  515. {
  516. *pcwcOutput = cwcActual;
  517. if ( _fUniCode )
  518. _bytesReadFromChunk += cInputChar * sizeof(WCHAR);
  519. else
  520. _bytesReadFromChunk += cInputChar;
  521. }
  522. else
  523. *pcwcOutput = 0;
  524. return sc;
  525. }
  526. //+---------------------------------------------------------------------------
  527. //
  528. // Member: CTextIFilter::GetValue, public
  529. //
  530. // Synopsis: No value chunks for plain text.
  531. //
  532. // History: 16-Jul-93 AmyA Created.
  533. //
  534. //----------------------------------------------------------------------------
  535. SCODE STDMETHODCALLTYPE CTextIFilter::GetValue( PROPVARIANT ** ppPropValue )
  536. {
  537. return FILTER_E_NO_VALUES;
  538. }
  539. //+---------------------------------------------------------------------------
  540. //
  541. // Member: CTextIFilter::BindRegion, public
  542. //
  543. // Synopsis: Creates moniker or other interface for text indicated
  544. //
  545. // Arguments: [origPos] -- the region of text to be mapped to a moniker
  546. // [riid] -- Interface to bind
  547. // [ppunk] -- Output pointer to interface
  548. //
  549. // History: 16-Jul-93 AmyA Created.
  550. //
  551. //----------------------------------------------------------------------------
  552. SCODE STDMETHODCALLTYPE CTextIFilter::BindRegion( FILTERREGION origPos,
  553. REFIID riid,
  554. void ** ppunk )
  555. {
  556. return E_NOTIMPL;
  557. }
  558. //+---------------------------------------------------------------------------
  559. //
  560. // Member: CTextIFilter::GetClassID, public
  561. //
  562. // Synopsis: Returns the class id of this class.
  563. //
  564. // Arguments: [pClassID] -- the class id
  565. //
  566. // History: 16-Jul-93 AmyA Created.
  567. //
  568. //----------------------------------------------------------------------------
  569. SCODE STDMETHODCALLTYPE CTextIFilter::GetClassID( CLSID * pClassID )
  570. {
  571. *pClassID = CLSID_CTextIFilter;
  572. return S_OK;
  573. }
  574. //+---------------------------------------------------------------------------
  575. //
  576. // Member: CTextIFilter::IsDirty, public
  577. //
  578. // Synopsis: Always returns S_FALSE since this class is read-only.
  579. //
  580. // History: 16-Jul-93 AmyA Created.
  581. //
  582. //----------------------------------------------------------------------------
  583. SCODE STDMETHODCALLTYPE CTextIFilter::IsDirty()
  584. {
  585. return S_FALSE; // Since the filter is read-only, there will never be
  586. // changes to the file.
  587. }
  588. //+---------------------------------------------------------------------------
  589. //
  590. // Function: WellKnownExtension
  591. //
  592. // Synopsis: Checks if the file extension is well-known to the text filter
  593. //
  594. // Arguments: [pwcFile] -- path of the file to be checked
  595. //
  596. // Returns: TRUE if the extension is well-known by the text filter
  597. //
  598. // History: 28-Jul-98 dlee Created.
  599. //
  600. //----------------------------------------------------------------------------
  601. BOOL WellKnownExtension( WCHAR const * pwcFile )
  602. {
  603. //
  604. // All we really care about is .dic and .txt files. Others have script
  605. // code that is better broken by the neutral word breaker.
  606. //
  607. const WCHAR *aKnownExt[] =
  608. {
  609. L"dic", // MS spell-check custom word dictionary
  610. L"txt",
  611. // L"wtx",
  612. // L"bat",
  613. // L"cmd",
  614. // L"idq",
  615. // L"ini",
  616. // L"inx",
  617. // L"reg",
  618. // L"inf",
  619. // L"vbs",
  620. };
  621. const unsigned cKnownExt = sizeof aKnownExt / sizeof aKnownExt[0];
  622. WCHAR const * pwcExt = wcsrchr( pwcFile, '.' );
  623. if ( 0 == pwcExt )
  624. return FALSE;
  625. pwcExt++;
  626. unsigned cwc = wcslen( pwcExt );
  627. // all the entries in the array above are 3 long
  628. if ( 3 == cwc )
  629. {
  630. WCHAR awcExt[ 4 ];
  631. unsigned cwcOut = LCMapString( LOCALE_NEUTRAL,
  632. LCMAP_LOWERCASE,
  633. pwcExt,
  634. 3,
  635. awcExt,
  636. 3 );
  637. Win4Assert( 3 == cwcOut );
  638. awcExt[ 3 ] = 0;
  639. for ( unsigned i = 0; i < cKnownExt; i++ )
  640. if ( !wcscmp( awcExt, aKnownExt[i] ) )
  641. return TRUE;
  642. }
  643. return FALSE;
  644. } //WellKnownExtension
  645. //+---------------------------------------------------------------------------
  646. //
  647. // Member: CTextIFilter::Load, public
  648. //
  649. // Synopsis: Loads the indicated file
  650. //
  651. // Arguments: [pszFileName] -- the file name
  652. // [dwMode] -- the mode to load the file in
  653. //
  654. // History: 16-Jul-93 AmyA Created.
  655. //
  656. // Notes: dwMode must be either 0 or STGM_READ.
  657. //
  658. //----------------------------------------------------------------------------
  659. SCODE STDMETHODCALLTYPE CTextIFilter::Load(LPCWSTR pszFileName, DWORD dwMode)
  660. {
  661. _fNSS = FALSE;
  662. if ( 0 != _pStream )
  663. {
  664. _pStream->Release();
  665. _pStream = 0;
  666. _mmIStream.Close();
  667. }
  668. if (_pwszFileName != 0)
  669. {
  670. delete _pwszFileName;
  671. _pwszFileName = 0;
  672. _mmStreamBuf.Rewind();
  673. _mmStream.Close();
  674. }
  675. SCODE sc = S_OK;
  676. unsigned cc = 0;
  677. TRY
  678. {
  679. //
  680. // If it's a file the text filter knows how to filter, use the
  681. // default system locale. Otherwise, use the neutral locale.
  682. //
  683. LCID lcid = MAKELCID( MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ),
  684. SORT_DEFAULT );
  685. if ( WellKnownExtension( pszFileName ) )
  686. lcid = GetSystemDefaultLCID();
  687. if ( _locale != lcid )
  688. {
  689. _locale = lcid;
  690. _ulCodePage = LocaleToCodepage( _locale );
  691. _fDBCSCodePage = IsDBCSCodePage( _ulCodePage );
  692. }
  693. cc = wcslen( pszFileName ) + 1;
  694. _pwszFileName = new WCHAR [cc];
  695. wcscpy( _pwszFileName, pszFileName );
  696. _mmStream.Open( _pwszFileName,
  697. GENERIC_READ,
  698. FILE_SHARE_READ,
  699. OPEN_EXISTING );
  700. if ( _mmStream.Ok() )
  701. _mmStreamBuf.Init( &_mmStream );
  702. else
  703. sc = ( STATUS_ACCESS_DENIED == GetLastError() ? // Open sets the last error
  704. FILTER_E_PASSWORD :
  705. FILTER_E_ACCESS );
  706. }
  707. CATCH( CException, e )
  708. {
  709. if ( e.GetErrorCode() == HRESULT_FROM_WIN32( ERROR_BAD_EXE_FORMAT ) )
  710. {
  711. _fNSS = TRUE;
  712. sc = S_OK;
  713. }
  714. else
  715. {
  716. sc = ( STATUS_ACCESS_DENIED == e.GetErrorCode() ?
  717. FILTER_E_PASSWORD :
  718. FILTER_E_ACCESS );
  719. }
  720. }
  721. END_CATCH;
  722. return sc;
  723. }
  724. //+---------------------------------------------------------------------------
  725. //
  726. // Member: CTextIFilter::Save, public
  727. //
  728. // Synopsis: Always returns E_FAIL, since the file is opened read-only
  729. //
  730. // History: 16-Jul-93 AmyA Created.
  731. //
  732. //----------------------------------------------------------------------------
  733. SCODE STDMETHODCALLTYPE CTextIFilter::Save(LPCWSTR pszFileName, BOOL fRemember)
  734. {
  735. return E_FAIL; // cannot be saved since it is read-only
  736. }
  737. //+---------------------------------------------------------------------------
  738. //
  739. // Member: CTextIFilter::SaveCompleted, public
  740. //
  741. // Synopsis: Always returns S_OK since the file is opened read-only
  742. //
  743. // History: 16-Jul-93 AmyA Created.
  744. //
  745. //----------------------------------------------------------------------------
  746. SCODE STDMETHODCALLTYPE CTextIFilter::SaveCompleted(LPCWSTR pszFileName)
  747. {
  748. return E_FAIL;
  749. }
  750. //+---------------------------------------------------------------------------
  751. //
  752. // Member: CTextIFilter::GetCurFile, public
  753. //
  754. // Synopsis: Returns a copy of the current file name
  755. //
  756. // Arguments: [ppszFileName] -- where the copied string is returned.
  757. //
  758. // History: 09-Aug-93 AmyA Created.
  759. //
  760. //----------------------------------------------------------------------------
  761. SCODE STDMETHODCALLTYPE CTextIFilter::GetCurFile(LPWSTR * ppszFileName)
  762. {
  763. if ( _pwszFileName == 0 )
  764. return E_FAIL;
  765. SCODE sc = S_OK;
  766. unsigned cc = wcslen( _pwszFileName ) + 1;
  767. *ppszFileName = (WCHAR *)CoTaskMemAlloc(cc*sizeof(WCHAR));
  768. if ( *ppszFileName )
  769. wcscpy( *ppszFileName, _pwszFileName );
  770. else
  771. sc = E_OUTOFMEMORY;
  772. return sc;
  773. }
  774. //+---------------------------------------------------------------------------
  775. //
  776. // Member: CTextIFilter::Load, public
  777. //
  778. // Synopsis: Loads the indicated stream
  779. //
  780. // Arguments: [pStm] -- The IStream
  781. //
  782. // History: 11-Feb-97 KyleP Created.
  783. //
  784. //----------------------------------------------------------------------------
  785. SCODE STDMETHODCALLTYPE CTextIFilter::Load( IStream * pStm )
  786. {
  787. _fNSS = FALSE;
  788. //
  789. // Close any previously open stuff.
  790. //
  791. if ( 0 != _pStream )
  792. {
  793. _pStream->Release();
  794. _pStream = 0;
  795. _mmIStream.Close();
  796. }
  797. if (_pwszFileName != 0)
  798. {
  799. delete _pwszFileName;
  800. _pwszFileName = 0;
  801. _mmStreamBuf.Rewind();
  802. _mmStream.Close();
  803. }
  804. //
  805. // Try to initialize map.
  806. //
  807. SCODE sc = S_OK;
  808. TRY
  809. {
  810. _pStream = pStm;
  811. _pStream->AddRef();
  812. _mmIStream.Open( pStm );
  813. if ( _mmIStream.Ok() )
  814. _mmStreamBuf.Init( &_mmIStream );
  815. else
  816. {
  817. sc = ( STG_E_ACCESSDENIED == GetLastError() ?
  818. FILTER_E_PASSWORD :
  819. FILTER_E_ACCESS );
  820. }
  821. }
  822. CATCH( CException, e )
  823. {
  824. sc = ( STG_E_ACCESSDENIED == e.GetErrorCode() ?
  825. FILTER_E_PASSWORD :
  826. FILTER_E_ACCESS );
  827. }
  828. END_CATCH;
  829. return sc;
  830. }
  831. //+---------------------------------------------------------------------------
  832. //
  833. // Member: CTextIFilter::Save, public
  834. //
  835. // Synopsis: Always returns E_FAIL, since the stream is opened read-only
  836. //
  837. // Arguments: [pStm] -- Stream
  838. // [fClearDirty] -- TRUE --> Clear dirty bit in stream
  839. //
  840. // History: 11-Feb-97 KyleP Created.
  841. //
  842. //----------------------------------------------------------------------------
  843. SCODE STDMETHODCALLTYPE CTextIFilter::Save( IStream * pStm, BOOL fClearDirty )
  844. {
  845. return E_FAIL;
  846. }
  847. //+---------------------------------------------------------------------------
  848. //
  849. // Member: CTextIFilter::GetSizeMax, public
  850. //
  851. // Synopsis: Always returns E_FAIL, since the stream is opened read-only
  852. //
  853. // Arguments: [pcbSize] -- Size of stream needed to save object.
  854. //
  855. // History: 11-Feb-97 KyleP Created.
  856. //
  857. //----------------------------------------------------------------------------
  858. SCODE STDMETHODCALLTYPE CTextIFilter::GetSizeMax( ULARGE_INTEGER * pcbSize )
  859. {
  860. return E_FAIL;
  861. }
  862. //+-------------------------------------------------------------------------
  863. //
  864. // Method: CTextIFilter::IsLastCharDBCSLeadByte
  865. //
  866. // Synopsis: Check if last byte in buffer is a DBCS lead byte
  867. //
  868. // Arguments: [pbIn] -- Input buffer
  869. // [cChIn] -- Buffer length
  870. //
  871. // History: 6-Jan-96 SitaramR Created
  872. //
  873. //--------------------------------------------------------------------------
  874. BOOL CTextIFilter::IsLastCharDBCSLeadByte( BYTE *pbIn,
  875. ULONG cChIn )
  876. {
  877. Win4Assert( IsDBCSCodePage( _ulCodePage ) );
  878. for ( ULONG cCh=0; cCh<cChIn; cCh++ )
  879. if ( IsDBCSLeadByteEx( _ulCodePage, pbIn[cCh] ) )
  880. cCh++;
  881. //
  882. // If last char is DBCS lead byte, then cCh == cChIn + 1, else cCh == cChIin
  883. //
  884. return cCh != cChIn;
  885. }
  886. //+-------------------------------------------------------------------------
  887. //
  888. // Method: CTextIFilter::IsDBCSCodePage
  889. //
  890. // Synopsis: Check if the codepage is a DBCS code page
  891. //
  892. // Arguments: [codePage] -- Code page to check
  893. //
  894. // History: 6-Jan-96 SitaramR Created
  895. //
  896. //--------------------------------------------------------------------------
  897. BOOL CTextIFilter::IsDBCSCodePage( ULONG ulCodePage )
  898. {
  899. CPINFO cpInfo;
  900. BOOL fSuccess = GetCPInfo( ulCodePage, &cpInfo );
  901. if ( fSuccess )
  902. {
  903. if ( cpInfo.LeadByte[0] != 0 && cpInfo.LeadByte[1] != 0 )
  904. return TRUE;
  905. else
  906. return FALSE;
  907. }
  908. ciDebugOut(( DEB_ERROR,
  909. "IsDBCSCodePage failed, 0x%x\n",
  910. GetLastError() ));
  911. return FALSE;
  912. }