os/ossrv/genericopenlibs/cppstdlib/stl/test/unit/codecvt_test.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200 (2012-06-15)
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 //
    15 
    16 #include <string>
    17 #include <sstream>
    18 #if !defined (STLPORT) || !defined (_STLP_USE_NO_IOSTREAMS)
    19 #  include <fstream>
    20 #  include <locale>
    21 
    22 #  include "cppunit/cppunit_proxy.h"
    23 
    24 #  if !defined (STLPORT) || defined(_STLP_USE_NAMESPACES)
    25 using namespace std;
    26 #  endif
    27 
    28 //
    29 // TestCase class
    30 //
    31 class CodecvtTest : public CPPUNIT_NS::TestCase
    32 {
    33   CPPUNIT_TEST_SUITE(CodecvtTest);
    34 #if defined (STLPORT) && defined (_STLP_NO_MEMBER_TEMPLATES)
    35   CPPUNIT_IGNORE;
    36 #endif
    37   CPPUNIT_TEST(variable_encoding);
    38   CPPUNIT_TEST(locale_cov1);
    39   CPPUNIT_TEST(locale_cov2);
    40   CPPUNIT_TEST(locale_cov3);
    41   CPPUNIT_TEST(locale_cov4);
    42   CPPUNIT_TEST(locale_cov5);
    43   CPPUNIT_TEST(locale_cov6);
    44   CPPUNIT_TEST(locale_cov7);
    45   CPPUNIT_TEST_SUITE_END();
    46 
    47 protected:
    48   void variable_encoding();
    49   void locale_cov1();
    50   void locale_cov2();
    51   void locale_cov3();
    52   void locale_cov4();
    53   void locale_cov5();
    54   void locale_cov6();
    55   void locale_cov7();
    56 };
    57 
    58 CPPUNIT_TEST_SUITE_REGISTRATION(CodecvtTest);
    59 
    60 #if defined (STLPORT)
    61 #  define __NO_THROW _STLP_NOTHROW
    62 #else
    63 #  define __NO_THROW throw()
    64 #endif
    65 
    66 
    67 /* Codecvt facet eating some characters from the external buffer.
    68  * Transform '01' in 'a'
    69  */
    70 struct eater_codecvt : public codecvt<char, char, mbstate_t> {
    71   typedef codecvt<char,char,mbstate_t> base;
    72 
    73   explicit eater_codecvt(size_t refs = 0) : base(refs) {}
    74 
    75   // primitive conversion
    76   virtual base::result
    77   do_in(mbstate_t& mb,
    78         const char* ebegin, const char* eend, const char*& ecur,
    79         char* ibegin, char* iend, char*& icur) const __NO_THROW {
    80       char *state = (char*)&mb;
    81       ecur = ebegin;
    82       icur = ibegin;
    83 
    84       while (ecur != eend) {
    85           if (icur == iend)
    86               return partial;
    87           if (*ecur == '0' || *state == 1) {
    88             if (*state != 1) {
    89               ++ecur;
    90             }
    91             if (ecur == eend) {
    92               *state = 1;
    93               return ok;
    94             }
    95 
    96             if (*ecur == '1') {
    97               *icur = 'a';
    98             }
    99             else {
   100               *(icur++) = '0';
   101               if (icur == iend) {
   102                 if (*state != 1) {
   103                   --ecur;
   104                 }
   105                 return partial;
   106               }
   107               *icur = *ecur;
   108             }
   109           }
   110           else {
   111             *icur = *ecur;
   112           }
   113 
   114           *state = 0;
   115           ++icur;
   116           ++ecur;
   117       }
   118 
   119       return ok;
   120   }
   121 
   122   // claim it's not a null-conversion
   123   virtual bool do_always_noconv() const __NO_THROW
   124   { return false; }
   125 
   126   // claim it doesn't have a fixed-length encoding
   127   virtual int do_encoding() const __NO_THROW
   128   { return 0; }
   129 
   130   // implemented for consistency with do_in overload
   131   virtual int do_length(const mbstate_t &state,
   132                         const char *efrom, const char *eend, size_t m) const {
   133     char *ibegin = new char[m];
   134     const char *ecur = efrom;
   135     char *icur = ibegin;
   136     mbstate_t tmp = state;
   137     do_in(tmp, efrom, eend, ecur, ibegin, ibegin + m, icur);
   138     delete[] ibegin;
   139     return ecur - efrom;
   140   }
   141 
   142   virtual int do_max_length() const __NO_THROW
   143   { return 2; }
   144 };
   145 
   146 /* Codecvt facet generating more characters than the ones read from the
   147  * external buffer, transform '01' in 'abc'
   148  * This kind of facet do not allow systematical positionning in the external
   149  * buffer (tellg -> -1), when you just read a 'a' you are at an undefined
   150  * external buffer position.
   151  */
   152 struct generator_codecvt : public codecvt<char, char, mbstate_t> {
   153   typedef codecvt<char,char,mbstate_t> base;
   154 
   155   explicit generator_codecvt(size_t refs = 0) : base(refs) {}
   156 
   157   // primitive conversion
   158   virtual base::result
   159   do_in(mbstate_t& mb,
   160         const char* ebegin, const char* eend, const char*& ecur,
   161         char* ibegin, char* iend, char*& icur) const __NO_THROW {
   162       //Access the mbstate information in a portable way:
   163       char *state = (char*)&mb;
   164       ecur = ebegin;
   165       icur = ibegin;
   166 
   167       if (icur == iend) return ok;
   168 
   169       if (*state == 2) {
   170         *(icur++) = 'b';
   171         if (icur == iend) {
   172           *state = 3;
   173           return ok;
   174         }
   175         *(icur++) = 'c';
   176         *state = 0;
   177       }
   178       else if (*state == 3) {
   179         *(icur++) = 'c';
   180         *state = 0;
   181       }
   182 
   183       while (ecur != eend) {
   184           if (icur == iend)
   185               return ok;
   186           if (*ecur == '0' || *state == 1) {
   187             if (*state != 1) {
   188               ++ecur;
   189             }
   190             if (ecur == eend) {
   191               *state = 1;
   192               return partial;
   193             }
   194 
   195             if (*ecur == '1') {
   196               *(icur++) = 'a';
   197               if (icur == iend) {
   198                 *state = 2;
   199                 return ok;
   200               }
   201               *(icur++) = 'b';
   202               if (icur == iend) {
   203                 *state = 3;
   204                 return ok;
   205               }
   206               *icur = 'c';
   207             }
   208             else {
   209               *(icur++) = '0';
   210               if (icur == iend) {
   211                 if (*state != 1) {
   212                   --ecur;
   213                 }
   214                 return ok;
   215               }
   216               *icur = *ecur;
   217             }
   218           }
   219           else {
   220             *icur = *ecur;
   221           }
   222 
   223           *state = 0;
   224           ++icur;
   225           ++ecur;
   226       }
   227 
   228       return ok;
   229   }
   230 
   231   // claim it's not a null-conversion
   232   virtual bool do_always_noconv() const __NO_THROW
   233   { return false; }
   234 
   235   // claim it doesn't have a fixed-length encoding
   236   virtual int do_encoding() const __NO_THROW
   237   { return 0; }
   238 
   239   // implemented for consistency with do_in overload
   240   virtual int do_length(const mbstate_t &mb,
   241                         const char *efrom, const char *eend, size_t m) const {
   242     const char *state = (const char*)&mb;
   243     int offset = 0;
   244     if (*state == 2)
   245       offset = 2;
   246     else if (*state == 3)
   247       offset = 1;
   248 
   249     char *ibegin = new char[m + offset];
   250     const char *ecur = efrom;
   251     char *icur = ibegin;
   252     mbstate_t tmpState = mb;
   253     do_in(tmpState, efrom, eend, ecur, ibegin, ibegin + m + offset, icur);
   254     /*
   255     char *state = (char*)&tmpState;
   256     if (*state != 0) {
   257       if (*state == 1)
   258         --ecur;
   259       else if (*state == 2 || *state == 3) {
   260         //Undefined position, we return -1:
   261         ecur = efrom - 1;
   262       }
   263     }
   264     else {
   265       if (*((char*)&mb) != 0) {
   266         //We take into account the character that hasn't been counted yet in
   267         //the previous decoding step:
   268         ecur++;
   269       }
   270     }
   271     */
   272     delete[] ibegin;
   273     return (int)min((size_t)(ecur - efrom), m);
   274   }
   275 
   276   virtual int do_max_length() const __NO_THROW
   277   { return 0; }
   278 };
   279 
   280 //
   281 // tests implementation
   282 //
   283 void CodecvtTest::variable_encoding()
   284 {
   285 #if !defined (STLPORT) || !defined (_STLP_NO_MEMBER_TEMPLATES)
   286   //We first generate the file used for test:
   287   const char* fileName = "c:\\test_file.txt";
   288   {
   289     ofstream ostr(fileName);
   290     //Maybe we simply do not have write access to repository
   291     CPPUNIT_ASSERT( ostr.good() );
   292     for (int i = 0; i < 2048; ++i) {
   293       ostr << "0123456789";
   294     }
   295     CPPUNIT_ASSERT( ostr.good() );
   296   }
   297 
   298   {
   299     ifstream istr(fileName);
   300     CPPUNIT_ASSERT( istr.good() );
   301     CPPUNIT_ASSERT( !istr.eof() );
   302 
   303     eater_codecvt codec(1);
   304     locale loc(locale::classic(), &codec);
   305 
   306     istr.imbue(loc);
   307     CPPUNIT_ASSERT( istr.good() );
   308     CPPUNIT_ASSERT( (int)istr.tellg() == 0 );
   309 
   310     int theoricalPos = 0;
   311     do {
   312       signed char c = (signed char)istr.get();
   313       if (c == char_traits<char>::eof()) {
   314         break;
   315       }
   316       ++theoricalPos;
   317       if (c == 'a') {
   318         ++theoricalPos;
   319       }
   320      CPPUNIT_ASSERT( (int)istr.tellg() == theoricalPos );
   321     }
   322     while (!istr.eof());
   323 
   324     CPPUNIT_ASSERT( istr.eof() );
   325   }
   326 
   327 #  if 0
   328   /* This test is broken, not sure if it is really possible to get a position in
   329    * a locale having a codecvt such as generator_codecvt. Maybe generator_codecvt
   330    * is not a valid theorical example of codecvt implementation. */
   331   {
   332     ifstream istr(fileName);
   333     CPPUNIT_ASSERT( istr.good() );
   334     CPPUNIT_ASSERT( !istr.eof() );
   335 
   336     generator_codecvt codec(1);
   337     locale loc(locale::classic(), &codec);
   338 
   339     istr.imbue(loc);
   340     CPPUNIT_ASSERT( istr.good() );
   341     CPPUNIT_ASSERT( (int)istr.tellg() == 0 );
   342 
   343     int theoricalPos = 0;
   344     int theoricalTellg;
   345     do {
   346       char c = istr.get();
   347       if (c == char_traits<char>::eof()) {
   348         break;
   349       }
   350       switch (c) {
   351         case 'a':
   352         case 'b':
   353           theoricalTellg = -1;
   354           break;
   355         case 'c':
   356           ++theoricalPos;
   357         default:
   358           ++theoricalPos;
   359           theoricalTellg = theoricalPos;
   360           break;
   361       }
   362 
   363       if ((int)istr.tellg() != theoricalTellg) {
   364         CPPUNIT_ASSERT( (int)istr.tellg() == theoricalTellg );
   365       }
   366     }
   367     while (!istr.eof());
   368 
   369     CPPUNIT_ASSERT( istr.eof() );
   370   }
   371 #  endif
   372 #endif
   373 }
   374 
   375 void CodecvtTest::locale_cov1()
   376 	{
   377 	locale loc ( "fr_FR.ISO-8859-1" );
   378 	locale loc1 ( "en_US.ISO-8859-1" );
   379 	bool result1,result2;
   380 		{
   381 	    result1 = isalnum ( 'L', loc);
   382 		result2 = isalnum ( '@', loc);
   383 		CPPUNIT_ASSERT( result1 == true );
   384 		CPPUNIT_ASSERT( result2 == false);
   385 		
   386 		result1 = isalnum ( 'L', loc1);
   387 		result2 = isalnum ( '@', loc1);
   388 		CPPUNIT_ASSERT( result1 == true );
   389 		CPPUNIT_ASSERT( result2 == false);
   390 		}
   391 		{
   392 	    result1 = isalpha ( 'L', loc);
   393 		result2 = isalpha ( '@', loc);
   394 		CPPUNIT_ASSERT( result1 == true );
   395 		CPPUNIT_ASSERT( result2 == false);
   396 		
   397 		result1 = isalpha ( 'L', loc1);
   398 		result2 = isalpha ( '@', loc1);
   399 		CPPUNIT_ASSERT( result1 == true );
   400 		CPPUNIT_ASSERT( result2 == false);
   401 		}
   402 		{
   403 	    result1 = iscntrl ( 'L', loc);
   404 		result2 = iscntrl ( '\n', loc);
   405 		CPPUNIT_ASSERT( result1 == false);
   406 		CPPUNIT_ASSERT( result2 == true );
   407 		
   408 		result1 = iscntrl ( 'L', loc1);
   409 		result2 = iscntrl ( '\n', loc1);
   410 		CPPUNIT_ASSERT( result1 == false);
   411 		CPPUNIT_ASSERT( result2 == true );
   412 		}
   413 		{
   414 	    result1 = isdigit ( 'L', loc);
   415 		result2 = isdigit ( '3', loc);
   416 		CPPUNIT_ASSERT( result1 == false);
   417 		CPPUNIT_ASSERT( result2 == true );
   418 		
   419 	    result1 = isdigit ( 'L', loc1);
   420 		result2 = isdigit ( '3', loc1);
   421 		CPPUNIT_ASSERT( result1 == false);
   422 		CPPUNIT_ASSERT( result2 == true );
   423 		}
   424 	}
   425 void CodecvtTest::locale_cov2()
   426 	{
   427 	locale loc ( "fr_FR.ISO-8859-1" );
   428 	locale loc1 ( "en_US.ISO-8859-1" );
   429 	bool result1,result2;
   430 		{
   431 	    result1 = isgraph ( ' ', loc);
   432 		result2 = isgraph ( '.', loc);
   433 		CPPUNIT_ASSERT( result1 == false);
   434 		CPPUNIT_ASSERT( result2 == true );
   435 		
   436 		result1 = isgraph ( ' ', loc1);
   437 		result2 = isgraph ( '.', loc1);
   438 		CPPUNIT_ASSERT( result1 == false);
   439 		CPPUNIT_ASSERT( result2 == true );
   440 		}
   441 		{
   442 	    result1 = islower ( 'L', loc);
   443 		result2 = islower ( 'v', loc);
   444 		CPPUNIT_ASSERT( result1 == false);
   445 		CPPUNIT_ASSERT( result2 == true );
   446 		
   447 		result1 = islower ( 'L', loc1);
   448 		result2 = islower ( 'v', loc1);
   449 		CPPUNIT_ASSERT( result1 == false);
   450 		CPPUNIT_ASSERT( result2 == true );
   451 		}
   452 		{
   453 	    result1 = isprint ( '\n', loc);
   454 		result2 = isprint ( 't', loc);
   455 		CPPUNIT_ASSERT( result1 == false);
   456 		CPPUNIT_ASSERT( result2 == true );
   457 		
   458 		result1 = isprint ( '\n', loc1);
   459 		result2 = isprint ( 't', loc1);
   460 		CPPUNIT_ASSERT( result1 == false);
   461 		CPPUNIT_ASSERT( result2 == true );
   462 		}
   463 		{
   464 	    result1 = ispunct ( 'L', loc);
   465 		result2 = ispunct ( ';', loc);
   466 		CPPUNIT_ASSERT( result1 == false);
   467 		CPPUNIT_ASSERT( result2 == true );
   468 		
   469 		result1 = ispunct ( 'L', loc1);
   470 		result2 = ispunct ( ';', loc1);
   471 		CPPUNIT_ASSERT( result1 == false);
   472 		CPPUNIT_ASSERT( result2 == true );
   473 		}
   474 	}
   475 void CodecvtTest::locale_cov3()
   476 	{
   477 	locale loc ( "fr_FR.ISO-8859-1" );
   478 	locale loc1 ( "en_US.ISO-8859-1" );
   479 	bool result1,result2,result3;
   480 		{
   481 		result2 = isspace ( '\n', loc);
   482 		result3 = isspace ( 'x', loc);
   483 		CPPUNIT_ASSERT( result2 == true );
   484 		CPPUNIT_ASSERT( result3 == false );
   485 		
   486 		result2 = isspace ( '\n', loc1);
   487 		result3 = isspace ( 'x', loc1);
   488 		CPPUNIT_ASSERT( result2 == true );
   489 		CPPUNIT_ASSERT( result3 == false );
   490 		}
   491 		{
   492 	    result1 = isupper ( 'L', loc);
   493 		result2 = isupper ( ';', loc);
   494 		CPPUNIT_ASSERT( result1 == true );
   495 		CPPUNIT_ASSERT( result2 == false );
   496 		
   497 		result1 = isupper ( 'L', loc1);
   498 		result2 = isupper ( ';', loc1);
   499 		CPPUNIT_ASSERT( result1 == true );
   500 		CPPUNIT_ASSERT( result2 == false );
   501 		}
   502 		{
   503 	    result1 = isxdigit ( 'f', loc);
   504 		result2 = isxdigit ( 'd', loc);
   505 		result3 = isxdigit ( 'q', loc);
   506 		CPPUNIT_ASSERT( result1 == true );
   507 		CPPUNIT_ASSERT( result2 == true );
   508 		CPPUNIT_ASSERT( result3 == false );
   509 		
   510 		result1 = isxdigit ( 'f', loc1);
   511 		result2 = isxdigit ( 'd', loc1);
   512 		result3 = isxdigit ( 'q', loc1);
   513 		CPPUNIT_ASSERT( result1 == true );
   514 		CPPUNIT_ASSERT( result2 == true );
   515 		CPPUNIT_ASSERT( result3 == false );
   516 		}
   517 	}
   518 void CodecvtTest::locale_cov4()
   519 	{
   520 	locale loc ( "fr_FR.ISO-8859-1" );
   521 	locale loc1 ( "en_US.ISO-8859-1" );
   522 	char cresult1;
   523 		{
   524 		cresult1 = tolower ( 'H', loc );
   525 		CPPUNIT_ASSERT( cresult1 == 'h' );
   526 		cresult1 = tolower ( 'h', loc );
   527 		CPPUNIT_ASSERT( cresult1 == 'h' );
   528 		cresult1 = tolower ( '$', loc );
   529 		CPPUNIT_ASSERT( cresult1 == '$' );
   530 		
   531 		cresult1 = tolower ( 'H', loc1 );
   532 		CPPUNIT_ASSERT( cresult1 == 'h' );
   533 		cresult1 = tolower ( 'h', loc1 );
   534 		CPPUNIT_ASSERT( cresult1 == 'h' );
   535 		cresult1 = tolower ( '$', loc1 );
   536 		CPPUNIT_ASSERT( cresult1 == '$' );
   537 		}
   538 		{
   539 		cresult1 = toupper ( 'H', loc );
   540 		CPPUNIT_ASSERT( cresult1 == 'H' );
   541 		cresult1 = toupper ( 'h', loc );
   542 		CPPUNIT_ASSERT( cresult1 == 'H' );
   543 		cresult1 = toupper ( '$', loc );
   544 		CPPUNIT_ASSERT( cresult1 == '$' );
   545 		
   546 		cresult1 = toupper ( 'H', loc1 );
   547 		CPPUNIT_ASSERT( cresult1 == 'H' );
   548 		cresult1 = toupper ( 'h', loc1 );
   549 		CPPUNIT_ASSERT( cresult1 == 'H' );
   550 		cresult1 = toupper ( '$', loc1 );
   551 		CPPUNIT_ASSERT( cresult1 == '$' );
   552 		}
   553 	}
   554 void CodecvtTest::locale_cov5()
   555 	{
   556 		{
   557 		char* str = "stdcpp with pips";
   558 		mbstate_t state = {0};
   559 		locale loc("C");
   560 		int res = use_facet<codecvt<wchar_t, char, mbstate_t> > ( loc ).length( state,str, &str[strlen(str)], 50 );
   561 		CPPUNIT_ASSERT( res == 16 );
   562 		res = use_facet<codecvt<char, char, mbstate_t> >( loc ).max_length( );
   563 		}
   564 		{
   565 		char* str = "stdcpp with pips";
   566 		wchar_t wstr [50];
   567 		memset(&wstr[0], 0, (sizeof(wchar_t))*(50));
   568 		const char* pszNext;
   569 		wchar_t* pwszNext;
   570 		mbstate_t state = {0};
   571 		locale loc("C");
   572 		int res = use_facet<codecvt<wchar_t, char, mbstate_t> >( loc ).in( state,str, &str[strlen(str)], pszNext,wstr, &wstr[strlen(str)], pwszNext );
   573 		wstr[strlen(str)] = 0; 
   574 		CPPUNIT_ASSERT(res!=codecvt_base::error);
   575 		}
   576 		{
   577 		locale loc ( "fr_FR.ISO-8859-1" );
   578 		int result1 = use_facet<codecvt<char, char, mbstate_t> > ( loc ).encoding ( );
   579 		CPPUNIT_ASSERT(result1 == 1);
   580 		bool result2 = use_facet<codecvt<char, char, mbstate_t> >( loc ).always_noconv( );
   581 		CPPUNIT_ASSERT(result2 == true);
   582 		result2 = use_facet<codecvt<wchar_t, char, mbstate_t> >( loc ).always_noconv( );
   583 		CPPUNIT_ASSERT(result2 == false);
   584 		}
   585 	}
   586 void CodecvtTest::locale_cov6()
   587 	{
   588 		{
   589 		locale loc( "en_US.ISO-8859-1" );
   590 		const numpunct < char> &npunct = use_facet <numpunct <char> >( loc );
   591 		string str = npunct.truename();
   592 		CPPUNIT_ASSERT(str == "true");
   593 		str = npunct.falsename();
   594 		CPPUNIT_ASSERT(str == "false");
   595 		CPPUNIT_ASSERT(npunct.thousands_sep( ) == ',');
   596 		
   597 		const numpunct < wchar_t> &npunct1 = use_facet <numpunct <wchar_t> >( loc );
   598 		wstring str1 = npunct1.truename();
   599 		CPPUNIT_ASSERT(str1 == L"true");
   600 		str1 = npunct1.falsename();
   601 		CPPUNIT_ASSERT(str1 == L"false");
   602 		CPPUNIT_ASSERT(npunct1.thousands_sep( ) == L',');
   603 		CPPUNIT_ASSERT(npunct1.decimal_point( ) == L'.');
   604 		}
   605 	}
   606 void CodecvtTest::locale_cov7()
   607 	{
   608 		{
   609 		locale loc( "en_US.ISO-8859-1" );
   610 		const moneypunct <char, true> &mpunct = use_facet <moneypunct <char, true > >(loc);
   611 		CPPUNIT_ASSERT(mpunct.thousands_sep( ) == ',');
   612 		string str = mpunct.positive_sign( );
   613 		str = mpunct.negative_sign( );
   614 		char x = mpunct.neg_format().field[0];
   615 		x = mpunct.neg_format().field[1];
   616 		x = mpunct.neg_format().field[2];
   617 		x = mpunct.neg_format().field[3];
   618 		CPPUNIT_ASSERT(mpunct.decimal_point( ) == '.');
   619 		str = mpunct.curr_symbol( );
   620 		
   621 		const moneypunct <char, false> &mpunct2 = use_facet <moneypunct <char, false> >(loc);
   622 		CPPUNIT_ASSERT(mpunct2.thousands_sep( ) == ',');
   623 		str = mpunct2.positive_sign( );
   624 		str = mpunct2.negative_sign( );
   625 		x = mpunct2.neg_format().field[0];
   626 		x = mpunct2.neg_format().field[1];
   627 		x = mpunct2.neg_format().field[2];
   628 		x = mpunct2.neg_format().field[3];
   629 		CPPUNIT_ASSERT(mpunct2.decimal_point( ) == '.');
   630 		str = mpunct2.curr_symbol( );
   631 		}
   632 	}
   633 #endif