XSetICValues()はエラーが無い場合にもNULLでない値を返す。 - 症状 仕様ではXSetICValues()はエラーが無い場合NULLを返し、エラーが発生した場 合は「設定できなかった最初の引数の名前」を返すことになっている。 The XSetICValues function returns NULL if no error occurred; otherwise, it returns the name of the first argument that could not be set. しかし、次の2つの問題がある。 1. コールバックなどの "inner resource" を設定するときは実際にXIMサーバ と通信が行われないことがある。このとき、XSetICValues()は成功しても最初 の引数の名前を返し、NULLを返さない。 2. 生成時にしか設定できないリソースや読み出し専用のリソースを設定した とき、「設定できなかった最初の引数の名前」ではなく「最初の引数の名前」 を返す。 xc/lib/X11/imDefIc.cの関数_XimProtoSetICValues()では、tmp_nameに最初の 引数の名前(引数がなければNULL)を代入する(687行目)。関数 _XimEncodeICATTRIBUTE()がNULLを返さない場合、返り値は設定できなかった 引数の名前であり、その名前がnameに代入されてforループを抜ける(721, 725行目)。関数_XimEncodeICATTRIBUTE()がNULLを返す場合、rel_lenはエンコー ドされたデータサイズを表し、totalは総データサイズを保持する(728行目)。 そしてarg_retがNULLの場合はforループを抜ける(730行目)。 forループを抜けた後、totalが0の場合はtmp_nameを返す(751行目)。しかし、 エラーが原因で(725行目から)ループを抜けた場合は、tmp_nameは「設定でき なかった最初の引数の名前」ではなく「最初の引数の名前」を表す。また、正 常に(730行目から)ループを抜けた場合でも、"inner resource" を設定したと きなどはtotalは0であるので、同じく「最初の引数の名前」を返してしまう。 661 Private char * 662 _XimProtoSetICValues(xic, arg) 663 XIC xic; 664 XIMArg *arg; 665 { ... 686 char *name; 687 char *tmp_name = (arg) ? arg->name : NULL; ... 717 total = 0; ... 719 for (;;) { 720 data = &buf[buf_size]; 721 if ((name = _XimEncodeICATTRIBUTE(ic, ic->private.proto.ic_resources, 722 ic->private.proto.ic_num_resources, arg, &arg_ret, 723 data, data_len, &ret_len, (XPointer)&ic_values, 724 &flag, XIM_SETICVALUES))) { 725 break; 726 } 727 728 total += ret_len; 729 if (!(arg = arg_ret)) { 730 break; 731 } ... 747 } ... 750 if (!total) { 751 return tmp_name; 752 } - 再現 % cat Imakefile DEPLIBS = $(DEPXONLYLIB) LOCAL_LIBRARIES = $(XONLYLIB) SRCS = xseticvalues.c OBJS = $(SRCS:.c=.o) ComplexProgramTarget(xseticvalues) % cat xseticvalues.c #include #include #include #include #include #define FONTSET "-*-fixed-medium-r-normal--*-*-*-*-*-*-*-*" #define XICCallback XIMCallback #define XICProc XIMProc int main(void) { Display *dpy; Window w; XIM im; XIMStyle style; XIMStyles *supported; XIC ic; XVaNestedList preedit, status, list; XICCallback null; XFontSet fs; int n_missing_sets; char **missing_set, *alternate_font; XPoint Origin = {0, 0}; char *s; if ((dpy = XOpenDisplay("")) == NULL) { errx(1, "cannot open display."); } if (setlocale(LC_ALL, "") == NULL) { errx(1, "cannot set locale."); } if (XSupportsLocale() == False) { errx(1, "locale not supported."); } if (XSetLocaleModifiers("") == NULL) { errx(1, "cannot set locale modifiers."); } if ((fs = XCreateFontSet(dpy, FONTSET, &missing_set, &n_missing_sets, &alternate_font)) == NULL) { errx(1, "cannot create font set."); } if ((im = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) { errx(1, "cannot open input method."); } if (XGetIMValues(im, XNQueryInputStyle, &supported, NULL) != NULL) { errx(1, "XNQueryInputStyle not supported."); } if (supported->count_styles < 1) { errx(1, "no style available."); } style = supported->supported_styles[0]; printf("style: 0x%x\n", (unsigned int)style); XFree(supported); null.client_data = NULL; null.callback = (XICProc)NULL; preedit = XVaCreateNestedList(0, XNFontSet, fs, XNSpotLocation, &Origin, XNPreeditStartCallback, &null, XNPreeditDoneCallback, &null, XNPreeditDrawCallback, &null, XNPreeditCaretCallback, &null, NULL); status = XVaCreateNestedList(0, XNFontSet, fs, XNStatusStartCallback, &null, XNStatusDoneCallback, &null, XNStatusDrawCallback, &null, NULL); w = DefaultRootWindow(dpy); if ((ic = XCreateIC(im, XNInputStyle, style, XNClientWindow, w, XNPreeditAttributes, preedit, XNStatusAttributes, status, NULL)) == NULL) { errx(1, "cannot create input context."); } printf("test 1: XSetICValues() should return NULL.\n"); if ((s = XSetICValues(ic, XNPreeditAttributes, preedit, NULL)) != NULL) { printf("XSetICValues() failed. (%s)\n", s); } printf("%s\n", (s == NULL) ? "ok" : "ng"); printf("test 2: XSetICValues() should fail and return \"inputStyle\".\n"); if ((s = XSetICValues(ic, XNPreeditAttributes, preedit, XNInputStyle, style, /* <- ERROR */ NULL)) != NULL) { /* s should be "inputStyle". */ printf("XSetICValues() failed. (%s)\n", s); } printf("%s\n", (s != NULL && strcmp(s, XNInputStyle) == 0) ? "ok" : "ng"); printf("test 3: XSetICValues() should return NULL.\n"); if ((s = XSetICValues(ic, /* EMPTY */ NULL)) != NULL) { printf("XSetICValues() failed. (%s)\n", s); } printf("%s\n", (s == NULL) ? "ok" : "ng"); printf("test 4: XSetICValues() should return NULL.\n"); list = XVaCreateNestedList(0, XNPreeditStateNotifyCallback, &null, NULL); if ((s = XSetICValues(ic, XNPreeditAttributes, list, NULL)) != NULL) { printf("XSetICValues() failed. (%s)\n", s); } printf("%s\n", (s == NULL) ? "ok" : "ng"); XFree(preedit); XFree(status); XFree(list); exit(0); return 0; } % xmkmf -a ; make ... % ./xseticvalues style: 0x102 test 1: XSetICValues() should return NULL. XSetICValues() failed. (preeditAttributes) ng test 2: XSetICValues() should fail and return "inputStyle". XSetICValues() failed. (preeditAttributes) ng test 3: XSetICValues() should return NULL. ok test 4: XSetICValues() should return NULL. XSetICValues() failed. (preeditAttributes) ng 注: "test 1" の結果はsupported->supported_styles[0]の値によっては、 XlibとXIMサーバで通信が発生するため、okになることもある。例えばXIMサー バが0x104(XIMPreeditPosition|XIMStatusArea)を最初のスタイルとして返す 場合がこれに当てはまる。 上記4つのテストすべてがokになる必要がある。 - 確認できるリリース R6.6 (XFree86-4.2.1) - 修正されているリリース なし - 回避策 XIMサーバとの通信が発生する引数としない引数を一緒にして使用しない。通 信が発生しない引数を使用する場合は、戻り値がNULLでなくても成功したとみ なす。 - 修正方法 R6.6に次のパッチを適用する。 diff -c -r xc.orig/lib/X11/imDefIc.c xc/lib/X11/imDefIc.c *** xc.orig/lib/X11/imDefIc.c Mon Apr 28 03:47:27 2003 --- xc/lib/X11/imDefIc.c Sun May 11 05:39:07 2003 *************** *** 1,4 **** --- 1,5 ---- /* $Xorg: imDefIc.c,v 1.5 2000/08/17 19:45:11 cpqbld Exp $ */ + /* SAMPLE FIX: 2003/05/11 */ /****************************************************************** Copyright 1991, 1992 by Sun Microsystems, Inc. *************** *** 748,754 **** _XimSetCurrentICValues(ic, &ic_values); if (!total) { ! return tmp_name; } buf_s = (CARD16 *)&buf[XIM_HEADER_SIZE]; --- 749,755 ---- _XimSetCurrentICValues(ic, &ic_values); if (!total) { ! return name; } buf_s = (CARD16 *)&buf[XIM_HEADER_SIZE];