Esempio n. 1
0
NAWcharBuf* ISO88591ToUnicode(const charBuf& input, 
	CollHeap *heap, NAWcharBuf*& unicodeString, NABoolean addNullAtEnd)
{
   NAWcharBuf* output = checkSpace(heap, input.getStrLen(), unicodeString, addNullAtEnd);

   if ( output == 0 ) return 0;

   NAWchar* target = output->data();

   Int32 i;
   for ( i=0; i<input.getStrLen(); i++ ) {
      target[i] = (NAWchar)(input.data()[i]);
   }

   if ( addNullAtEnd )
      target[i] = 0;

   output->setStrLen(input.getStrLen());
   return output;

}
Esempio n. 2
0
Int32 main(Int32 argc, char** argv)
{
      charBuf* latin1 = 0;

      NAWchar wbuf[1];
      NAWcharBuf uni(wbuf, 1);

      for ( NAWchar i=0; i<0xff; i++ ) {
         wbuf[0] = i;
         latin1 = unicodeToISO88591(uni, 0, latin1);
         if ( latin1 && latin1->data()[0] != i ) {
               printf("u2l1 test failed\n");
               return 1;
         }
      }

      unsigned char buf[1];
      charBuf ascii(buf, 1);
      NAWcharBuf* unicode = 0;

      for ( unsigned char j=0; j<0xff; j++ ) {
         buf[0] = j;
         unicode = ISO88591ToUnicode(ascii, 0, unicode);

         if ( unicode && unicode->data()[0] != j ) {
               printf("l12u test failed\n");
               return 1;
            }
      }

      wbuf[0] = 0xC0F3; // negative test
      latin1 = unicodeToISO88591(uni, 0, latin1);
      if ( latin1 ) 
         printf("negative u2l1 test failed\n");

      printf("test pass\n");
  
     return 0;
}
Esempio n. 3
0
short Param::convertValue(SqlciEnv * sqlci_env, short targetType,
			  Lng32 &targetLen,
			  Lng32 targetPrecision,
			  Lng32 targetScale,
                          Lng32 vcIndLen,
   			  ComDiagsArea* diags) {

  // get rid of the old converted value
  if (converted_value) {
    delete [] converted_value;
    converted_value = 0;
  };

  short sourceType;
  Lng32 sourceLen;

  // set up the source and its length based on the how the value is passed-in.
  if ( isInSingleByteForm() == FALSE ) {
    sourceLen = (Lng32)(NAWstrlen((NAWchar*)value) * BYTES_PER_NAWCHAR);
    switch (getCharSet()) {
      case CharInfo::UNICODE:
        sourceType = REC_NCHAR_F_UNICODE;
        break;

      case CharInfo::KANJI_MP:
      case CharInfo::KSC5601_MP:
        sourceType = REC_BYTE_F_ASCII; // KANJI/KSC passed in as NAWchar*
        break;

      default:
        return SQL_Error; // error case
    }
  } else {
    sourceLen = (Lng32)strlen(value); // for any source in single-byte format
    sourceType = REC_BYTE_F_ASCII;
  }

  char * pParamValue = value;

  if ( DFS2REC::isAnyCharacter(targetType) ) {

    if (termCS_ == CharInfo::UnknownCharSet)
      termCS_ = sqlci_env->getTerminalCharset();
    if (cs == CharInfo::UnknownCharSet)
    {
      isQuotedStrWithoutCharSetPrefix_ = TRUE;
      cs = termCS_;
    }

    // If the target is CHARACTER and param is set as [_cs_prefix]'...', then 
    // make sure the source is assignment compatible with the target.
    CharInfo::CharSet targetCharSet = (CharInfo::CharSet)targetScale;
    if ( targetCharSet == CharInfo::UNICODE )
    {
      if (getUTF16StrLit() == (NAWchar*)NULL)
      {
        utf16StrLit_ = new NAWchar [ sourceLen * 2 + 1 ]; // plenty of room
        Lng32 utf16StrLenInNAWchars =
          LocaleStringToUnicode(cs/*sourceCS*/, /*sourceStr*/value, sourceLen,
                                utf16StrLit_/*outputBuf*/, sourceLen+1/*outputBufSizeInNAWchars*/,
                                TRUE /* in - NABoolean addNullAtEnd*/);
        if (sourceLen > 0 && utf16StrLenInNAWchars == 0)
          return SQL_Error;

        // ComASSERT(utf16StrLenInNAWchars == NAWstrlen(getUTF16StrLit()));
        // Resize the NAWchar buffer to save space
        NAWchar *pNAWcharBuf = new NAWchar [ utf16StrLenInNAWchars + 1 ];
        NAWstrncpy (pNAWcharBuf, utf16StrLit_, utf16StrLenInNAWchars + 1);
        pNAWcharBuf[utf16StrLenInNAWchars] = NAWCHR('\0'); // play it safe
        delete [] utf16StrLit_;
        utf16StrLit_ = pNAWcharBuf; // do not deallocate pNAWcharBuf
      }
      sourceLen = (Lng32)(NAWstrlen(getUTF16StrLit()) * BYTES_PER_NAWCHAR);
      // check to see if the parameter utf16 string fits in the target
      if ( sourceLen > targetLen )
        return SQL_Error;

      pParamValue = (char *)getUTF16StrLit();
      sourceType = REC_NCHAR_F_UNICODE;
    }

  } else {
 
    // MP NCHAR (KANJI/KSC) can not be converted to non-character objects
   if ( CharInfo::is_NCHAR_MP(cs) )
      return SQL_Error;
  }


  switch(targetType) {
  case REC_BIN16_SIGNED:
  case REC_BIN16_UNSIGNED:
  case REC_BPINT_UNSIGNED:
  case REC_BIN32_SIGNED:
  case REC_BIN32_UNSIGNED:
  case REC_BIN64_SIGNED:
  case REC_DECIMAL_UNSIGNED:
  case REC_DECIMAL_LSE:
  case REC_FLOAT32:
  case REC_FLOAT64:
  case REC_TDM_FLOAT32:
  case REC_TDM_FLOAT64:
  case REC_BYTE_F_ASCII:
  case REC_BYTE_V_ASCII:
  case REC_BYTE_V_ASCII_LONG:
  case REC_NCHAR_F_UNICODE:
  case REC_NCHAR_V_UNICODE:
  {
    char *VCLen = NULL;
    short VCLenSize = 0;

// 5/27/98: added VARNCHAR cases
    if ((targetType == REC_BYTE_V_ASCII) || 
        (targetType == REC_BYTE_V_ASCII_LONG) ||
        (targetType == REC_NCHAR_V_UNICODE)) 
    {
      // add bytes for variable length field
      VCLenSize = vcIndLen; //sizeof(short);
      VCLen = converted_value = new char[targetLen + VCLenSize];
    } else
      converted_value = new char[targetLen];


#pragma nowarn(1506)   // warning elimination 
    ex_expr::exp_return_type ok;
    CharInfo::CharSet TCS = sqlci_env->getTerminalCharset();
    CharInfo::CharSet ISOMAPCS = sqlci_env->getIsoMappingCharset();
    
    NAString* tempstr;
    if ( 
        DFS2REC::isAnyCharacter(sourceType) && DFS2REC::isAnyCharacter(targetType) &&
        !(getUTF16StrLit() != NULL && sourceType == REC_NCHAR_F_UNICODE && targetScale == CharInfo::UCS2) &&
        /*source*/cs != targetScale/*i.e., targetCharSet*/
        )
    {
      charBuf cbuf((unsigned char*)pParamValue, sourceLen);
      NAWcharBuf* wcbuf = 0;
      Int32 errorcode = 0;
      wcbuf = csetToUnicode(cbuf, 0, wcbuf,
                            cs/*sourceCharSet*/
                            , errorcode);
      if (errorcode != 0) return SQL_Error;
      tempstr = unicodeToChar(wcbuf->data(),wcbuf->getStrLen(),
                              targetScale/*i.e., targetCharSet*/
                              );
      if (tempstr == NULL) 
	return SQL_Error;  //Avoid NULL ptr reference if conversion error
      sourceType = targetType; // we just converted it to the target type
      sourceLen = tempstr->length();
      pParamValue = (char *)tempstr->data();

      if ( sourceLen > targetLen )
        return SQL_Error;
    }

    ok = convDoIt(pParamValue,
		  sourceLen, 
		  sourceType,
		  0, // source Precision
		  targetScale, // new charset we converted to
		  &converted_value[VCLenSize],
		  targetLen,
		  targetType,
		  targetPrecision,
		  targetScale,
		  VCLen,
		  VCLenSize,
		  0,
		  &diags);
    
    if ( ok != ex_expr::EXPR_OK)
    {
      // No need to delete allocated memory before return because class member
      // converted_value still points to allocated memory that is deleted in 
      // desctructor.
      return SQL_Error; // error case
    }
#pragma warn(1506)  // warning elimination
    
  };
  break;


  case REC_DATETIME: {

    char *VCLen = NULL;
    short VCLenSize = 0;
    converted_value = new char[targetLen + 1];

#pragma nowarn(1506)   // warning elimination 
    ex_expr::exp_return_type ok = convDoIt(value,
					   sourceLen, 
					   sourceType,
					   0, // source Precision
					   0, // source Scale
					   converted_value,
					   targetLen,
					   targetType,
					   targetPrecision,
					   targetScale,
					   VCLen,
					   VCLenSize,
					   0,
					   &diags);
    
    if ( ok != ex_expr::EXPR_OK)
      {
	return SQL_Error; // error case
      }
#pragma warn(1506)  // warning elimination
  };
  break;

  case REC_INT_YEAR:
  case REC_INT_MONTH:
  case REC_INT_YEAR_MONTH:
  case REC_INT_DAY:
  case REC_INT_HOUR:
  case REC_INT_DAY_HOUR:
  case REC_INT_MINUTE:
  case REC_INT_HOUR_MINUTE:
  case REC_INT_DAY_MINUTE:
  case REC_INT_SECOND:
  case REC_INT_MINUTE_SECOND:
  case REC_INT_HOUR_SECOND:
  case REC_INT_DAY_SECOND: {

    // convert target back to string.
    converted_value = new char[targetLen];
    Lng32 convFlags = CONV_ALLOW_SIGN_IN_INTERVAL;
#pragma nowarn(1506)   // warning elimination 
    short ok = 
      convDoItMxcs(value,
		   sourceLen, 
		   sourceType,
		   0, // source Precision
		   0, // source Scale
		   converted_value,
		   targetLen,
		   targetType,
		   targetPrecision,
		   targetScale,
		   convFlags);
    
    if ( ok != 0 )
    {
      // No need to delete allocated memory before return because class member
      // converted_value still points to allocated memory that is deleted in 
      // desctructor.
      return SQL_Error; // error case
    }
#pragma warn(1506)  // warning elimination
  };
  break;

  case REC_NUM_BIG_UNSIGNED:
  case REC_NUM_BIG_SIGNED:
  {
    converted_value = new char[targetLen];
#pragma nowarn(1506)   // warning elimination 
    short ok = 
      convDoItMxcs(value,
		   sourceLen, 
		   sourceType,
		   0, // source Precision
		   0, // source Scale
		   converted_value,
		   targetLen,
		   targetType,
		   targetPrecision,
		   targetScale,
		   0);
    
    if ( ok != 0 )
    {
      // No need to delete allocated memory before return because class member
      // converted_value still points to allocated memory that is deleted in 
      // desctructor.
      return SQL_Error; // error case
    }
#pragma warn(1506)  // warning elimination
    
  };
  break;

  default:
    break;
  };

  return 0;
}
Lng32 
LocaleStringToUnicode(Lng32 charset, const char* str, Lng32 strLen, 
                      NAWchar* wstrBuf, Lng32 wstrBufLen, NABoolean addNullAtEnd)
{
   // Changed the algorithm to call the new LocaleToUTF16() but keep
   // the old call to old ISO88591ToUnicode() when the character set is
   // ISO88591.  We want to keep the old "pass through" behavior so
   // Use of ISO 8859-15 characters (a.k.a., Latin-9) in
   // CHARACTER SET ISO88591 target column continues to work.

   if (charset == (Lng32) CharInfo::ISO88591)
   {
     NAWcharBuf wcbuf(wstrBuf, wstrBufLen);
     NAWcharBuf* wcbufPtr = &wcbuf;
     NAWcharBuf* res = 0;
     res = ISO88591ToUnicode(
                charBuf((unsigned char*)str, strLen), 0,
                wcbufPtr, addNullAtEnd
                        );
     return (res) ? res->getStrLen() : 0;
   }

   //
   // else (charset != (Lng32) CharInfo::ISO88591)
   //

   enum cnv_charset convCS = convertCharsetEnum(charset);
   if (convCS == cnv_UnknownCharSet)
     return 0; // nothing we can do; exit the routine

   UInt32 outBufSizeInBytes = wstrBufLen*sizeof(NAWchar);
   char * pFirstUntranslatedChar = NULL;
   UInt32 outputDataLenInBytes = 0;
   UInt32 translatedtCharCount = 0;
   Int32 convStatus =
     LocaleToUTF16(cnv_version1,           // const enum cnv_version version
                   str,                    // const char *in_bufr
                   strLen,                 // const int in_len in # of bytes
                   (const char *)wstrBuf,  // const char *out_bufr
                   (const Int32)outBufSizeInBytes,
                   convCS,       // enum cnv_charset charset -- output charset
                   pFirstUntranslatedChar, // char * & first_untranslated_char
                   &outputDataLenInBytes,  // unsigned int *output_data_len_p
                   0,                      // const int cnv_flags (default is 0)
                   (const Int32)addNullAtEnd,
                   &translatedtCharCount); // unsigned int *translated_char_cnt_p

   UInt32 outLenInW = outputDataLenInBytes/sizeof(NAWchar);
   if (convStatus == 0) // success
     return outLenInW;  // include the NULL terminator if (addNullAtEnd == TRUE)

   // If convStatus != 0, LocaleToUTF16 will not add the NULL terminator
   if (addNullAtEnd && wstrBuf && wstrBufLen > 0)
   {
     if (outLenInW < (UInt32)wstrBufLen)
       wstrBuf[outLenInW] = WIDE_('\0');
     else
     {
       // assume the specified wstrBufLen includes room for the NULL terminator
       // when the passed-in addNullAtEnd parameter is set to TRUE
       wstrBuf[wstrBufLen-1] = WIDE_('\0');
     }
   }
   return 0; // tell the caller not to use data in wstrBuf
}
Esempio n. 5
0
NAWcharBuf* csetToUnicode(const charBuf& input, 
	CollHeap *heap, NAWcharBuf*& unicodeString, Int32 cset, Int32 &errorcode,
        NABoolean addNullAtEnd, Int32 *charCount, Int32 *errorByteOff)
{
    char * err_ptr = NULL;
    UInt32 byteCount = 0, lv_charCount = 0, computedMaxBufSizeInNAWchars = 0;
    NABoolean outputBufferAllocatedByThisRoutine = (unicodeString == NULL) ? TRUE : FALSE;
    enum cnv_charset cnvSet = convertCharsetEnum (cset);

    computedMaxBufSizeInNAWchars = (input.getStrLen()+1)*2; // in NAWchar elements for the worst case
      
    NAWcharBuf* output = checkSpace(heap, computedMaxBufSizeInNAWchars, unicodeString, addNullAtEnd);

    if ( output == NULL ) {errorcode = CNV_ERR_BUFFER_OVERRUN; return NULL;}

    NAWchar* target = output->data();

    errorcode = LocaleToUTF16(
         cnv_version1,
         (const char *)input.data(), input.getStrLen(),
         (const char *)target, output->getBufSize()*BYTES_PER_NAWCHAR /* in bytes */,
         cnvSet,
         err_ptr,
         &byteCount,
         0,
         addNullAtEnd,
         &lv_charCount);
    if (errorcode == CNV_ERR_NOINPUT) errorcode=0;  // empty string is OK
    if (errorByteOff) *errorByteOff = err_ptr - (char *)input.data();
    if (charCount)    *charCount    = (Int32)lv_charCount;
    // If errorcode != 0, LocaleToUTF16 will not add the NULL terminator
    if (errorcode == 0 && addNullAtEnd && byteCount > 0)
       {
         // Exclude the size (in bytes) of the NULL terminator from the byte count.
         if (byteCount > BYTES_PER_NAWCHAR)
           byteCount -= BYTES_PER_NAWCHAR;
         else
           byteCount = 0;
       }
        
    output->setStrLen/*in_NAWchar_s*/(byteCount/BYTES_PER_NAWCHAR); // excluding the NULL terminator

    if (outputBufferAllocatedByThisRoutine &&
        output->getBufSize() > output->getStrLen() + 500) // allocated too much space
    {
      // Try to save space in the heap but still allocate 50 extra NAWchars so we do not need
      // to resize the buffer if there is a need to append a few more characters later on.
      // The additional 1 NAWchar is for the NULL terminator.

      NAWcharBuf * outNAWcharBuf2 = new (heap) NAWcharBuf ( output->getStrLen() + 51 // in NAWchars
                                                          , heap );
      if (outNAWcharBuf2 != NULL) // successful allocation
      {
        // Copy data to the newly allocated, smaller buffer.
        NAWstrncpy(outNAWcharBuf2->data(), output->data(), output->getStrLen());
        outNAWcharBuf2->setStrLen/*in_NAWchar_s*/(output->getStrLen());
        // Always append a UCS-2 NULL terminator but exclude it from the string length count.
        outNAWcharBuf2->data()[outNAWcharBuf2->getStrLen()] = 0;

        // Remove the old buffer and set up for the returned value and out parameter.
        unicodeString = outNAWcharBuf2; // return via the out parameter NAWcharBuf*& unicodeString
        NADELETE(output, NAWcharBuf, heap);
        output = outNAWcharBuf2;
      }
    }
    return output;

}
Esempio n. 6
0
short Env::process(SqlciEnv *sqlci_env)
{
  // ## Should any of this text come from the message file,
  // ## i.e. from a translatable file for I18N?


  // When adding new variables, please keep the information in 
  // alphabetic order
  Logfile *log = sqlci_env->get_logfile();

  log->WriteAll("----------------------------------");
  log->WriteAll("Current Environment");
  log->WriteAll("----------------------------------");
 

  bool authenticationEnabled = false;
  bool authorizationEnabled = false;
  bool authorizationReady = false;
  bool auditingEnabled = false;
  Int32 rc = sqlci_env->getAuthState(authenticationEnabled,
                                     authorizationEnabled,
                                     authorizationReady,
                                     auditingEnabled);

  // TDB: add auditing state
  log->WriteAllWithoutEOL("AUTHENTICATION     ");
  if (authenticationEnabled)
    log->WriteAll("enabled");
  else
    log->WriteAll("disabled");

  log->WriteAllWithoutEOL("AUTHORIZATION      ");
  if (authorizationEnabled)
    log->WriteAll("enabled");
  else
    log->WriteAll("disabled");

  log->WriteAllWithoutEOL("CURRENT DIRECTORY  ");

  // NT_PORT (bv 10/24/96) Added NA_MAX_PATH here and in common/Platform.h
  log->WriteAll(getcwd((char *)NULL, NA_MAX_PATH));


  log->WriteAllWithoutEOL("LIST_COUNT         ");
  char buf[100];
  Int32 len = sprintf(buf, "%u", sqlci_env->getListCount());
  if (len-- > 0)
    if (buf[len] == 'L' || buf[len] == 'l')
      buf[len] = '\0';
  log->WriteAll(buf);
  
  if (log->IsOpen())
    {
      log->WriteAllWithoutEOL("LOG FILE           ");
      log->WriteAll(log->Logname());
    }
  else
    {
      log->WriteAll("LOG FILE");
    }

  log->WriteAllWithoutEOL("MESSAGEFILE        ");
  const char *mf = GetErrorMessageFileName();
  log->WriteAll(mf ? mf : "");

#if 0
  log->WriteAllWithoutEOL("ISO88591 MAPPING   ");
  log->WriteAll(CharInfo::getCharSetName(sqlci_env->getIsoMappingCharset()));

  log->WriteAllWithoutEOL("DEFAULT CHARSET    ");
  log->WriteAll(CharInfo::getCharSetName(sqlci_env->getDefaultCharset()));

  log->WriteAllWithoutEOL("INFER CHARSET      ");
  log->WriteAll((sqlci_env->getInferCharset())?"ON":"OFF");
#endif

  // ## These need to have real values detected from the env and written out:

  // "US English" is more "politically correct" than "American English".
  //
  log->WriteAllWithoutEOL("MESSAGEFILE LANG   US English\n");

  log->WriteAllWithoutEOL("MESSAGEFILE VRSN   ");
  char vmsgcode[10];
  sprintf(vmsgcode, "%d", SQLERRORS_MSGFILE_VERSION_INFO);
#pragma nowarn(1506)   // warning elimination 
  Error vmsg(vmsgcode, strlen(vmsgcode), Error::ENVCMD_);
#pragma warn(1506)  // warning elimination 
  vmsg.process(sqlci_env);

  ComAnsiNamePart defaultCat;
  ComAnsiNamePart defaultSch;

  sqlci_env->getDefaultCatAndSch (defaultCat, defaultSch);
  CharInfo::CharSet TCS = sqlci_env->getTerminalCharset();
  CharInfo::CharSet ISOMAPCS = sqlci_env->getIsoMappingCharset();

  if(TCS !=
            CharInfo::UTF8
     )  {
      NAString dCat = defaultCat.getExternalName();
	  NAString dSch = defaultSch.getExternalName();
	  charBuf cbufCat((unsigned char*)dCat.data(), dCat.length());
	  charBuf cbufSch((unsigned char*)dSch.data(), dSch.length());
      NAWcharBuf* wcbuf = 0;
	  Int32 errorcode	= 0;
	  
	  wcbuf = csetToUnicode(cbufCat, 0, wcbuf, CharInfo::UTF8, errorcode);
	  NAString* tempstr;
	  if (errorcode != 0){
	    tempstr = new NAString(defaultCat.getExternalName().data()); 
	  }
	  else {				
	    tempstr = unicodeToChar(wcbuf->data(),wcbuf->getStrLen(), TCS, NULL, TRUE);
	    TrimNAStringSpace(*tempstr, FALSE, TRUE);  // trim trailing blanks
	  }
      log->WriteAllWithoutEOL("SQL CATALOG        ");
      log->WriteAll(tempstr->data());

	  // Default Schema

	  wcbuf = 0;  // must 0 out to get next call to allocate memory.
	  wcbuf = csetToUnicode(cbufSch, 0, wcbuf, CharInfo::UTF8, errorcode);
	  if (errorcode != 0){
	    tempstr = new NAString(defaultSch.getExternalName().data()); 
	  }
	  else {				
	    tempstr = unicodeToChar(wcbuf->data(),wcbuf->getStrLen(), TCS, NULL, TRUE);
	    TrimNAStringSpace(*tempstr, FALSE, TRUE);  // trim trailing blanks
	  }
       log->WriteAllWithoutEOL("SQL SCHEMA         ");
       log->WriteAll(tempstr->data());
	}
	else
	{
  log->WriteAllWithoutEOL("SQL CATALOG        ");
  log->WriteAll(defaultCat.getExternalName());
  log->WriteAllWithoutEOL("SQL SCHEMA         ");
  log->WriteAll(defaultSch.getExternalName());
  }

  // On Linux we include the database user name and user ID in the
  // command output
  NAString username;
  rc = sqlci_env->getExternalUserName(username);
  log->WriteAllWithoutEOL("SQL USER CONNECTED "); 
  if (rc >= 0)
    log->WriteAll(username.data());
  else
    log->WriteAll("?");

  rc = sqlci_env->getDatabaseUserName(username);
  log->WriteAllWithoutEOL("SQL USER DB NAME   ");
  if (rc >= 0)
    log->WriteAll(username.data());
  else
    log->WriteAll("?");
  
  Int32 uid = 0;
  rc = sqlci_env->getDatabaseUserID(uid);
  log->WriteAllWithoutEOL("SQL USER ID        ");
  if (rc >= 0)
    sprintf(buf, "%d", (int) uid);
  else
    strcpy(buf, "?");
  log->WriteAll(buf);
  
  log->WriteAllWithoutEOL("TERMINAL CHARSET   ");
  log->WriteAll(CharInfo::getCharSetName(sqlci_env->getTerminalCharset()));

  Int64 transid;
  if (sqlci_env->statusTransaction(&transid))
    {
      // transaction is active.
      char transid_str[20];
      convertInt64ToAscii(transid, transid_str);
      log->WriteAllWithoutEOL("TRANSACTION ID     ");
      log->WriteAll(transid_str);
      log->WriteAll("TRANSACTION STATE  in progress");
    }  
  else
    {
      log->WriteAll("TRANSACTION ID     ");
      log->WriteAll("TRANSACTION STATE  not in progress");
    }

  if (log->isVerbose())
    log->WriteAll("WARNINGS           on");
  else
    log->WriteAll("WARNINGS           off");
  
  return 0;  
}