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 Content-Type: text/plain; charset="us-ascii" 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 */ #include #include #include #include #include #include #include #include #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, ®s); 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, ®s); 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, ®s); 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 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/