/*
*******************************************************************************
*                                                                             *
* COPYRIGHT:                                                                  *
*   (C) Copyright Taligent, Inc.,  1997                                       *
*   (C) Copyright International Business Machines Corporation,  1997-1998     *
*   Licensed Material - Program-Property of IBM - All Rights Reserved.        *
*   US Government Users Restricted Rights - Use, duplication, or disclosure   *
*   restricted by GSA ADP Schedule Contract with IBM Corp.                    *
*                                                                             *
*******************************************************************************
*
*  FILE NAME : putil.c (previously putil.cpp and ptypes.cpp)
*
*   Date        Name        Description
*   04/14/97    aliu        Creation.
*   04/24/97    aliu        Added getDefaultDataDirectory() and
*                            getDefaultLocaleID().
*   04/28/97    aliu        Rewritten to assume Unix and apply general methods 
*                            for assumed case.  Non-UNIX platforms must be
*                            special-cased.  Rewrote numeric methods dealing
*                            with NaN and Infinity to be platform independent
*                             over all IEEE 754 platforms.
*   05/13/97    aliu        Restored sign of timezone 
*                            (semantics are hours West of GMT)
*   06/16/98    erm         Added IEEE_754 stuff, cleaned up isInfinite, isNan,
*                             nextDouble..
*   07/22/98    stephen     Added remainder, max, min, trunc
*   08/13/98    stephen     Added isNegativeInfinity, isPositiveInfinity
*   08/24/98    stephen     Added longBitsFromDouble
*   09/08/98    stephen     Minor changes for Mac Port
*   03/02/99    stephen     Removed openFile().  Added AS400 support.  
*                            Fixed EBCDIC tables
*   04/15/99    stephen     Converted to C.
*   06/28/99    stephen     Removed mutex locking in u_isBigEndian().
*   08/04/99    jeffrey R.  Added OS/2 changes
*******************************************************************************
*/

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <locale.h>

#include "utypes.h"

#include "umutex.h"
#include "cmemory.h"
#include "cstring.h"

#ifdef OS400
#include <float.h>
#endif

#ifdef XP_MAC
#include "Files.h"
#include "IntlResources.h"
#include "Script.h"
#endif


#ifdef WIN32
#include "locmap.h"
#include <wtypes.h>
#include <winnls.h>
#endif

#ifdef __amigaos__
#include <proto/dos.h>
#endif

/* We return QNAN rather than SNAN*/
#define NAN_TOP ((int16_t)0x7FF8)
#define INF_TOP ((int16_t)0x7FF0)

#define SIGN 0x80000000L

static char DEFAULT_CONVERTER_NAME[60] = "";
static char tempString[10] = "";

/* statics */
static bool_t fgNaNInitialized = FALSE;
static double fgNan;
static bool_t fgInfInitialized = FALSE;
static double fgInf;

/* protos */
static char* u_topNBytesOfDouble(double* d, int n);
static char* u_bottomNBytesOfDouble(double* d, int n);


/*---------------------------------------------------------------------------
  Platform utilities
  Our general strategy is to assume we're on a POSIX platform.  Platforms which
  are non-POSIX must declare themselves so.  The default POSIX implementation
  will sometimes work for non-POSIX platforms as well (e.g., the NaN-related
  functions).
  ---------------------------------------------------------------------------*/

/* Assume POSIX, and modify as necessary below*/
#if defined(_WIN32) || defined(XP_MAC) || defined(OS400) || defined(OS2) || defined(__amigaos__)
#   undef POSIX
#else
#   define POSIX
#endif

/*---------------------------------------------------------------------------
  Universal Implementations
  These are designed to work on all platforms.  Try these, and if they don't
  work on your platform, then special case your platform with new
  implementations.
  ---------------------------------------------------------------------------*/

/* Get UTC (GMT) time measured in seconds since 0:00 on 1/1/70.*/
int32_t
icu_getUTCtime()
{
#ifdef XP_MAC
  time_t t, t1, t2;
  struct tm tmrec;
  
  memset( &tmrec, 0, sizeof(tmrec) );
  tmrec.tm_year = 70;
  tmrec.tm_mon = 0;
  tmrec.tm_mday = 1;
  t1 = mktime(&tmrec);    /* seconds of 1/1/1970*/
  
  time(&t);
  memcpy( &tmrec, gmtime(&t), sizeof(tmrec) );
  t2 = mktime(&tmrec);    /* seconds of current GMT*/
  return t2 - t1;         /* GMT (or UTC) in seconds since 1970*/
#else
  time_t epochtime;
  time(&epochtime);
  return epochtime;
#endif
}

/*-----------------------------------------------------------------------------
  IEEE 754
  These methods detect and return NaN and infinity values for doubles
  conforming to IEEE 754.  Platforms which support this standard include X86,
  Mac 680x0, Mac PowerPC, AIX RS/6000, and most others.
  If this doesn't work on your platform, you have non-IEEE floating-point, and
  will need to code your own versions.  A naive implementation is to return 0.0
  for getNaN and getInfinity, and false for isNaN and isInfinite.
  ---------------------------------------------------------------------------*/

bool_t 
icu_isNaN(double number)
{
#ifdef IEEE_754
  /* This should work in theory, but it doesn't, so we resort to the more*/
  /* complicated method below.*/
  /*  return number != number;*/
  
  /* You can't return number == getNaN() because, by definition, NaN != x for*/
  /* all x, including NaN (that is, NaN != NaN).  So instead, we compare*/
  /* against the known bit pattern.  We must be careful of endianism here.*/
  /* The pattern we are looking for id:*/
  
  /*   7FFy yyyy yyyy yyyy  (some y non-zero)*/
  
  /* There are two different kinds of NaN, but we ignore the distinction*/
  /* here.  Note that the y value must be non-zero; if it is zero, then we*/
  /* have infinity.*/
  
  uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number, 
                              sizeof(uint32_t));
  uint32_t lowBits  = *(uint32_t*)u_bottomNBytesOfDouble(&number, 
                             sizeof(uint32_t));

  return ((highBits & 0x7FF00000L) == 0x7FF00000L) && 
    (((highBits & 0x000FFFFFL) != 0) || (lowBits != 0));
#else
  /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/
  /* you'll need to replace this default implementation with what's correct*/
  /* for your platform.*/
  return number != number;
#endif
}

bool_t
icu_isInfinite(double number)
{
#ifdef IEEE_754
  /* We know the top bit is the sign bit, so we mask that off in a copy of */
  /* the number and compare against infinity. [LIU]*/
  /* The following approach doesn't work for some reason, so we go ahead and */
  /* scrutinize the pattern itself. */
  /*  double a = number; */
  /*  *(int8_t*)u_topNBytesOfDouble(&a, 1) &= 0x7F;*/
  /*  return a == icu_getInfinity();*/
  /* Instead, We want to see either:*/
  
  /*   7FF0 0000 0000 0000*/
  /*   FFF0 0000 0000 0000*/
  
  uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number, 
                              sizeof(uint32_t));
  uint32_t lowBits  = *(uint32_t*)u_bottomNBytesOfDouble(&number, 
                             sizeof(uint32_t));
  
  return ((highBits  & ~SIGN) == 0x7FF00000L) && (lowBits == 0x00000000L);
#else
  /* If your platform doesn't support IEEE 754 but *does* have an infinity*/
  /* value, you'll need to replace this default implementation with what's*/
  /* correct for your platform.*/
  return number == (2.0 * number);
#endif
}

bool_t   
icu_isPositiveInfinity(double number)
{
#ifdef IEEE_754
  return (number > 0 && icu_isInfinite(number));
#else
  return icu_isInfinite(number);
#endif
}

bool_t   
icu_isNegativeInfinity(double number)
{
#ifdef IEEE_754
  return (number < 0 && icu_isInfinite(number));
#else
  return icu_isInfinite(number);
#endif
}

double 
icu_getNaN()
{
#ifdef IEEE_754  
  if( ! fgNaNInitialized) {
    umtx_lock(NULL);
    if( ! fgNaNInitialized) {
      int i;
      int8_t* p = (int8_t*)&fgNan;
      for(i = 0; i < sizeof(double); ++i) 
    *p++ = 0;
      *(int16_t*)u_topNBytesOfDouble(&fgNan, sizeof(NAN_TOP)) = NAN_TOP;
      fgNaNInitialized = TRUE;
    }
    umtx_unlock(NULL);
  }
  return fgNan;
#else
  /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/
  /* you'll need to replace this default implementation with what's correct*/
  /* for your platform.*/
  return 0.0;
#endif
}

double 
icu_getInfinity()
{
#ifdef IEEE_754  
  if (!fgInfInitialized)
    {
      int i;
      int8_t* p = (int8_t*)&fgInf;
      for(i = 0; i < sizeof(double); ++i) 
    *p++ = 0;
      *(int16_t*)u_topNBytesOfDouble(&fgInf, sizeof(INF_TOP)) = INF_TOP;
      fgInfInitialized = TRUE;
    }
  return fgInf;
#else
  /* If your platform doesn't support IEEE 754 but *does* have an infinity*/
  /* value, you'll need to replace this default implementation with what's*/
  /* correct for your platform.*/
  return 0.0;
#endif
}

double 
icu_floor(double x)
{
  return floor(x);
}

double 
icu_ceil(double x)
{
  return ceil(x);
}

double 
icu_fabs(double x)
{
  return fabs(x);
}

double 
icu_modf(double x, double* y)
{
  return modf(x, y);
}

double 
icu_fmod(double x, double y)
{
  return fmod(x, y);
}

double
icu_pow10(int32_t x)
{
#ifdef XP_MAC
  return pow(10.0, (double)x);
#else
  return pow(10.0, x);
#endif
}

double 
icu_IEEEremainder(double x, double p)
{
#ifdef IEEE_754
  int32_t hx, hp;
  uint32_t sx, lx, lp;
  double p_half;
  
  hx = *(int32_t*)u_topNBytesOfDouble(&x, sizeof(int32_t));
  lx = *(uint32_t*)u_bottomNBytesOfDouble(&x, sizeof(uint32_t));
  
  hp = *(int32_t*)u_topNBytesOfDouble(&p, sizeof(int32_t));
  lp = *(uint32_t*)u_bottomNBytesOfDouble(&p, sizeof(uint32_t));
  
  sx = hx & SIGN;
  
  hp &= 0x7fffffff;
  hx &= 0x7fffffff;
  
  /* purge off exception values */
  if((hp|lp) == 0) 
    return (x*p) / (x*p);     /* p = 0 */
  if((hx >= 0x7ff00000)||        /* x not finite */
     ((hp>=0x7ff00000) &&    /* p is NaN */
      (((hp-0x7ff00000)|lp) != 0)))
    return (x*p) / (x*p);
  
  
  if(hp <= 0x7fdfffff) 
    x = icu_fmod(x, p + p);    /* now x < 2p */
  if(((hx-hp)|(lx-lp)) == 0) 
    return 0.0 * x;
  x  = icu_fabs(x);
  p  = icu_fabs(p);
  if (hp < 0x00200000) {
    if(x + x > p) {
      x -= p;
      if(x + x >= p) 
    x -= p;
    }
  } 
  else {
    p_half = 0.5 * p;
    if(x > p_half) {
      x -= p;
      if(x >= p_half) 
    x -= p;
    }
  }
  
  *(int32_t*)u_topNBytesOfDouble(&x, sizeof(int32_t)) ^= sx;
  
  return x;
#else
  /* {sfb} need to fix this*/
  return icu_fmod(x, p);
#endif
}

double 
icu_fmax(double x, double y)
{
#ifdef IEEE_754
  int32_t lowBits;
  
  /* first handle NaN*/
  if(icu_isNaN(x) || icu_isNaN(y))
    return icu_getNaN();
  
  /* check for -0 and 0*/
  lowBits = *(uint32_t*) u_bottomNBytesOfDouble(&x, sizeof(uint32_t));
  if(x == 0.0 && y == 0.0 && (lowBits & SIGN))
    return y; 
  
  return (x > y ? x : y);
#else
  /* {sfb} fix this*/
  return x;
#endif
}

int32_t 
icu_max(int32_t x, int32_t y)
{
  return (x > y ? x : y);
}

double 
icu_fmin(double x, double y)
{
#ifdef IEEE_754
  int32_t lowBits;

  /* first handle NaN*/
  if(icu_isNaN(x) || icu_isNaN(y))
    return icu_getNaN();
  
  /* check for -0 and 0*/
  lowBits = *(uint32_t*) u_bottomNBytesOfDouble(&y, sizeof(uint32_t));
  if(x == 0.0 && y == 0.0 && (lowBits & SIGN))
    return y; 
  
  return (x > y ? y : x);
#else
  /* {sfb} fix this*/
  return x;
#endif
}

int32_t 
icu_min(int32_t x, int32_t y)
{
  return (x > y ? y : x);
}

/**
 * Truncates the given double.
 * trunc(3.3) = 3.0, trunc (-3.3) = -3.0
 * This is different than calling floor() or ceil():
 * floor(3.3) = 3, floor(-3.3) = -4
 * ceil(3.3) = 4, ceil(-3.3) = -3
 */
double 
icu_trunc(double d)
{
#ifdef IEEE_754

  int32_t lowBits;
  
  /* handle error cases*/
  if(icu_isNaN(d))        return icu_getNaN();
  if(icu_isInfinite(d))        return icu_getInfinity();
  
  lowBits = *(uint32_t*) u_bottomNBytesOfDouble(&d, sizeof(uint32_t));
  if( (d == 0.0 && (lowBits & SIGN)) || d < 0)
    return ceil(d);
  else
    return floor(d);
#else
  return d >= 0 ? floor(d) : ceil(d);
#endif
}

void        
icu_longBitsFromDouble(double d, int32_t *hi, uint32_t *lo)
{
  *hi = *(int32_t*)u_topNBytesOfDouble(&d, sizeof(int32_t));
  *lo = *(uint32_t*)u_bottomNBytesOfDouble(&d, sizeof(uint32_t));
}
    

/**
 * Return the floor of the log base 10 of a given double.
 * This method compensates for inaccuracies which arise naturally when
 * computing logs, and always give the correct value.  The parameter
 * must be positive and finite.
 * (Thanks to Alan Liu for supplying this function.)
 */
int16_t 
icu_log10(double d)
{
  /* The reason this routine is needed is that simply taking the*/
  /* log and dividing by log10 yields a result which may be off*/
  /* by 1 due to rounding errors.  For example, the naive log10*/
  /* of 1.0e300 taken this way is 299, rather than 300.*/
  double log10 = log(d) / log(10.0);
  int16_t ilog10 = (int16_t)floor(log10);
  
  /* Positive logs could be too small, e.g. 0.99 instead of 1.0*/
  if (log10 > 0 && d >= pow(10.0, ilog10 + 1))
    ++ilog10;
  
  /* Negative logs could be too big, e.g. -0.99 instead of -1.0*/
  else if (log10 < 0 && d < pow(10.0, ilog10))
    --ilog10;
  
  return ilog10;
}

int32_t 
icu_digitsAfterDecimal(double x)
{
  char buffer[20];
  int16_t numDigits;
  char *p;
  int16_t ptPos, exponent;

  /* negative numbers throw off the calculations*/
  x = fabs(x);
  
  /* cheat and use the string-format routine to get a string representation*/
  /* (it handles mathematical inaccuracy better than we can), then find out */
  /* many characters are to the right of the decimal point */
  sprintf(buffer, "%.9g", x);
  p = icu_strchr(buffer, '.');
  if (p == 0)
    return 0;
  
  ptPos = p - buffer;
  numDigits = strlen(buffer) - ptPos - 1;
  
  /* if the number's string representation is in scientific notation, find */
  /* the exponent and take it into account*/
  exponent = 0;
  p = icu_strchr(buffer, 'e');
  if (p != 0) {
    int16_t expPos = p - buffer;
    numDigits -= strlen(buffer) - expPos;
    exponent = atoi(p + 1);
  }
  
  /* the string representation may still have spurious decimal digits in it, */
  /* so we cut off at the ninth digit to the right of the decimal, and have */
  /* to search backward from there to the first non-zero digit*/
  if (numDigits > 9) {
    numDigits = 9;
    while (numDigits > 0 && buffer[ptPos + numDigits] == '0')
      --numDigits;
  }
  numDigits -= exponent;
  return numDigits;
}

/*---------------------------------------------------------------------------
  Platform-specific Implementations
  Try these, and if they don't work on your platform, then special case your
  platform with new implementations.
  ---------------------------------------------------------------------------*/

/* Time zone utilities */

#ifdef __amigaos__
static int tzone = 0;
static char tzname1[20];
static char tzname2[20];
#endif

void 
icu_tzset()
{
#ifdef POSIX
  tzset();
#endif

#if defined(OS400) || defined(XP_MAC)
  /* no initialization*/
#endif

#if defined(WIN32) || defined(OS2)
  _tzset();
#endif

#ifdef  __amigaos__
  static char t[80];
  int status;
  int n;
  status = GetVar("TZ", t, sizeof(t), GVF_GLOBAL_ONLY);
  if (status == -1) {
    tzone = 0;
    strcpy(tzname1, "GMT");
    tzone = 0;
    strcpy(tzname2, "GMT");
  }else{
    n = sscanf(t, "%3s%d%s", tzname1, &tzone, tzname2);
    switch (n) {
      case 0:
	strcpy(tzname1, "GMT");
        tzone = 0;
	strcpy(tzname2, "GMT");
	break;
      case 1:
        tzone = 0;
	strcpy(tzname2, tzname1);
	break;
      case 2:
	strcpy(tzname2, tzname1);
        break;
      default:
        tzone *= 3600;
	break;
    }
  }
#endif
}

int32_t 
icu_timezone()
{
#ifdef POSIX
  return timezone;
#endif

#if defined(OS400) || defined(XP_MAC)
  time_t t, t1, t2;
  struct tm tmrec;
  bool_t dst_checked;
  
  time(&t);
  memcpy( &tmrec, localtime(&t), sizeof(tmrec) );
  dst_checked = (tmrec.tm_isdst != 0); /* daylight savings time is checked*/
  t1 = mktime(&tmrec);                 /* local time in seconds*/
  memcpy( &tmrec, gmtime(&t), sizeof(tmrec) );
  t2 = mktime(&tmrec);                 /* GMT (or UTC) in seconds*/
  int32_t tdiff = t2 - t1;
  /* imitate NT behaviour, which returns same timezone offset to GMT for 
     winter and summer*/
  if (dst_checked) tdiff += 3600;
  return tdiff;
#endif

#if defined(WIN32) || defined(OS2)
  return _timezone;
#endif

#ifdef __amigaos__
  return tzone;
#endif
}

char* 
icu_tzname(int index)
{
#ifdef POSIX
  return tzname[index];
#endif

#if defined(OS400) || defined(XP_MAC)
  return "";
#endif

#if defined(WIN32) || defined(OS2)
  return _tzname[index];
#endif

#ifdef __amigaos__
  if (index == 0) {
    return tzname1;
  }else{
    return tzname2;
  }
#endif
}

const char* 
icu_getDefaultDataDirectory()
{
#if defined(POSIX) || defined(__amigaos__)
  static char *PATH = 0;
  if(PATH == 0) {
    umtx_lock(NULL);
    if(PATH == 0) {
      /* Normally, the locale and converter data will be installed in
         the same tree as the ICU libraries - typically /usr/local/lib
         for the libraries, /usr/local/include for the headers, and
         /usr/local/share for the binary data.  However, the directory
         where the ICU looks for the binary data can be overridden by
         setting the environment variable ICU_DATA */
      char *dir = getenv("ICU_DATA");

      /* If the environment variable is set, use it */
      if(dir != 0) {
        PATH = dir;
      }
      /* Otherwise, use the compiled in default */
      else {
        PATH = ICU_DATA_DIR;
      }
    }
    umtx_unlock(NULL);
  }
  return PATH;
#endif

#ifdef OS400
  return "/icu/data/";
#endif

#ifdef XP_MAC
  static char path[256];
  char* mainDir;
  char* relPath = ":icu:data:";
  
  Str255 volName;
  int16_t volNum;
  OSErr err = GetVol( volName, &volNum );
  if (err != noErr) 
    volName[0] = 0;
  mainDir = (char*) &(volName[1]);
  mainDir[volName[0]] = 0;
  int32_t lenMainDir = strlen( mainDir );
  int32_t lenRelPath = strlen( relPath );
  if (sizeof(path) < lenMainDir + lenRelPath + 2) { 
    path[0] = 0; 
    return path; 
  }
  icu_strcpy( path, mainDir );
  icu_strcat( path, relPath );
  
  return path;
#endif

#ifdef WIN32
  char * dpath;
  dpath = getenv("ICU_DATA");
  if (!dpath || !*dpath)
      return "\\icu\\data\\";
  return dpath;
#endif

#ifdef OS2
  char * dpath;
  dpath = getenv("ICU_DATA");
  if (!dpath || !*dpath)
      return "\\icu\\data\\";
  return dpath;
#endif


}

/* Macintosh-specific locale information */
#ifdef XP_MAC

struct mac_lc_rec {
  int32_t script;
  int32_t region;
  int32_t lang;
  int32_t date_region;
  char* posixID;
};
/* To do: This will be updated with a newer version from www.unicode.org web
   page when it's available.*/
#define MAC_LC_MAGIC_NUMBER -5
#define MAC_LC_INIT_NUMBER -9

mac_lc_rec mac_lc_recs[] = {
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 0, "en_US",  
  /* United States*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 1, "fr_FR",  
  /* France*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 2, "en_GB",  
  /* Great Britain*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 3, "de_DE",  
  /* Germany*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 4, "it_IT",  
  /* Italy*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 5, "nl_NL", 
  /* Metherlands*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 6, "fr_BE", 
  /* French for Belgium or Lxembourg*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 7, "sv_SE", 
  /* Sweden*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 9, "da_DK", 
  /* Denmark*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 10, "pt_PT", 
  /* Portugal*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 11, "fr_CA", 
  /* French Canada*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 13, "is_IS", 
  /* Israel*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 14, "ja_JP", 
  /* Japan*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 15, "en_AU", 
  /* Australia*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 16, "ar_AE", 
  /* the Arabic world (?)*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 17, "fi_FI", 
  /* Finland*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 18, "fr_CH", 
  /* French for Switzerland*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 19, "de_CH", 
  /* German for Switzerland*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 20, "EL_GR", 
  /* Greece*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 21, "is_IS", 
  /* Iceland ===*/
  /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 22, "", 
    // Malta ===*/
  /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 23, "", 
    // Cyprus ===*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 24, "tr_TR", 
  /* Turkey ===*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 25, "sh_YU", 
  /* Croatian system for Yugoslavia*/
  /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 33, "", 
    // Hindi system for India*/
  /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 34, "", 
    // Pakistan*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 41, "lt_LT", 
  /* Lithuania*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 42, "pl_PL", 
  /* Poland*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 43, "hu_HU", 
  /* Hungary*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 44, "et_EE", 
  /* Estonia*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 45, "lv_LV", 
  /* Latvia*/
  /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 46, "", 
    // Lapland  [Ask Rich for the data. HS]*/
  /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 47, "", 
    // Faeroe Islands*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 48, "fa_IR", 
  /* Iran*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 49, "ru_RU", 
  /* Russia*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 50, "en_IE", 
  /* Ireland*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 51, "ko_KR", 
  /* Korea*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 52, "zh_CN", 
  /* People's Republic of China*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 53, "zh_TW", 
  /* Taiwan*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 54, "th_TH", 
  /* Thailand*/
  
  /* fallback is en_US*/
  MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 
  MAC_LC_MAGIC_NUMBER, "en_US"
};

#endif

const char* 
icu_getDefaultLocaleID()
{
#if defined(POSIX) || defined(__amigaos__)
  char* posixID = getenv("LC_ALL");
  if (posixID == 0) posixID = getenv("LANG");
  if (posixID == 0) posixID = setlocale(LC_ALL, NULL);
  if (icu_strcmp("C", posixID) == 0) posixID = "en_US";
  return posixID;
#endif

#ifdef OS400
  /* TBD */
  return "";
#endif

#ifdef XP_MAC
  int32_t script = MAC_LC_INIT_NUMBER; 
  /* = IntlScript(); or GetScriptManagerVariable(smSysScript);*/
  int32_t region = MAC_LC_INIT_NUMBER; 
  /* = GetScriptManagerVariable(smRegionCode);*/
  int32_t lang = MAC_LC_INIT_NUMBER;   
  /* = GetScriptManagerVariable(smScriptLang);*/
  int32_t date_region = MAC_LC_INIT_NUMBER;
  char* posixID = 0;
  Intl1Hndl ih;
  
  ih = (Intl1Hndl) GetIntlResource(1);
  if (ih) date_region = ((uint16_t)(*ih)->intl1Vers) >> 8;
  
  int32_t count = sizeof(mac_lc_recs) / sizeof(mac_lc_rec);
  for (int32_t i = 0; i < count; i++) {
    if ( ((mac_lc_recs[i].script == MAC_LC_MAGIC_NUMBER)      
      || (mac_lc_recs[i].script == script))
     && ((mac_lc_recs[i].region == MAC_LC_MAGIC_NUMBER)
         || (mac_lc_recs[i].region == region))
     && ((mac_lc_recs[i].lang == MAC_LC_MAGIC_NUMBER)
         || (mac_lc_recs[i].lang == lang))
     && ((mac_lc_recs[i].date_region == MAC_LC_MAGIC_NUMBER) 
         || (mac_lc_recs[i].date_region == date_region))
     ) {
      posixID = mac_lc_recs[i].posixID;
      break;
    }
  }
  
  return posixID;
#endif

#ifdef WIN32
  LCID id = GetThreadLocale();
  return T_convertToPosix(id);
#endif

#ifdef OS2
    char * locID;

    locID = getenv("LC_ALL");
    if (!locID || !*locID)
        locID = getenv("LANG");
    if (!locID || !*locID) {
        locID = "C";
    }
    if (!stricmp(locID, "c") || !stricmp(locID, "posix") ||
        !stricmp(locID, "univ"))
        locID = "en_US";
    return locID;
#endif

}

/* end of platform-specific implementation */

double 
icu_nextDouble(double d, bool_t next)
{
#ifdef IEEE_754
  int32_t highBits;
  uint32_t lowBits;
  int32_t highMagnitude;
  uint32_t lowMagnitude;
  double result;
  uint32_t *highResult, *lowResult;
  uint32_t signBit;

  /* filter out NaN's */
  if (icu_isNaN(d)) {
    return d;
  }
  
  /* zero's are also a special case */
  if (d == 0.0) {
    double smallestPositiveDouble = 0.0;
    uint32_t *lowBits = 
      (uint32_t *)u_bottomNBytesOfDouble(&smallestPositiveDouble, 
                     sizeof(uint32_t));
    
    *lowBits = 1;
    
    if (next) {
      return smallestPositiveDouble;
    } else {
      return -smallestPositiveDouble;
    }
  }
  
  /* if we get here, d is a nonzero value */
  
  /* hold all bits for later use */
  highBits = *(int32_t*)u_topNBytesOfDouble(&d, sizeof(uint32_t));
  lowBits = *(uint32_t*)u_bottomNBytesOfDouble(&d, sizeof(uint32_t));
  
  /* strip off the sign bit */
  highMagnitude = highBits & ~SIGN;
  lowMagnitude = lowBits;
  
  /* if next double away from zero, increase magnitude */
  if ((highBits >= 0) == next) {
    if (highMagnitude != 0x7FF00000L || lowMagnitude != 0x00000000L) {
      lowMagnitude += 1;
      if (lowMagnitude == 0) {
    highMagnitude += 1;
      }
    }
  }
  /* else decrease magnitude */
  else {
    lowMagnitude -= 1;
    if (lowMagnitude > lowBits) {
      highMagnitude -= 1;
    }
  }
  
  /* construct result and return */
  signBit = highBits & SIGN;
  highResult = (uint32_t *)u_topNBytesOfDouble(&result, sizeof(uint32_t));
  lowResult  = (uint32_t *)u_bottomNBytesOfDouble(&result, sizeof(uint32_t));
  
  *highResult = signBit | highMagnitude;
  *lowResult  = lowMagnitude;
  return result;
#else
  /* This is the portable implementation...*/
  /* a small coefficient within the precision of the mantissa*/
  static const double smallValue = 1e-10;  
  double epsilon = ((d<0)?-d:d) * smallValue; /* first approximation*/
  if (epsilon == 0) epsilon = smallValue; /* for very small d's*/
  if (!next) epsilon = -epsilon;
  double last_eps = epsilon * 2.0;
  /* avoid higher precision possibly used for temporay values*/
  double sum = d + epsilon; 
  while ((sum != d) && (epsilon != last_eps)) {
    last_eps = epsilon;
    epsilon /= 2.0;
    sum = d + epsilon;
  }
  return d + last_eps;
#endif
}

static char*
u_topNBytesOfDouble(double* d, int n)
{
  return U_IS_BIG_ENDIAN ? (char*)d : (char*)(d + 1) - n;
}

static char* u_bottomNBytesOfDouble(double* d, int n)
{
  return U_IS_BIG_ENDIAN ? (char*)(d + 1) - n : (char*)d;
}

const char* icu_getDefaultCodepage()
{
  /*Lazy evaluates DEFAULT_CONVERTER_NAME*/
  if (DEFAULT_CONVERTER_NAME[0]) return DEFAULT_CONVERTER_NAME;
#if defined(OS400)
  /* Currently TBD 
     in the future should use thread specific CP
  */
#elif defined(OS390)
  icu_strcpy(DEFAULT_CONVERTER_NAME, "ibm-1047");
#elif defined(XP_MAC)
  /* TBD */
#elif defined(WIN32) 
  icu_strcpy(DEFAULT_CONVERTER_NAME, "cp");
  icu_strcat(DEFAULT_CONVERTER_NAME, _itoa(GetACP(), tempString, 10));
#elif defined(POSIX)
  icu_strcpy(DEFAULT_CONVERTER_NAME, "LATIN_1");

#else 
  icu_strcpy(DEFAULT_CONVERTER_NAME, "LATIN_1");
#endif
  
  return DEFAULT_CONVERTER_NAME;
}
