[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[debian-users:13172] Bug#JP/794: Wchar incompatibility between glibc2 and Xlib



Package: wcsmbs-locale
Version: 0.4.1


○ Netscape のフォームとワイド文字

Netscape のフォーム内の一行入力欄 (Motif) で日本語の表示が化けるのは 
libc 側と Xlib 側でワイド文字の表現方法が違っているからです。現行の 
libc (glibc2.0.7 + wcsmbs-locale) の mbtowc()などでワイド文字を作ると、
その表現法は UCS (ユニコード) になりますが、Xlib では Xlib 独自の表現
法を使っているので、libc の関数を使って作ったワイド文字を Xlib のテキ
スト描画関数などに渡しても Xlib では適切に解釈することができません。

そこで私は Xlib が理解することのできる形式でワイド文字を作れる
wcsmbs.so ライブラリを作って使っています。


○ Debian 2.0 と X Window System

この X 用ライブラリを使うには、基本的にはこのレポートに添付のソースを
コンパイルしてインストールするだけでよいのですが、Debian2.0 の場合、X 
Window System が「X_LOCALE が定義された」状態でコンパイルされているの
で、このままだとうまく作動しません。

X_LOCALE が定義されていると、Xlib は setlocale() のかわりに 
_Xsetlocale() を使うようにコンパイルされてしまいます。何かのアプリケー
ションを実行させた場合を考えてみると、アプリケーションは setlocale() 
を使ってロケール設定をしているのに、Xlib 内の関数は、_Xsetlocale() を
使って現在のロケール設定を知ろうとするというような状況になります。
_Xsetlocale() と setlocale() とは何のつながりもないので (そもそも 
_Xsetlocale() は setlocale() を持っていないシステムのために用意されて
いるもので、両方存在するのは変です)、アプリケーションが setlocale() 
を使ってどんなロケール設定をしようと、_Xsetlocale() が持っているロケー
ル情報はつねにデフォルトのまま ("C" ロケール、つまり英語) になります。

つまり環境変数 "LANG" を "ja_JP.ujis" に設定しても、Xlib 側は英語のま
まのふるまいになってしまいます。例えば Netscape で日本語メニューを使
うリソース設定にしても半角文字しか表示されず、全角文字は X に無視され
てしまうのはこのためです。Netscape の場合、Debian の netscape4-ja パッ
ケージなどに入っている liblocale.so を使うと、このメニュー等の日本語
化の問題は解決しますが、この場合、ロケール設定が libc 側には反映され
なくなるので、libc 側のふるまいが英語になってしまいます (ワイド文字関
係のところはユニコードになります。liblocale.so 内の setlocale() が使
われるようになるので、適切な wcsmbs.so がロードされないからです)。

しょうがないので、私は X_LOCALE をはずした状態で、X をコンパイルしな
おして使用しています。


○ Version など

Debian:
   version 2.0
   
wcsmbs-locale:
   version 0.4.1
   
libc6:
   version 2.0.7t-1.wcsmbs.4.1

xlib6g:
   version 3.3.2.3-1
   
   前述の通り、X_LOCALE が定義された状態でコンパイルされているので、こ
   の定義をはずして自力コンパイルしたものを使っています。
   
netscape:
   version 4.07
   
   RedHat 5.2 に入っていたものを 'alien' コマンドで .deb 化して、
   Debian 上で使っています。netscape で wcsmbs.so の作動をチェックす
   るには、環境変数 LANG を "ja_JP.ujis" にするだけで、あとは英語(デ
   フォルト)設定のままでよいと思います。
   
   ちなみにフォームの一行入力欄の初期値が表示されないとか(リセットボ
   タンがあれば、それを押せば表示されますけど)、ファイルリクエスタの
   挙動がおかしいとか、その辺の問題は wcsmbs.so を変えても解決しませ
   ん。


○ X 用 wcsmbs.so のソースコード (EUC 用)

(コンパイルするには X のソースが必要です。)

/*
 * wcsmbs-xeuc.c
 * a wcsmbs.so library for Linux (glibc 2.0.7, wcsmbs-locale 0.4.1
 * and X11R6.3)
 *
 * written by Narumi Watanabe, 1999/02/01
 * 
 * COMPILE:
 * gcc -fPIC -shared -Wl,-soname,wcsmbs.so -o wcsmbs-x.so \
 *  wcsmbs-xeuc.c -I$X11SRC/lib/X11 -L/usr/X11R6/lib -lX11
 *                  ^^^^^^^ 
 * INSTALL:
 * cp wcsmbs-x.so /usr/lib/wcsmbs/ja_JP.ujis/wcsmbs-x.so
 * cd /usr/lib/wcsmbs/ja_JP.ujis
 * mv wcsmbs.so wcsmbs-ucs.so
 * ln -s wcsmbs-x.so wcsmbs.so
 */

#include <stdio.h>
#include <wchar.h>

#include "Xlibint.h"
#include "XlcGeneric.h"

#define CLR_MSB(c)  ((c) & 0x7f)
#define SET_MSB(c)  ((c) | 0x80)
#define IS_MSB(c)   ((c) & 0x80)

static unsigned char ShiftCode[4];
#define SS2  ShiftCode[2]
#define SS3  ShiftCode[3]

static XLCd get_lcd(void)
{
   static XLCd lcd = NULL;
   
   if (lcd == NULL) {
      if (lcd = _XOpenLC(NULL)) {
         if (_XlcNCompareISOLatin1(XLC_PUBLIC_PART(lcd)->codeset, "euc", 3)) {
            _XCloseLC(lcd);
            lcd = NULL;
         }
	 else if (XLC_GENERIC(lcd, codeset_num) < 1 || (XLC_GENERIC_PART(lcd)->codeset_list[0])->length != 1) {
	    _XCloseLC(lcd);
	    lcd = NULL;
	 }
      }
      
      if (lcd) {
	 CodeSet codeset;
	 
         if (XLC_GENERIC(lcd, codeset_num) >= 3) {
            codeset = XLC_GENERIC_PART(lcd)->codeset_list[2];
            if (codeset->parse_info && codeset->parse_info->encoding)
               SS2 = *codeset->parse_info->encoding;
         }
         if (XLC_GENERIC(lcd, codeset_num) >= 4) {
            codeset = XLC_GENERIC_PART(lcd)->codeset_list[3];
            if (codeset->parse_info && codeset->parse_info->encoding)
               SS3 = *codeset->parse_info->encoding;
         }
      }   
#ifdef DEBUG
      if (lcd == NULL)
	 printf("can't open lcd.\n");
      else {
	 int i;
	 char *msg;
	 int enc;
	 CodeSet codeset;
	 
	 for (i = 0; i < XLC_GENERIC(lcd, codeset_num); ++i) {
	    codeset = XLC_GENERIC_PART(lcd)->codeset_list[i];
	    msg = "";
	    enc = 0;
	    if (codeset->parse_info == NULL)
	       msg = "(no parse info)";
	    else if (codeset->parse_info->encoding == NULL)
	       msg = "(no encoding)";
	    else
	       enc = *(unsigned char *)codeset->parse_info->encoding;
	    printf("-cs%d: %02x %s\n", i, enc, msg);
	 }
	 printf("-mb_cur_max=%d\n", XLC_PUBLIC(lcd, mb_cur_max));
      }
#endif /* DEBUG */
   }
   
   return(lcd);
}

static int wc_codeset_no(XLCd lcd, wchar_t wc)
{
   int i;
   
   wc &= XLC_GENERIC(lcd, wc_encode_mask);
   
   for (i = 0; i < XLC_GENERIC(lcd, codeset_num); ++i)
      if (wc == (XLC_GENERIC_PART(lcd)->codeset_list[i])->wc_encoding)
	 return(i);
   
   return(-1);
}

#ifdef DEBUG
void show_str(const char *s, int len)
{
   int i;
   char buf[32];
   
   if (s == NULL)
      len = 0;
   if (len >= sizeof(buf))
      len = sizeof(buf) - 2;
   
   for (i = 0; i < len; ++i) {
      buf[i] = s[i];
      if (buf[i] & 0x80) {
	 ++i;
	 buf[i] = s[i];
      }
   }
   buf[i] = 0;
   
   printf("\"%s\"", buf);
}
#endif /* DEBUG */	
    
size_t dll_mbrtowc_locale(wchar_t *pwc, const char *s, size_t slen, mbstate_t *ps)
{
   XLCd lcd;
   wchar_t wc;
   unsigned char ch;
   
   if ((lcd = get_lcd()) == NULL)
      return((size_t)-1);
   
#ifdef DEBUG
   printf("-mbrtowc: %p, %p, %d\n\t<- ", pwc, s, slen);
   if (s)
      show_str(s, slen);
   printf("\n");
#endif

   if (s == NULL)
      return(XLC_PUBLIC(lcd, is_state_depend));
   if (*s == '\0') {
      if (pwc)
	 *pwc = 0;
      return(0);
   }
   if (slen <= 0)
      return((size_t)-1); /* ??? */
   
   ch = *(unsigned char *)s;
   if (!IS_MSB(ch)) {
      wc = ch | (XLC_GENERIC_PART(lcd)->codeset_list[0])->wc_encoding;
      slen = 1;
   }
   else {
      CodeSet codeset;
      int len, wc_shift;
      
      if (ch < 0xa0) {
         if (ch == SS2) {
	    codeset = XLC_GENERIC_PART(lcd)->codeset_list[2];
	    len = codeset->length;
	    if (slen < 1 + len)
	       return((size_t)-1);
	    slen = 1 + len;
	    ++s;
	 }
         else if (ch == SS3) {
	    codeset = XLC_GENERIC_PART(lcd)->codeset_list[3];
	    len = codeset->length;
	    if (slen < 1 + len)
	       return((size_t)-1);
	    slen = 1 + len;
	    ++s;
	 }
	 else
	    return((size_t)-1);
      }
      else if (XLC_GENERIC(lcd, codeset_num) >= 2) {
	 codeset = XLC_GENERIC_PART(lcd)->codeset_list[1];
	 len = codeset->length;
	 if (slen < len)
	    return((size_t)-1);
	 slen = len;
      }
      
      wc_shift = XLC_GENERIC(lcd, wc_shift_bits);
      wc = 0;
      for ( ; --len >= 0; ) {
	 if ((ch = *(unsigned char *)s++) < 0xa0)
	    return((size_t)-1);
	 wc = (wc << wc_shift) | CLR_MSB(ch);
      }
      wc |= codeset->wc_encoding;
   }
   
   if (pwc)
      *pwc = wc;

#ifdef DEBUG
   printf("\t-> wc=%08x, len=%d\n", wc, slen);
#endif
   return(slen);
}

size_t dll_mbsnrtowcs_locale(wchar_t *dst, const char **src, size_t srclen, size_t dstlen, mbstate_t *ps)
{
   XLCd lcd;
   unsigned char *s, ch;
   int wc_shift, wc_encoding_cs0;
   size_t nwc;
   wchar_t wc;
   
   if ((lcd = get_lcd()) == NULL)
      return((size_t)-1);
   
#ifdef DEBUG
   printf("-mbsnrtowcs: %p, %p, %d, %d\n\t<- ", dst, *src, srclen, dstlen);
   if (s = *(unsigned char **)src)
      show_str(s, srclen);
   printf("\n");
#endif
   
   wc_shift = XLC_GENERIC(lcd, wc_shift_bits);
   wc_encoding_cs0 = (XLC_GENERIC_PART(lcd)->codeset_list[0])->wc_encoding;
   s = (unsigned char *)*src;
   if (dstlen == 0)
      dst = NULL;
   nwc = 0;
   
   for ( ; srclen > 0 && (ch = *s) != '\0' ; ) {
      if (!IS_MSB(ch)) {
	 wc = ch | wc_encoding_cs0;
	 --srclen;
	 ++s;
      }
      else {
	 CodeSet codeset;
	 int len;
	 
	 if (ch < 0xa0) {
	    if (ch == SS2) {
	       codeset = XLC_GENERIC_PART(lcd)->codeset_list[2];
	       len = codeset->length;
	       if ((size_t)(1 + len) > srclen)
		  return((size_t)-1);
	       --srclen;
	       ++s;
	    }
	    else if (ch == SS3) {
	       codeset = XLC_GENERIC_PART(lcd)->codeset_list[3];
	       len = codeset->length;
	       if ((size_t)(1 + len) > srclen)
		  return((size_t)-1);
	       --srclen;
	       ++s;
	    }
	    else
	       return((size_t)-1);
	 }
	 else if (XLC_GENERIC(lcd, codeset_num) >= 2) {
	    codeset = XLC_GENERIC_PART(lcd)->codeset_list[1];
	    len = codeset->length;
	    if ((size_t)len > srclen)
	       rerutn((size_t)-1);
	 }
	 else
	    return((size_t)-1);
	 
	 srclen -= len;
	 wc = 0;
	 for ( ; --len >= 0; ) {
	    if ((ch = *s++) < 0xa0)
	       return((size_t)-1);
	    wc = (wc << wc_shift) | CLR_MSB(ch);
	 }
	 wc |= codeset->wc_encoding;
      }
      
      ++nwc;
      if (dst) {
	 *dst++ = wc;
	 if (nwc >= dstlen)
	    break;
      }
   }
  
   if (dst != NULL && nwc < dstlen)
      *dst = 0;
   
   *src = (srclen > 0 || *s == '\0') ? NULL : (char *)s;
   return(nwc);
}

size_t dll_mbsrtowcs_locale(wchar_t *dst, const char **src, size_t dstlen, mbstate_t *ps)
{
   return(dll_mbsnrtowcs_locale(dst, src, (size_t)~0, dstlen, ps));
}

size_t dll_wcrtomb_locale(char *d, wchar_t wc, mbstate_t *ps)
{
   XLCd lcd;
   int dlen = 0;
   CodeSet codeset;
   int i, wc_shift;
#ifdef DEBUG
   void *dtop = d;
#endif

   if ((lcd = get_lcd()) == NULL)
      return(-1);

#ifdef DEBUG
   printf("-wcrtomb: %p, %08x\n", d, wc);
#endif
   
   if (d == NULL)
      return(XLC_PUBLIC(lcd, is_state_depend));
   if (wc == 0) {
      *d = '\0';
      return(0);
   }
   
   if ((i = wc_codeset_no(lcd, wc)) < 0)
      return(-1);
   codeset = XLC_GENERIC_PART(lcd)->codeset_list[i];
   
   if (ShiftCode[i]) { /* SS2 or SS3 */
      *d++ = ShiftCode[i];
      ++dlen;
   }
   
   wc_shift = XLC_GENERIC(lcd, wc_shift_bits);
   i = codeset->length;
   d += i;
   dlen += i;
   for ( ; --i >= 0; ) {
      *(--d) = (codeset->side == XlcGR) ? SET_MSB(wc) : CLR_MSB(wc);
      wc >>= wc_shift;
   }

#ifdef DEBUG
   printf("\t-> ");
   show_str(dtop, dlen);
   printf("\n");
#endif
   
   return(dlen);
}

int dll_wcsnrtombs_locale(char *dst, const wchar_t **src, size_t srclen, size_t dstlen, mbstate_t *ps)
{
   XLCd lcd;
   wchar_t wc, *ws;
   int wc_shift;
   size_t dpos;
   
   if ((lcd = get_lcd()) == NULL)
      return((size_t)-1);

#ifdef DEBUG
   printf("-wcsnrtombs: %p, %p, %d, %d\n", dst, *src, srclen, dstlen);
#endif
   
   wc_shift = XLC_GENERIC(lcd, wc_shift_bits);
   ws = *(wchar_t **)src;
   if (dstlen == 0)
      dst = NULL;
   dpos = 0;
   
   for ( ; (srclen > 0) && (wc = *ws); --srclen, ++ws) {
      int i;
      CodeSet codeset;
      
      if ((i = wc_codeset_no(lcd, wc)) < 0)
	 return((size_t)-1);
      codeset = XLC_GENERIC_PART(lcd)->codeset_list[i];      
      
      if (dst == NULL) {
	 if (ShiftCode[i]) /* SS2 or SS3 */
	    ++dpos;
	 dpos += codeset->length;
      }
      else {
	 int len = codeset->length;
	 unsigned char *ptr;
	 
	 if (ShiftCode[i]) {
	    if (dpos + 1 + len > dstlen)
	       break;
	    dst[dpos++] = ShiftCode[i];
	 }
	 else {
	    if (dpos + len > dstlen)
	       break;
	 }
	 dpos += len;
	 
	 ptr = dst + dpos;
      	 for ( ; --len >= 0; ) {
	    *(--ptr) = (codeset->side == XlcGR) ? SET_MSB(wc) : CLR_MSB(wc);
	    wc >>= wc_shift;
	 }
      }
   }
   
   if (dst != NULL && dpos < dstlen)
      dst[dpos] = '\0';
     
#ifdef DEBUG
   printf("\t-> ");
   if (dst)
      show_str(dst, dpos);
   printf("\n");
#endif
   
   *src = (srclen == 0 || *ws == 0) ? NULL : (wchar_t *)ws;
   return(dpos);
}

int dll_wcsrtombs_locale(char *dst, const wchar_t **src, size_t dstlen, mbstate_t *ps)
{
   return(dll_wcsnrtombs_locale(dst, src, (size_t)~0, dstlen, ps));
}

int dll_ctype_get_mb_cur_max(void)
{
   XLCd lcd;
   
   if ((lcd = get_lcd()) == NULL)
      return(1);
   return(XLC_PUBLIC(lcd, mb_cur_max));
}

(ソースコードおわり)

------------------------------------
渡辺成身 (watanaru@xxxxxxxxxxxxxxxx)