/****************************************************************************

  $Workfile: JNIString.c $
  $Revision: 3 $
  $Modtime:: $
  $Copyright:

  Copyright (c) 1989-1995 Novell, Inc.  All Rights Reserved.

  THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL PROPRIETARY
  AND TRADE SECRET INFORMATION OF NOVELL, INC. ACCESS  TO  THIS  WORK IS
  RESTRICTED TO (I) NOVELL, INC.  EMPLOYEES WHO HAVE A NEED TO  KNOW HOW
  TO  PERFORM  TASKS WITHIN  THE SCOPE  OF  THEIR   ASSIGNMENTS AND (II)
  ENTITIES OTHER  THAN  NOVELL, INC.  WHO  HAVE ENTERED INTO APPROPRIATE
  LICENSE   AGREEMENTS.  NO  PART  OF  THIS WORK MAY BE USED, PRACTICED,
  PERFORMED COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
  CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED,  RECAST, TRANSFORMED
  OR ADAPTED  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC.  ANY USE
  OR EXPLOITATION  OF  THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE
  PERPETRATOR  TO CRIMINAL AND CIVIL LIABILITY.$

 ***************************************************************************/

#include <jni.h>
#include <nunicode.h>
#include <nwlocale.h>
#include <string.h>
#include <ntypes.h>
#include <stdlib.h>
#include "jncp.h"
#include "JNIString.h"

#ifdef N_PLAT_NLM
#include <signal.h>
#include <nwconio.h>
#include <nwthread.h>
#include <nwadv.h>
#endif

#ifdef WIN32
#include <windows.h>
#endif

NWRCODE
__StandardUnicodeToLocalStr
(
   pnstr8 destStr,
   nuint destStrSize,
   punicode uniStr,
   jint javaLength
);

NWRCODE
__StandardUnicodePathToLocalStr
(
   pnstr8 destStr,
   nuint destStrSize,
   punicode uniPath,
   jint javaLength
);

NWRCODE
__StandardLocalToJavaStr
(
   JNIEnv *env,
   jstring *pJavaStr,
   punicode uniBuffer,
   pnstr8 srcStr
);

NWRCODE
__OldUnicodeToLocalStr
(
   pnstr8 destStr,
   nuint destStrSize,
   punicode uniStr,
   jint javaLength
);

NWRCODE
__OldLocalToJavaStr
(
   JNIEnv *env,
   jstring *pJavaStr,
   punicode uniBuffer,
   pnstr8 srcStr
);

// return error if an unmappable character is encountered using the old
//   unicode APIs
#define JNCP_DEF_MAP_CHAR 0

N_GLOBAL_VAR void *standardUniFunctions[] = {
      (void *) __StandardUnicodeToLocalStr,
      (void *) __StandardUnicodePathToLocalStr,
      (void *) __StandardLocalToJavaStr };

N_GLOBAL_VAR void *oldUniFunctions[] = {
      (void *) __OldUnicodeToLocalStr,
      (void *) __OldUnicodeToLocalStr,
      (void *) __OldLocalToJavaStr };

//
// unicodeFunctions points to an array of __UnicodeToLocalStr,
//   __UnicodePathToLocalStr, and __LocalToJavaStr functions
//
// depending on which conversion tables are present at startup, this
//   will point to the appropriate array of function pointers
//
N_GLOBAL_VAR void **_unicodeFunctions = standardUniFunctions;

//
// these must be valid if 'unicodeFunctions' points to 'oldUniFunctions'
//
N_GLOBAL_VAR nptr _hUnicodeToLocal;
N_GLOBAL_VAR nptr _hLocalToUnicode;

//
// this must be valid if 'unicodeFunctions' points to 'standardUniFunctions'
//
N_GLOBAL_VAR pCONVERT _unicodeConverter = NULL;

#ifdef N_PLAT_NLM

//
// the standard unicode functions have to be loaded dynamically on
//   the NLM platform so that we don't require Service Pack 6 on Green River
//   servers.  This is the jump table for this dynamic loading.
//
N_GLOBAL_VAR StandardUnicodeJumpTable actual;
N_GLOBAL_VAR pStandardUnicodeJumpTable _NLMUnicodeJumpTable = &actual;

#endif

// 1 if unicode has been initialized, 0 means not initialized (default)
N_GLOBAL_VAR int _unicodeInitialized = 0;

// 1 if using standard APIs (default), 0 if using old APIs
N_GLOBAL_VAR int _usingStandardUnicode = 1;


char *NLMUnicodeSymbols[6] = {
      "NWUSGetCodePage",
      "NWUXLoadByteUnicodeConverter",
      "NWUXUnloadConverter",
      "NWUXByteToUnicode",
      "NWUXUnicodeToByte",
      "NWUXUnicodeToBytePath" };
/*
 * Initialize the entire unicode translation.  Here is the policy:
 *   - [NLM] Try to import standard unicode function ptrs
 *   - [NLM] If successful, fill out '_NLMUnicodeJumpTable
 *   - Try to initialize standard unicode libraries
 *   - If successful, assign standard jump table to 'unicodeFunctions'
 *   - Else, try to initialize old unicode libraries
 *   - If successful, assign old jump table to 'unicodeFunctions'
 */
NWRCODE __InitNativeUnicode(void)
{
   NWRCODE ccode = 0;
   nuint cp = 437;

   // set up the NLM Unicode jump tables

   #ifdef N_PLAT_NLM

      nint32 NLMHandle = GetNLMHandle();
      void *arr[sizeof(NLMUnicodeSymbols)/sizeof(char *)];
      int allLoaded = 1;
      int i;

      for (i = 0; i < sizeof(NLMUnicodeSymbols)/sizeof(char *); i++)
      {
         arr[i] = (void *) ImportSymbol(NLMHandle, NLMUnicodeSymbols[i]);

         if (arr[i] == NULL)
            allLoaded = 0;
      }

      if (!allLoaded)
      {
         for (i = 0; i < sizeof(NLMUnicodeSymbols)/sizeof(char *); i++)
         {
            if (arr[i] != NULL)
               UnimportSymbol(NLMHandle, NLMUnicodeSymbols[i]);
         }

         ccode = -1;
      }
      else
      {
         _NLMUnicodeJumpTable->getCP = (NWUSGetCodePage_t) arr[0];
         _NLMUnicodeJumpTable->loadConverter =
               (NWUXLoadByteUnicodeConverter_t) arr[1];
         _NLMUnicodeJumpTable->unloadConverter =
               (NWUXUnloadConverter_t) arr[2];
         _NLMUnicodeJumpTable->byteToUnicode = (NWUXByteToUnicode_t) arr[3];
         _NLMUnicodeJumpTable->unicodeToByte = (NWUXUnicodeToByte_t) arr[4];
         _NLMUnicodeJumpTable->unicodeToBytePath =
               (NWUXUnicodeToBytePath_t) arr[5];
      }

   #endif

   #ifdef N_PLAT_NLM
      if (ccode == 0)
      {
         nuint country;
         
         ccode = (*_NLMUnicodeJumpTable->getCP)(&cp, &country);
         
         if (ccode == 0)
         {
            ccode = (*_NLMUnicodeJumpTable->loadConverter)(cp,
                  &_unicodeConverter);
         }
      }
   #else
      #ifdef WIN32
      cp = GetOEMCP();
      #endif
      
      ccode = NWUXLoadByteUnicodeConverter(cp, &_unicodeConverter);
   #endif

   if (ccode != 0)
   {
      if (NULL != NWLsetlocale(NLC_ALL, ""))
      {
         LCONV lconv;

         if (NULL != NWLlocaleconv(&lconv))
         {
            ccode = NWInitUnicodeTables(lconv.country_id, lconv.code_page);

            if (0 == ccode)
            {
               // here we're setting the global variables that the old
               //   calls will use to do unicode conversion
               NWGetUnicodeToLocalHandle(&_hUnicodeToLocal);
               NWGetLocalToUnicodeHandle(&_hLocalToUnicode);

               _usingStandardUnicode = 0;
               _unicodeInitialized = 1;

               // set up the jump table for old functions
               _unicodeFunctions = oldUniFunctions;
            }
         }
      }
   }
   else
   {
      _usingStandardUnicode = 1;
      _unicodeInitialized = 1;

      // set up the jump table new functions
      _unicodeFunctions = standardUniFunctions;
   }

   #ifdef DEBUG_OUT
      if (_unicodeInitialized)
         printf("unicode initialized\n");

      if (_usingStandardUnicode)
         printf("using standard unicode APIs\n");
      else
         printf("using OLD unicode APIs\n");
   #endif

   return (ccode);
}

void __CleanupNativeUnicode(void)
{
   #ifdef DEBUG_OUT
      #ifdef N_PLAT_NLM
         ConsolePrintf("in cleanup native unicode\n");
      #else
         printf("in cleanup native unicode\n");
      #endif
   #endif

   if (_unicodeInitialized)
   {
      if (_usingStandardUnicode)
      {
         #ifdef N_PLAT_NLM

            nint32 NLMHandle = GetNLMHandle();
            int i;

            #ifdef DEBUG_OUT
               ConsolePrintf("cleaning up NLM standard native unicode\n");
            #endif

            _NLMUnicodeJumpTable->unloadConverter(_unicodeConverter);

            for (i = 0; i < sizeof(NLMUnicodeSymbols) / sizeof(char *); i++)
               UnimportSymbol(NLMHandle, NLMUnicodeSymbols[i]);

         #else
            #ifdef DEBUG_OUT
               printf("cleaning up standard native unicode\n");
            #endif

            NWUXUnloadConverter(_unicodeConverter);
         #endif
      }
      else
      {
         #ifdef DEBUG_OUT
            #ifdef N_PLAT_NLM
               ConsolePrintf("cleaning up OLD native unicode\n");
            #else
               printf("cleaning up OLD native unicode\n");
            #endif
         #endif

         NWFreeUnicodeTables();
      }
   }
   #ifdef DEBUG_OUT
   else
   {
      #ifdef N_PLAT_NLM
         ConsolePrintf("NLM unicode not initialized\n");
      #else
         printf("unicode not initialized\n");
      #endif
   }
   #endif
}

/*
 * Get the characters of the String object into a unicode string buffer.
 * No allocation occurs.  Assumes that len is the size of the buffer, and
 * that the buf is at least len+1 unicodes in size. The unicode buffer's
 * address is returned.
 */
unicode * jstring2unicode (
      JNIEnv *env,
      jstring string,
      unicode *buf,
      int buflen)
{
   int      strLen;
   unicode  *ujString;
   int		tempLen = buflen / 2;

   if (NULL == string || NULL == buf || 1 == tempLen)
   {
      buf [0] = '\0';
   }

   strLen = (int)(*env)->GetStringLength(env, string);
   ujString = (unicode*)(*env)->GetStringChars(env, string, 0);

   if (strLen >= tempLen)
   {
      strLen = tempLen - 1;  // guarentee null-terminated unicode string
   }

   /* copy Java String to unicode string */
   memcpy (buf, ujString, strLen * sizeof (unicode));
   buf [strLen] = '\0';    // null-terminate unicode string

   (*env)->ReleaseStringChars(env, string, ujString);

   return (buf);
}

/*
 * Posts an exception on the java side of things.  The exception will be
 * thrown when the original JNI function returns.  JNI functions can
 * call this function to do the work of resolving the exception class name
 * and posting the exception, the JNI function then must return to allow
 * the exception to be thrown on the java side.
 */
void __ThrowException
(
   JNIEnv *env,
   char *fullClassName,
   char *msg
)
{
   jclass clazz = (*env)->FindClass (env, fullClassName);

   (*env)->ThrowNew (env, clazz, msg);

   return;
}

/*
 * See standard and old functions below for implementation.
 */
NWRCODE
__UnicodeToLocalStr
(
   pnstr8 destStr,
   nuint destStrSize,
   punicode uniStr,
   jint javaLength
)
{
   if (!_unicodeInitialized)
      return (-1);

   return (*((utol_t) _unicodeFunctions[0]))(destStr, destStrSize,
         uniStr, javaLength);
}

/*
 * See standard and old functions below for implementation.
 */
NWRCODE
__UnicodePathToLocalStr
(
   pnstr8 destStr,
   nuint destStrSize,
   punicode uniPath,
   jint javaLength
)
{
   if (!_unicodeInitialized)
      return (-1);

   return (*((utol_t) _unicodeFunctions[1]))(destStr, destStrSize,
         uniPath, javaLength);
}

/*
 * See standard and old functions below for implementation.
 */
NWRCODE
__LocalToJavaStr
(
   JNIEnv *env,
   jstring *pJavaStr,
   punicode uniBuffer,
   pnstr8 srcStr
)
{
   if (!_unicodeInitialized)
      return (-1);

   return (*((ltoj_t) _unicodeFunctions[2]))(env, pJavaStr,
         uniBuffer, srcStr);
}

/*
 * Converts a unicode string to a local code page string, truncating if
 * the unicode string does not fit in 'destStr' (size in bytes
 * is 'destStrSize').
 *
 * Uses the new standard unicode APIs.
 */
NWRCODE
__StandardUnicodeToLocalStr
(
   pnstr8 destStr,
   nuint destStrSize,
   punicode uniStr,
   jint javaLength
)
{
   NWRCODE ccode;
   nuint actualLength;
   unicode save;

   // When getting the unicode from a Java string sometimes, an extra
   //   charater is tacked onto the end; so we need to truncate it
   //   manually (and we can't rely on 'unilen()' to find the right len)...
   save = uniStr[javaLength];
   uniStr[javaLength] = '\0';

   #ifdef N_PLAT_NLM
      ccode = _NLMUnicodeJumpTable->unicodeToByte (
            _unicodeConverter,
            destStr,
            destStrSize,
            uniStr,
            &actualLength);
   #else
      ccode = NWUXUnicodeToByte (
            _unicodeConverter,
            destStr,
            destStrSize,
            uniStr,
            &actualLength);
   #endif

   uniStr[javaLength] = save;

   return ((jint) ccode);
}

/*
 * Converts a unicode path (treats '\' like a path separator) to a local
 * code page string, truncating if the unicode string does not fit in
 * 'destStr' (size in bytes is 'destStrSize').
 *
 * Uses the new standard unicode APIs.
 */
NWRCODE
__StandardUnicodePathToLocalStr
(
   pnstr8 destStr,
   nuint destStrSize,
   punicode uniPath,
   jint javaLength
)
{
   NWRCODE ccode;
   nuint actualLength;
   unicode save;

   // When getting the unicode from a Java string sometimes, an extra
   //   charater is tacked onto the end; so we need to truncate it
   //   manually (and we can't rely on 'unilen()' to find the right len)...
   save = uniPath[javaLength];
   uniPath[javaLength] = '\0';

   #ifdef N_PLAT_NLM
      ccode = _NLMUnicodeJumpTable->unicodeToBytePath (
            _unicodeConverter,
            destStr,
            destStrSize,
            uniPath,
            &actualLength);
   #else
      ccode = NWUXUnicodeToBytePath (
            _unicodeConverter,
            destStr,
            destStrSize,
            uniPath,
            &actualLength);
   #endif

   uniPath[javaLength] = save;

   return ((jint) ccode);
}

/*
 * Allocates a new Java string using a local code page string.  A temporary
 * unicode buffer is needed.  This temporary buffer must be at least as
 * many unicode characters wide as 'srcStr' is bytes long (including any
 * bytes used in 'srcStr' for a NULL terminator).  If the Java object
 * allocation fails, then this function returns -1, and posts a
 * java.lang.OutOfMemoryError.  Therefore:
 *
 * IF THIS FUNCTION RETURNS -1, DO NOT CALL ANY JNI FUNCTIONS BEFORE
 * RETURNING TO JAVA, BECAUSE AN EXCEPTION HAS BEEN POSTED.
 *
 * Uses the new standard unicode APIs.
 */
NWRCODE
__StandardLocalToJavaStr
(
   JNIEnv *env,
   jstring *pJavaStr,
   punicode uniBuffer,
   pnstr8 srcStr
)
{
   NWRCODE ccode;
   nuint charsCopied;

   #ifdef N_PLAT_NLM
      ccode = _NLMUnicodeJumpTable->byteToUnicode (
            _unicodeConverter,
            uniBuffer,
            NWLmbslen (srcStr) + 1,
            srcStr,
            &charsCopied);
   #else
      ccode = NWUXByteToUnicode (
            _unicodeConverter,
            uniBuffer,
            NWLmbslen (srcStr) + 1,
            srcStr,
            &charsCopied);
   #endif

   if (ccode != 0)
      return (ccode);

   *pJavaStr = (*env)->NewString (env, uniBuffer, charsCopied);

   if (*pJavaStr == NULL)
   {
      __ThrowException (env, JAVAPKG "OutOfMemoryError", "JNI");
      return (-1);
   }

   return ((NWRCODE) 0);
}

/*
 * Converts a unicode string to a local code page string, truncating if
 * the unicode string does not fit in 'destStr' (size in bytes
 * is 'destStrSize').
 *
 * Uses the old unicode APIs.  DOES NOT HANDLE THE BACKSLASH CORRECTLY
 * IN JAPANESE OR SIMILAR CODE PAGES.
 */
NWRCODE
__OldUnicodeToLocalStr
(
   pnstr8 destStr,
   nuint destStrSize,
   punicode uniStr,
   jint javaLength
)
{
   NWRCODE ccode;
   nuint charsCopied;
   unicode save;

   // When getting the unicode from a Java string sometimes, an extra
   //   charater is tacked onto the end; so we need to truncate it
   //   manually (and we can't rely on 'unilen()' to find the right len)...
   save = uniStr[javaLength];
   uniStr[javaLength] = '\0';

   ccode = NWUnicodeToLocal (
         _hUnicodeToLocal,    // use the global variable
         destStr,
         destStrSize,
         uniStr,
         JNCP_DEF_MAP_CHAR,
         &charsCopied);

   uniStr[javaLength] = save;

   if (ccode != 0)
      return (ccode);

   // If a unicode string is too large to fit in 'destStr', then
   //   charsCopied could be 0, and we need to check for this.  The U->L
   //   function will never overwrite the array, but it doesn't null-
   //   terminate it at all, so we do that manually.
   if (charsCopied - 1 != (nuint) javaLength)
      NWLTruncateString (destStr, destStrSize);

   return ((NWRCODE) 0);
}

/*
 * Allocates a new Java string using a local code page string.  A temporary
 * unicode buffer is needed.  This temporary buffer must be at least as
 * many unicode characters wide as 'srcStr' is bytes long (including any
 * bytes used in 'srcStr' for a NULL terminator).  If the Java object
 * allocation fails, then this function returns -1, and posts a
 * java.lang.OutOfMemoryError.  Therefore:
 *
 * IF THIS FUNCTION RETURNS -1, DO NOT CALL ANY JNI FUNCTIONS BEFORE
 * RETURNING TO JAVA, BECAUSE AN EXCEPTION HAS BEEN POSTED.
 *
 * Uses the old unicode APIs.
 */
NWRCODE
__OldLocalToJavaStr
(
   JNIEnv *env,
   jstring *pJavaStr,
   punicode uniBuffer,
   pnstr8 srcStr
)
{
   NWRCODE ccode;
   nuint charsCopied;

   ccode = NWLocalToUnicode (
         _hLocalToUnicode,    // use the global variable
         uniBuffer,
         NWLmbslen (srcStr) + 1,
         srcStr,
         JNCP_DEF_MAP_CHAR,
         &charsCopied);

   if (ccode != 0)
      return (ccode);

   *pJavaStr = (*env)->NewString (env, uniBuffer, charsCopied - 1);

   if (*pJavaStr == NULL)
   {
      __ThrowException (env, JAVAPKG "OutOfMemoryError", "JNI");
      return (-1);
   }

   return ((NWRCODE) 0);
}

