delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2004/10/31/06:22:23

X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f
Date: Sun, 31 Oct 2004 04:22:08 -0700
From: Brian Inglis <Brian DOT Inglis AT SystematicSw DOT ab DOT ca>
Subject: Re: C99 strftime and Related Changes
In-reply-to: <200410311021.i9VALPev002762@speedy.ludd.ltu.se>
To: djgpp-workers AT delorie DOT com
Message-id: <7lh9o05hev9rofoptbidnldhehdocdoa4k@4ax.com>
Organization: Systematic Software
MIME-version: 1.0
X-Mailer: Forte Agent 1.93/32.576 English (American)
References: <gfrnn0t2u9qtc45t8rgdju7iflbmgurctu AT 4ax DOT com>
<200410311021 DOT i9VALPev002762 AT speedy DOT ludd DOT ltu DOT se>
X-MIME-Autoconverted: from quoted-printable to 8bit by delorie.com id i9VBMKoJ026794
Reply-To: djgpp-workers AT delorie DOT com
Errors-To: nobody AT delorie DOT com
X-Mailing-List: djgpp-workers AT delorie DOT com
X-Unsubscribes-To: listserv AT delorie DOT com

On Sun, 31 Oct 2004 11:21:25 +0100 (CET), ams AT ludd DOT ltu DOT se wrote:

>According to Brian Inglis:
>> >I don't understand where those liblocal.02/ patches should be
>> >applied. Can somebody explain?
>> 
>> To the contributed liblocale library by Alexander S. Aganichev
>> <asa AT users DOT sf DOT net> in $DJDIR/contrib/liblocal.02/src/ansi/... 
>
>Hohum... I'm still a little confused. I don't have that directory.
>
>Anyway I dug up some file Alexander posted 2002-01-02 containing
>setlocale()! Is this what we are talking about?

Yes. 

>If it is, then I wonder:
>
>1. I guess this is a replacement for libc/ansi/locale/setlocal.c. Is
>it? 

It was packaged as a separate library liblocal.a containing setlocal.c
and a version of strftime.c which is now identical to time/strftime.c. 
It uses the DOS country information to set some locale characteristics. 

>2. As Alexander posted it as a new file (which I can understand,
>because the current file is 13 lines and the new one is very long) it
>would be easiest for me if you, Brian, just gave me the new file with
>your patches already applied. Then I'd know it is up-to-date as well.

See below. 

>3. Does the documentation need any updating? Because of the large
>amount of new code that is likely, but I don't see any changes.

Just a bug fix to operate correctly and pass the tests:
fix problem resetting standard locale and returning updated locale name

Index: setlocale.c
===================================================================
RCS file: contrib/liblocal.02/src/ansi/locale/setlocale.c,v
diff -p -u /dev/null setlocale.c
--- /dev/null	0000-00-00 00:00:00.000000000 -0000
+++ setlocale.c	2004-02-24 01:57:18.000000000 -0700
@@ -0,0 +1,634 @@
+/*
+ * setlocal.c: Set or read international environment
+ *
+ * Copyright (C) 2002 Alexander S. Aganichev <asa AT users DOT sf DOT net>
+ *
+ * This software may be used freely so long as this copyright notice is
+ * left intact.  There is no warranty on this software.
+ */
+
+#include <locale.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dpmi.h>
+#ifdef TEST
+#include <sys/farptr.h>
+#else
+#include <libc/farptrgs.h>
+#endif
+#include <go32.h>
+#include <limits.h>
+
+/*
+ * Number of supported categories
+ */
+#define LC_CATEGORIES 5
+
+/*
+ * Maximum name length for locale
+ */
+#define LC_MAXNAMESIZE 16
+
+/*
+ * Buffer size for the defined locales
+ */
+#define LC_BUFSIZ (LC_MAXNAMESIZE * LC_CATEGORIES)
+
+/*
+ * This variable contains the locales defined in the order LC_COLLATE,
+ * LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME separated by comma. If all
+ * locales are the same, then locale specifyed only once.
+ */
+static char lc_buffer[LC_BUFSIZ];
+static char lc_current[LC_CATEGORIES][LC_MAXNAMESIZE] =
+{
+  "C", "C", "C", "C", "C"
+};
+
+/*
+ * This is what we can extract from country.sys for our purposes.
+ */
+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] = "";
+
+extern unsigned char __dj_collate_table[];
+extern char __dj_date_format[];
+extern char __dj_time_format[];
+
+/*
+ * Reset collate table to the C locale
+ */
+static int
+resetlocalecollate(void)
+{
+  int i;
+
+  for (i = 0; i < 256; i++)
+    __dj_collate_table[i] = i;
+  return 1;
+}
+
+/*
+ * Reset ctype tables to the C locale
+ */
+static int
+resetlocalectype(void)
+{
+  int i;
+
+  /* ASCII portion always left unchanged */
+  for (i = 128; i < 256; i++)
+  {
+    __dj_ctype_tolower[i + 1] = i;
+    __dj_ctype_toupper[i + 1] = i;
+    __dj_ctype_flags[i + 1] = 0;
+  }
+  return 1;
+}
+
+/*
+ * Reset monetary to the C locale
+ * lcnv should be non-NULL
+ */
+static int
+resetlocalemonetary(void)
+{
+  int honored = 0;
+  struct lconv *lcnv = localeconv();
+
+  if(lcnv != NULL)
+  {
+    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; */
+    honored = 1;
+  }
+  return honored;
+}
+
+/*
+ * Reset numeric to the C locale
+ * lcnv should be non-NULL
+ */
+static int
+resetlocalenumeric(void)
+{
+  int honored = 0;
+  struct lconv *lcnv = localeconv();
+
+  if(lcnv != NULL)
+  {
+    strcpy(thousands_sep, "");
+    lcnv->thousands_sep = thousands_sep;
+    strcpy(decimal_point, ".");
+    lcnv->decimal_point = decimal_point;
+    /* lcnv->grouping = ""; */
+    honored = 1;
+  }
+  return honored;
+}
+
+/*
+ * Reset time strings to the C locale
+ */
+static int
+resetlocaletime(void)
+{
+  strcpy(__dj_date_format, "%m/%d/%y");
+  strcpy(__dj_time_format, "%H:%M:%S");
+  return 1;
+}
+
+/*
+ * Set collate table to the locale specifyed
+ * regs->x.bx = code page
+ * regs->x.dx = country ID
+ * regs->x.es/di = segment/offset
+ */
+static int
+setlocalecollate(const char *locale __attribute__((unused)), int selector,
+                 __dpmi_regs *regs)
+{
+  regs->h.ah = 0x65;
+  regs->h.al = 0x06;
+  regs->x.cx = 5;
+  __dpmi_int(0x21, regs);
+  if ((regs->x.flags & 1) || (regs->x.cx != 5))
+    return 0;
+  else
+  {
+    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);
+    return 1;
+  }
+}
+
+/*
+ * Set ctype table to the locale specifyed
+ * regs->x.bx = code page
+ * regs->x.dx = country ID
+ * regs->x.es/di = segment/offset
+ */
+static int
+setlocalectype(const char *locale __attribute__((unused)), int selector,
+               __dpmi_regs *regs)
+{
+  int temp_flags;
+  int i;
+
+  regs->h.ah = 0x65;
+  regs->h.al = 0x02;
+  regs->x.cx = 5;
+  __dpmi_int(0x21, regs);
+  if ((regs->x.flags & 1) || (regs->x.cx != 5))
+    return 0;
+  else
+  {
+    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++)
+    {
+      /*
+       * Actually isgraph(), ispunct() and isspace() will return wrong results
+       * for some letters like 0xff in CP866 but we can't detect them reliably
+       */
+      temp_flags = __dj_ISPRINT | __dj_ISGRAPH;
+      if (tolower(i) != toupper(i))
+      {
+        temp_flags |= __dj_ISALPHA | __dj_ISALNUM;
+        if (i == toupper(i))
+          temp_flags |= __dj_ISUPPER;
+        else
+          temp_flags |= __dj_ISLOWER;
+      }
+      else
+        temp_flags |= __dj_ISPUNCT;
+      __dj_ctype_flags[i + 1] = temp_flags;
+    }
+    return 1;
+  }
+}
+
+/*
+ * Set monitary values to the locale specifyed
+ */
+static int
+setlocalemonetary(const char *locale, int selector,
+                  __dpmi_regs *regs __attribute__((unused)))
+{
+  struct lconv *lcnv = localeconv();
+
+  if(lcnv == NULL)
+    return 0;
+  else
+  {
+    /* parse: de_DE_EURO.850 */
+    const char *p = strrchr(locale, '_');
+
+    if ((p != NULL) && !strnicmp(p + 1, "EURO", 4))
+      strcpy(currency_symbol, "EUR");
+    else
+      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; */
+    return 1;
+  }
+}
+
+/*
+ * Set numeric values to the locale specifyed
+ */
+static int
+setlocalenumeric(const char *locale __attribute__((unused)), int selector,
+                 __dpmi_regs *regs __attribute__((unused)))
+{
+  struct lconv *lcnv = localeconv();
+
+  if(lcnv == NULL)
+    return 0;
+  else
+  {
+    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 = ""; */
+    return 1;
+  }
+}
+
+/*
+ * Set time strings to the locale specifyed
+ */
+static int
+setlocaletime(const char *locale __attribute__((unused)), int selector,
+              __dpmi_regs *regs __attribute__((unused)))
+{
+  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);
+  return 1;
+}
+
+static const struct _cat
+{
+  int type;
+  const char *env;
+  int (*reset)(void);
+  int (*set)(const char *locale, int selector, __dpmi_regs *regs);
+}
+cat[LC_CATEGORIES] =
+{
+  { LC_COLLATE, "LC_COLLATE", resetlocalecollate, setlocalecollate },
+  { LC_CTYPE, "LC_CTYPE", resetlocalectype, setlocalectype },
+  { LC_MONETARY, "LC_MONETARY", resetlocalemonetary, setlocalemonetary },
+  { LC_NUMERIC, "LC_NUMERIC", resetlocalenumeric, setlocalenumeric },
+  { LC_TIME, "LC_TIME", resetlocaletime, setlocaletime }
+};
+
+static const struct _loc2id {
+  int id;
+  const char *loc;
+  int len;
+} loc2id[] = {
+  { 43,    "de_AT",       5 },
+  { 43,    "de_AT_EURO",  10 },
+  { 41,    "de_CH",       5 },
+  { 49,    "de_DE",       5 },
+  { 49,    "de_DE_EURO",  10 },
+  { 352,   "de_LU",       5 },
+  { 352,   "de_LU_EURO",  10 },
+  { 61,    "en_AU",       5 },
+  { 32,    "en_BE",       5 },
+  { 4,     "en_CA",       5 },
+  { 44,    "en_GB",       5 },
+  { 61,    "en_IE",       5 },
+  { 61,    "en_IE_EURO",  10 },
+  { 64,    "en_NZ",       5 },
+  { 1,     "en_US",       5 },
+  { 27,    "en_ZA",       5 },
+  { 54,    "es_AR",       5 },
+  { 591,   "es_BO",       5 },
+  { 56,    "es_CL",       5 },
+  { 57,    "es_CO",       5 },
+  { 506,   "es_CR",       5 },
+  { 809,   "es_DO",       5 },
+  { 593,   "es_EC",       5 },
+  { 34,    "es_ES",       5 },
+  { 34,    "es_ES_EURO",  10 },
+  { 502,   "es_GT",       5 },
+  { 504,   "es_HN",       5 },
+  { 52,    "es_MX",       5 },
+  { 507,   "es_PA",       5 },
+  { 51,    "es_PE",       5 },
+  { 595,   "es_PY",       5 },
+  { 503,   "es_SV",       5 },
+  { 598,   "es_UY",       5 },
+  { 58,    "es_VE",       5 },
+  { 358,   "fi_FI",       5 },
+  { 358,   "fi_FI_EURO",  10 },
+  { 32,    "fr_BE",       5 },
+  { 32,    "fr_BE_EURO",  10 },
+  { 2,     "fr_CA",       5 },
+  { 41,    "fr_CH",       5 },
+  { 352,   "fr_LU",       5 },
+  { 352,   "fr_LU_EURO",  10 },
+  { 33,    "fr_FR",       5 },
+  { 33,    "fr_FR_EURO",  10 },
+  { 30,    "gr_GR",       5 },
+  { 30,    "gr_GR_EURO",  10 },
+  { 972,   "he_IL",       5 },
+  { 385,   "hr_HR",       5 },
+  { 36,    "hu_HU",       5 },
+  { 354,   "is_IS",       5 },
+  { 41,    "it_CH",       5 },
+  { 39,    "it_IT",       5 },
+  { 39,    "it_IT_EURO",  10 },
+  { 370,   "lt_LT",       5 },
+  { 371,   "lv_LV",       5 },
+  { 389,   "mk_MK",       5 },
+  { 91,    "mr_IN",       5 },
+  { 356,   "mt_MT",       5 },
+  { 32,    "nl_BE",       5 },
+  { 32,    "nl_BE_EURO",  10 },
+  { 31,    "nl_NL",       5 },
+  { 31,    "nl_NL_EURO",  10 },
+  { 47,    "no_NO",       5 },
+  { 48,    "pl_PL",       5 },
+  { 55,    "pt_BR",       5 },
+  { 351,   "pt_PT",       5 },
+  { 351,   "pt_PT_EURO",  10 },
+  { 40,    "ro_RO",       5 },
+  { 7,     "ru_RU",       5 },
+  { 38,    "sh_YU",       5 },
+  { 42,    "sk_SK",       5 },
+  { 386,   "sl_SI",       5 },
+  { 355,   "sq_AL",       5 },
+  { 381,   "sr_YU",       5 },
+  { 358,   "sv_FI",       5 },
+  { 46,    "sv_SE",       5 },
+  { 91,    "ta_IN",       5 },
+  { 200,   "th_TH",       5 },
+  { 90,    "tr_TR",       5 },
+  { 804,   "uk_UA",       5 },
+  { 84,    "vi_VN",       5 }
+};
+
+/*
+ * Set or read international environment
+ */
+char *
+setlocale(int category, const char *locale)
+{
+  int honored = 1;
+  int i, j;
+
+  if (locale != NULL)
+  {
+    int segment = -1, selector = -1;
+    __dpmi_regs regs;
+    char buf[LC_MAXNAMESIZE];
+    char *p1, *p2;
+
+    strncpy(lc_buffer, locale, LC_BUFSIZ);
+    lc_buffer[LC_BUFSIZ - 1] = '\0';
+    p1 = lc_buffer - 1;
+    p2 = lc_buffer;
+    for (i = 0; i < LC_CATEGORIES; i++)
+    {
+      p1 = strchr (p1 + 1, ',');
+      if (p1 == NULL)
+      {
+        p1 = p2;
+      }
+      else
+      {
+        *p1 = '\0';
+        p2 = p1;
+      }
+      if ((category == LC_ALL) || (cat[i].type == category))
+      {
+        locale = p1;
+        if (locale[0] == '\0')
+        {
+          const char *env;
+          if ((env = getenv (cat[i].env)) != NULL)
+          {
+            locale = env;
+          }
+	  else
+          if ((env = getenv ("LC_ALL")) != NULL)
+          {
+            locale = env;
+          }
+	  else
+          if ((env = getenv ("LANG")) != NULL)
+          {
+            locale = env;
+          }
+        }
+        if ((stricmp(locale, "C") == 0) || (stricmp(locale, "POSIX") == 0))
+        {
+          if (cat[i].reset() == 0)
+	  {
+            honored = 0;
+	    continue;
+	  }
+        }
+        else
+        {
+          int CID, CCP;
+
+          /* Allocate DOS memory */
+          if (segment == -1)
+          {
+            if ((segment = __dpmi_allocate_dos_memory(3, &selector)) == -1)
+            {
+              honored = 0;
+              continue;
+            }
+          }
+
+          /* Now try to find out the country/codepage */
+          CID = 0xffff;
+          CCP = 0xffff;
+          if (locale[0] != '\0')
+          {
+            int len;
+            const char *p = strchr(locale, '.');
+            if (p == NULL)
+              p = locale + strlen(locale);
+            len = p - locale;
+            for (j = 0; j < sizeof(loc2id) / sizeof(struct _loc2id); j++)
+              if (!strncmp(locale, loc2id[j].loc,
+                           len >= loc2id[j].len ? len : loc2id[j].len))
+              {
+                CID = loc2id[j].id;
+                break;
+              }
+            if (*p == '.')
+              CCP = atoi(p + 1);
+            /* User requested the country/codepage we doesn't know about */
+            if ((CID == 0xffff) || (CCP == 0xffff))
+            {
+              honored = 0;
+              continue;
+            }
+          }
+
+          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))
+          {
+            honored = 0;
+            continue;
+          }
+
+          if (*locale == '\0') {
+            CID = _farpeekw(selector, 3);
+            CCP = _farpeekw(selector, 5);
+            locale = buf;
+            strcpy(buf, "??_??.");
+            for (j = 0; j < sizeof(loc2id) / sizeof(struct _loc2id); j++)
+              if (loc2id[j].id == CID)
+              {
+                strcpy(buf, loc2id[j].loc);
+                buf[loc2id[j].len] = '.';
+                break;
+              }
+            itoa(CCP, &buf[strlen(buf)], 10);
+          }
+
+          /* regs.x.bx, regs.x.dx, regs.x.es/di are preserved by DOS */
+          if (cat[i].set(locale, selector, &regs) == 0)
+          {
+            honored = 0;
+            continue;
+          }
+        }
+
+	strncpy(lc_current[i], locale, LC_MAXNAMESIZE);
+	lc_current[i][LC_MAXNAMESIZE - 1] = '\0';
+      }
+    }
+    if (segment != -1)
+      __dpmi_free_dos_memory(selector);
+  }
+
+  if (honored)
+  {
+    if (category != LC_ALL)
+    {
+      for (i = 0; i < LC_CATEGORIES; i++)
+        if (cat[i].type == category)
+          return lc_current[i];
+      return NULL;
+    }
+    if (!stricmp(lc_current[0], lc_current[1]) &&
+        !stricmp(lc_current[1], lc_current[2]) &&
+        !stricmp(lc_current[2], lc_current[3]) &&
+        !stricmp(lc_current[3], lc_current[4]))
+    {
+      return lc_current[0];
+    }
+    else
+    {
+      char *p;
+
+      p = lc_buffer;
+      for (i = 0; i < LC_CATEGORIES; i++)
+      {
+        p = stpcpy(p, lc_current[i]);
+        if (i != (LC_CATEGORIES - 1))
+          p = stpcpy(p, ",");
+      }
+      return lc_buffer;
+    }
+  }
+  else
+    return NULL;
+}
+
+#ifdef TEST
+#include <stdio.h>
+
+unsigned char __dj_collate_table[256];
+extern char __dj_date_format[10];
+extern char __dj_time_format[16];
+
+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);
+  return 0;
+}
+#endif

- Raw text -


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