delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2001/02/23/12:31:37

Date: Fri, 23 Feb 2001 12:31:25 -0500
From: AAganichev AT netscape DOT net (Alexander Aganichev)
To: djgpp-workers AT delorie DOT com
Subject: locale support - 3rd edition
Mime-Version: 1.0
Message-ID: <015762A2.5B68681B.09ACFA57@netscape.net>
X-Mailer: Franklin Webmailer 1.0
Reply-To: djgpp-workers AT delorie DOT com

Hi, that's me again :-)

That's a third edition of locale support (I haven't changed strcoll() and strftime() any more, so please peek them from previous patch).

I wonder if someone intrested in these patches. Any comments except of formatting style? ;-)

/* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */
/* Portions copyright (C) 1994-1996 by Eberhard Mattes */
/* Modified 1999-2001 by Alexander S. Aganichev <aaganichev AT netscape DOT net> */

#include <locale.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <dpmi.h>
#include <libc/farptrgs.h>
#include <go32.h>
#include <limits.h>

#define LOCALE_NAME_MAX (5+1+10)    /* "ru_RU.866" */

static char lcn_collate[LOCALE_NAME_MAX + 1] = "C";
static char lcn_ctype[LOCALE_NAME_MAX + 1] = "C";
static char lcn_monetary[LOCALE_NAME_MAX + 1] = "C";
static char lcn_numeric[LOCALE_NAME_MAX + 1] = "C";
static char lcn_time[LOCALE_NAME_MAX + 1] = "C";

static char currency_symbol[6] = "";
static char mon_decimal_point[2] = "";
static char mon_thousands_sep[2] = "";
static char decimal_point[2] = ".";
static char thousands_sep[2] = "";

static struct __loc2id {
  int id;
  const char loc[LOCALE_NAME_MAX + 1];
  int len;
} loc2id[] = {

  /* add your country here ;-) */
  {
    1, "en_US", 5
  },
  {
    7, "ru_RU", 5
  },
  {
    33, "fr_FR", 5
  },
  {
    49, "de_DE", 5
  }
};

static const int cat[5] =
{LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME};

extern unsigned char __dj_collate_table[];
extern char __dj_date_format[];
extern char __dj_time_format[];

static int
setlocale2(int category, const char *locale)
{
  int segment, selector;
  __dpmi_regs regs;
  struct lconv *lcnv = localeconv();
  int i, rv = 0;
  char buf[LOCALE_NAME_MAX + 1];

  if (category == LC_ALL) {
    rv = !setlocale2(LC_COLLATE, locale);
    rv += !setlocale2(LC_CTYPE, locale);
    rv += !setlocale2(LC_MONETARY, locale);
    rv += !setlocale2(LC_NUMERIC, locale);
    rv += !setlocale2(LC_TIME, locale);
    return !rv;
  }

  if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
    switch (category) {
    case LC_COLLATE:
      for (i = 0; i < 256; i++)
    __dj_collate_table[i] = i;
      strcpy(lcn_collate, locale);
      return 1;
    case LC_CTYPE:
      for (i = 128; i < 256; i++) {
    __dj_ctype_tolower[i + 1] = i;
    __dj_ctype_toupper[i + 1] = i;
    __dj_ctype_flags[i + 1] = 0;
      }
      strcpy(lcn_ctype, locale);
      return 1;
    case LC_MONETARY:
      if (lcnv) {
    strcpy(currency_symbol, "");
    lcnv->int_curr_symbol = lcnv->currency_symbol = currency_symbol;
    strcpy(mon_thousands_sep, "");
    lcnv->mon_thousands_sep = mon_thousands_sep;
    strcpy(mon_decimal_point, "");
    lcnv->mon_decimal_point = mon_decimal_point;
    /* lcnv->mon_grouping = ""; */
    /* lcnv->negative_sign = ""; */
    /* lcnv->positive_sign = ""; */
    lcnv->int_frac_digits = lcnv->frac_digits = CHAR_MAX;
    lcnv->p_cs_precedes = lcnv->n_cs_precedes = CHAR_MAX;
    lcnv->p_sep_by_space = lcnv->n_sep_by_space = CHAR_MAX;
    /* lcnv->p_sign_posn = lcnv->n_sign_posn = CHAR_MAX; */
    strcpy(lcn_monetary, locale);
    return 1;
      }
      else
    return 0;
    case LC_NUMERIC:
      if (lcnv) {
    strcpy(thousands_sep, "");
    lcnv->thousands_sep = thousands_sep;
    strcpy(decimal_point, ".");
    lcnv->decimal_point = decimal_point;
    /* lcnv->grouping = ""; */
    strcpy(lcn_numeric, locale);
    return 1;
      }
      else
    return 0;
    case LC_TIME:
      strcpy(__dj_date_format, "%m/%d/%y");
      strcpy(__dj_time_format, "%H:%M:%S");
      strcpy(lcn_time, locale);
      return 1;
    }
  }
  if ((segment = __dpmi_allocate_dos_memory(3, &selector)) != -1) {
    int CID = 0xffff, CCP = 0xffff;

    if (*locale) {
      int len;
      const char *p = strchr(locale, '.');
      if (p == NULL)
    p = locale + strlen(locale);
      len = p - locale;
      for (i = 0; i < sizeof(loc2id) / sizeof(struct __loc2id); i++)
    if (!strncmp(locale, loc2id[i].loc, len >= loc2id[i].len ? len : loc2id[i].len)) {
      CID = loc2id[i].id;
      break;
    }
      if (*p == '.')
    CCP = atoi(p + 1);
    }

    regs.h.ah = 0x65;
    regs.h.al = 0x01;
    regs.x.bx = CCP;
    regs.x.dx = CID;
    regs.x.cx = 41;
    regs.x.es = segment;
    regs.x.di = 0;
    __dpmi_int(0x21, &regs);
    if (!(regs.x.flags & 1) && regs.x.cx == 41) {
      if (!*locale) {
    CID = _farpeekw(selector, 3);
    CCP = _farpeekw(selector, 5);
    locale = buf;
    strcpy(buf, "en_US.");
    for (i = 0; i < sizeof(loc2id) / sizeof(struct __loc2id); i++)
      if (loc2id[i].id == CID) {
        strcpy(buf, loc2id[i].loc);
        buf[loc2id[i].len] = '.';
        break;
      }
    itoa(CCP, &buf[strlen(buf)], 10);
      }
      switch (category) {
      case LC_COLLATE:
    regs.h.ah = 0x65;
    regs.h.al = 0x06;
    regs.x.bx = CCP;
    regs.x.dx = CID;
    regs.x.cx = 5;
    regs.x.es = segment;
    regs.x.di = 0;
    __dpmi_int(0x21, &regs);
    if (!(regs.x.flags & 1) && (regs.x.cx == 5)) {
      unsigned int table = _farpeekw(selector, 3) * 16 + _farpeekw(selector, 1);
      int size = _farpeekw(_dos_ds, table);
      movedata(_dos_ds, table + 2, _my_ds(), (unsigned int) __dj_collate_table, size);
      rv = 1;
    }
    else
      rv = 0;
    strcpy(lcn_collate, locale);
    rv = 1;
    break;
      case LC_CTYPE:
    regs.h.ah = 0x65;
    regs.h.al = 0x02;
    regs.x.bx = CCP;
    regs.x.dx = CID;
    regs.x.cx = 5;
    regs.x.es = segment;
    regs.x.di = 0;
    __dpmi_int(0x21, &regs);
    if (!(regs.x.flags & 1) && (regs.x.cx == 5)) {
      unsigned int table = _farpeekw(selector, 3) * 16 + _farpeekw(selector, 1);
      int size = _farpeekw(_dos_ds, table);
      movedata(_dos_ds, table + 2, _my_ds(), (unsigned int) &(__dj_ctype_toupper[128 + 1]), size);
      /* let's build lowercase table from uppercase... */
      for (i = 0; i < size; i++) {
        int c = toupper(i + 128);
        if ((c != i + 128) && (c > 127))
          __dj_ctype_tolower[c + 1] = i + 128;
      }
      for (i = 128; i < 256; i++) {
        /* By this conversion we could break something like 0xe1 in CP437
           * but in most cases it better works this way */
        if ((toupper(tolower(i)) != i) && (tolower(toupper(i)) != i))
          __dj_ctype_tolower[i + 1] = __dj_ctype_toupper[i + 1] = i;
        if (toupper(tolower(i)) != toupper(i))
          __dj_ctype_toupper[i + 1] = i;
        if (tolower(toupper(i)) != tolower(i))
          __dj_ctype_tolower[i + 1] = i;
        /* Actually isgraph(), ispunct() and isspace() will return wrong
           * results for some letters like 0xff in CP866 but we can't *
           detect them reliably */
        __dj_ctype_flags[i + 1] = __dj_ISPRINT | __dj_ISGRAPH;
        if (tolower(i) != toupper(i))
          __dj_ctype_flags[i + 1] |= __dj_ISALPHA | __dj_ISALNUM | ((i == toupper(i)) ? __dj_ISUPPER : __dj_ISLOWER);
        else
          __dj_ctype_flags[i + 1] |= __dj_ISPUNCT;
      }
      rv = 1;
    }
    else
      rv = 0;
    strcpy(lcn_ctype, locale);
    break;
      case LC_MONETARY:
    if (lcnv) {
      movedata(selector, 9, _my_ds(), (unsigned) currency_symbol, 5);
      lcnv->int_curr_symbol = lcnv->currency_symbol = currency_symbol;
      movedata(selector, 14, _my_ds(), (unsigned) mon_thousands_sep, 2);
      lcnv->mon_thousands_sep = mon_thousands_sep;
      movedata(selector, 16, _my_ds(), (unsigned) mon_decimal_point, 2);
      lcnv->mon_decimal_point = mon_decimal_point;
      /* lcnv->mon_grouping = ""; */
      /* lcnv->negative_sign = ""; */
      /* lcnv->positive_sign = ""; */
      lcnv->int_frac_digits = lcnv->frac_digits = _farpeekb(selector, 24);
      lcnv->p_cs_precedes = lcnv->n_cs_precedes = _farpeekb(selector, 23) & 1;
      lcnv->p_sep_by_space = lcnv->n_sep_by_space = _farpeekb(selector, 23) & 2;
      /* lcnv->p_sign_posn = lcnv->n_sign_posn = CHAR_MAX; */
      strcpy(lcn_monetary, locale);
      rv = 1;
    }
    else
      rv = 0;
    break;
      case LC_NUMERIC:
    if (lcnv) {
      movedata(selector, 14, _my_ds(), (unsigned) thousands_sep, 2);
      lcnv->thousands_sep = thousands_sep;
      movedata(selector, 16, _my_ds(), (unsigned) decimal_point, 2);
      lcnv->decimal_point = decimal_point;
      /* lcnv->grouping = ""; */
      strcpy(lcn_numeric, locale);
      rv = 1;
    }
    else
      rv = 0;
    break;
      case LC_TIME:
    switch (_farpeekw(selector, 7)) {
    case 0:
    default:
      strcpy(__dj_date_format, "%m/%d/%y");
      break;
    case 1:
      strcpy(__dj_date_format, "%d/%m/%y");
      break;
    case 2:
      strcpy(__dj_date_format, "%y/%m/%d");
      break;
    }
    __dj_date_format[2] = __dj_date_format[5] = _farpeekb(selector, 18);
    if (_farpeekb(selector, 24) & 1)
      strcpy(__dj_time_format, "%H:%M:%S");
    else
      strcpy(__dj_time_format, "%I:%M:%S %p");
    __dj_time_format[2] = __dj_time_format[5] = _farpeekb(selector, 20);
    strcpy(lcn_time, locale);
    rv = 1;
    break;
      }
    }
    else
      rv = 0;
    __dpmi_free_dos_memory(selector);
    return rv;
  }
  else
    return 0;
}

char *
setlocale(int category, const char *locale)
{
  int i;
  static char rv[5 * (LOCALE_NAME_MAX + 1)];

  switch (category) {
  case LC_ALL:
    if (stricmp(lcn_collate, lcn_ctype) == 0
    && stricmp(lcn_collate, lcn_monetary) == 0
    && stricmp(lcn_collate, lcn_numeric) == 0
    && stricmp(lcn_collate, lcn_time) == 0)
      strcpy(rv, lcn_collate);
    else {
      /* Create a comma-separated list of locales for all the categories. */

      strcpy(rv, lcn_collate);
      strcat(rv, ",");
      strcat(rv, lcn_ctype);
      strcat(rv, ",");
      strcat(rv, lcn_monetary);
      strcat(rv, ",");
      strcat(rv, lcn_numeric);
      strcat(rv, ",");
      strcat(rv, lcn_time);
    }
    break;
  case LC_COLLATE:
    strcpy(rv, lcn_collate);
    break;
  case LC_CTYPE:
    strcpy(rv, lcn_ctype);
    break;
  case LC_MONETARY:
    strcpy(rv, lcn_monetary);
    break;
  case LC_NUMERIC:
    strcpy(rv, lcn_numeric);
    break;
  case LC_TIME:
    strcpy(rv, lcn_time);
    break;
  default:
    return NULL;
  }

  if (locale != 0) {

    if (*locale == '\0') {
      char *lc = getenv("LANG");
      if (lc != NULL)
    locale = lc;
    }

    if ((category != LC_ALL) || (strchr(locale, ',') == NULL))
      return setlocale2(category, locale) ? rv : NULL;
    else {
      char *s1, *s2;
      char buf[5 * (LOCALE_NAME_MAX + 1)];

      strcpy(buf, locale);
      s1 = buf;
      for (i = 0; i < 5; i++) {
    s2 = strchr(s1, ',');
    if (s2 != NULL)
      *s2 = '\0';
    if (!setlocale2(cat[i], s1) || ((s2 == NULL) && (i != 4)))
      return NULL;
      }
    }
  }
  return rv;
}

#ifdef TEST
#include <stdio.h>

unsigned char __dj_collate_table[256];
char __dj_date_format[10] = "%m/%d/%y";
char __dj_time_format[16] = "%H:%M:%S";

int
main(int ac, char *av[])
{
  int i;
  const char *loc = (ac == 1) ? "" : av[1];
  char *lc = setlocale(LC_ALL, loc);
  lc = setlocale(LC_ALL, NULL);
  printf("Locale: %s\n", lc ? lc : "not detected");
  for (i = 0; i < 256; i++)
    printf("%c%c%c|", (char) i, tolower(i), toupper(i));
  printf("\n");
  for (i = 0; i < 256; i++)
    printf("%02xh ", __dj_collate_table[i]);
  printf("\n%f\n%s %s\n", 1000456.23, __dj_date_format, __dj_time_format);
}
#endif

--
Alexander Aganichev
Hypercom Europe Limited, Inc.
Software Engineer


__________________________________________________________________
Get your own FREE, personal Netscape Webmail account today at http://webmail.netscape.com/

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019