蝉は、やがて死ぬる午後に気づいた。ああ、私たち、もっと仕合せになってよかったのだ。:2007年06月分

2007/06/01(Fri)

LC_TIME

glibcのERAの{start,end}_dateの年部およびoffsetはint32_tで定義されてるもより。
int64なtime_tであれば西暦2900億年まで扱える訳で
これだと8bit足りずに西暦2147483647年(0x7FFFFFFF)問題発生の予感。

元々struct tmのtm_yearがintなので32bitマシンの場合
西暦2147485547年(0x7FFFFFFF+1900)問題というのがあるんだが
それより1900年も早く発生しますな。

ってどうでもいいやwwwww実用上なんの問題もないでしょ。
そのうちDr.StrangeloveがDoomsday machineで解決してくれるだろう。

[NetBSD] 性能の差が決定的な差でないことを教えてや(以下ry

ちょっと古いネタ。 http://blog.mon.itor.us/?p=286

こうですか?わかりません ><

  \25,000
  ___  \49,800
  |      |\ .__
  |      | | | ||
  |      | | | ||
  |      | | | ||   グラフで比較するとそれほど差はない
  |      | | | ||   むしろWiiのほうが高く感じられる
  |      | | | ||
  |      | | | ||
  |      | | | ||
  |      | | | ||
  |      | | |_||PS3
  |      | |//
  |      | | /
  |      | | /
  |      | |/
  |      | ./
  |___|/Wii
/     /

2007/06/03(Sun)

[OpenBSD] Citrus patch

OpenBSD 4.1-current(20070603)向けを いつもの場所に置きました。

encoding moduleをdymamic loadingせずにlibcにbuiltinするoptionは廃止しました。
そのうちDragonFlyBSDでのsys/linker_set.hを使う方法に改めて復活するかも。

MB_LEN_MAXを1 -> 32に変更した結果、binary互換に問題があります。
対策としてrename.patchを同梱しました。
ただしmultibyte localeを使用可能にするにはどっちみちXやportsなどは
再コンパイルが必須になりますのでご注意を。

Xを再コンパイルする場合-DX_LOCALEフラグは不要です、ついでに

Index: OpenBSD.cf
===================================================================
RCS file: /home/cvs/xorg/xorg/xc/config/cf/OpenBSD.cf,v
retrieving revision 1.6
diff -u -r1.6 OpenBSD.cf
--- OpenBSD.cf	29 Aug 2004 22:02:35 -0000	1.6
+++ OpenBSD.cf	24 Nov 2005 02:49:34 -0000
@@ -211,7 +211,7 @@
 
 
 #define StandardDefines	-DCSRG_BASED
-#define XawI18nDefines	-DUSE_XWCHAR_STRING -DUSE_XMBTOWC
+#define XawI18nDefines	-DHAS_WCHAR_H -DHAS_WCTYPE_H -DHAS_ISW_FUNCS -DNO_WIDEC_H
 
 
 #define	AdmDir			/var/log

としておくといいでしょう。

2007/06/05(Tue)

[Linux] 64bit time_tでも不足するケース

linux-2.6.18/fs/cifs/cifs_unicode.h

 *   Copyright (c) International Business Machines  Corp., 2000,2005555555555555555555555555555555555555555555555555555555

あるあるwwwww vi wwwww

西暦 20阿僧祇555恒河沙 年か、うーん出光(年がばれますな

[DragonFlyBSD] Citrus patch

最新Citrusに追従するpatch作ってみたがこんなん需要あんのかな。
いちおう ここに転がしておく。
これもMB_LEN_MAX 6 -> 32に増やした関係上バイナリ互換が崩れるので
対策にrename.patchを同梱しときました。

それとOpenBSD向けのpatchもrename.patchを入れ忘れてたので 更新しときました、ごめんちゃい。

[DragonFlyBSD] bsd.patch.mk

SRCS+=	foo.c.patch

と書くとfoo.cにfoo.c.patchを適用するsuffix ruleがあるのね。
contribにある3rd partyなソースのバージョンを上げる場合
cvs update -jを使わずに済むとか多分そんな理由なんだろか。

なんとなく土足厳禁の車を思い出した。

2007/06/06(Wed)

[Unicode] リガチャ

もしかしてUnicodeの中ではリガチャ(合字)の最も長いものは
U+FDFA(ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM)なのかな。 ORE U+FDFA = U+0635 + U+0644 + U+0649 \ + U+0020 + U+0627 + U+0644 + U+0644 + U+0647 \ + U+0020 + U+0639 + U+0644 + U+064A + U+0647 \ + U+0020 + U+0648 + U+0633 + U+0644 + U+0645

[NetBSD] iconv

こないだのCP{864,1046} -> ISO-8858-6への変換の話は、結局のところ
「Unicode正規化を実装すべし」というところに行き着くわけだけど
これどう実装するかだよなぁ。

mapper.dirが

UCS/FOO		mapper_std	FOO/UCS%FOO.mps

となってるのを

# Unicode Normarization Form Composition/Decomposition
UCS/UCS:NFCD	mapper_many2many	UCS/UCS%UCS@NFCD.mps

UCSx/FOO	mapper_std	FOO/UCS%FOO.mps
UCS:NFCD/FOO	mapper_serial	UCS/UCS:NFCD,UCSx/FOO
UCS/FOO		mapper_parallel	UCSx/FOO,UCS:NFCD/FOO

と全面的に書き換えてまわるのはちと嫌なんで、UCS/FOO(=UCS%FOO.mps)が
それぞれ必要なだけ正規化についての変換情報を持つんだろうな。
うーん /usr/share/i18n/csmapper 以下が更に太りそうだ。

まあM:N変換モジュールはJISX0213の丸付き数字のように
FOO/UCSでも必要になるのでまずはそこへんからだろうな。

きのう

「パリ、テキサス」のデジタルニューマスター版見ながら寝た。

「パリで一泊するんだ───川崎のね」

という地域限定な終電直前ネタはどれぐらいの人が判るんだろうか。

って6月中旬以降はそういう生活に逆戻りだおwwwwwww

2007/06/07(Thu)

[GNU libiconv] TRANSLIT

$ echo "\\u3231" | /usr/pkg/bin/iconv -f C99 -t EUC-JP//TRANSLIT
(株)

へぇ。

では昨日のアラビア文字のリガチャは?

$ echo "\\uFDFA" | /usr/pkg/bin/iconv -f C99 -t ISO-8859-6//TRANSLIT
/usr/pkg/bin/iconv: (stdin):1:0: cannot convert

Unicode5.0の正規化を完全にサポートしてる訳ではないみたい。

[GCC] C++0x

http://gcc.gnu.org/gcc-4.3/cxx0x_status.html
ふーんC++0x対応ハジマタのね。

まだlibstdc++には N2249とか N2238のuchar.h(char{16,32}_tとかmbrtoc{16,32}/c{16,32}rtomb)関連の
実装はないみたい *1
まあそもそもこいつはlibc側で ISO/IEC TR19769対応することが前提だから当たり前なんだろけど。

ちなみにc{16,32}rtomb(s, c, ps)のsには最低MB_CUR_MAXを保障せいと書いてあるので
ここではUnicode正規化は行われない、つか無理。
codecvtとかはどうなんだろね。

*1:ada binding周りにちょっとgrep引っかかるけど。

[C言語] ISO/IEC TR19769

c16rtomb()に上位サロゲート食わせた時の挙動。

UTF-7とかのごく一部のCESでもない限り、下位サロゲートが判らん限りmultibyteには
変換できないんだけども、restartする為の戻り値、(size_t)-2が定義されてないので
一瞬仕様バグかと思ったけど、これは(size_t)0を返すことでrestartになるっつーことだな。

適当に書いた実証用コード。

#include <errno.h>
#include <locale.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

typedef uint16_t	char16_t;

typedef struct {
	char16_t c16; /* for surrogate-pair */
	mbstate_t st;
} c16_mbstate_t;

size_t
mbrtoc16(char16_t *pc16, const char *s, size_t n, c16_mbstate_t *ps) {
	static c16_mbstate_t mbrtoc16_internal_state = { 0 };
	wchar_t wc;
	size_t ret;

	if (ps == NULL)
		ps = &mbrtoc16_internal_state;
	if (ps->c16 != 0) {
		if (ps->c16 >= 0xDC00 && ps->c16 <= 0xDFFF) {
			if (pc16 != NULL)
				*pc16 = ps->c16;
			ps->c16 = 0;
			return (size_t)-3;
		}
		errno = EILSEQ;
		return (size_t)-1;
	}
	/* XXX: assume wchar_t as UCS4 */
	ret = mbrtowc(&wc, s, n, &ps->st);
	switch (ret) {
	case (size_t)-1: case (size_t)-2:
		break;
	default:
		if (wc <= 0xFFFF) {
			if (pc16 != NULL)
				*pc16 = (char16_t)wc;
			break;
		}
		if (wc <= 0x10FFFF) {
			wc -= 0x100000;
			if (pc16 != NULL)
				*pc16 = (char16_t)((wc >> 10) + 0xD800);
			ps->c16 = (wc & 0x3FF) + 0xDC00;
			break;
		}
		errno = EILSEQ;
		ret = (size_t)-1;
	}
	return ret;
}

size_t
c16rtomb(char *s, char16_t c16, c16_mbstate_t *ps)
{
	static c16_mbstate_t c16rtomb_internal_state = { 0 };
	wchar_t wc;

	if (ps == NULL)
		ps = &c16rtomb_internal_state;
	if (ps->c16 == 0) {
		if (c16 >= 0xD800 && c16 <= 0xDBFF) {
			ps->c16 = c16;
			return 0;
		} else if (c16 >= 0xDC00 && c16 <= 0xDFFF) {
			errno = EILSEQ;
			return (size_t)-1;
		}
		wc = (wchar_t)c16;
	} else if (ps->c16 >= 0xD800 && ps->c16 <= 0xDBFF &&
	    c16 >= 0xDC00 && c16 <= 0xDFFF) {
		wc = (ps->c16 - 0xD800) << 10;
		wc |= c16 - 0xDC00;
		wc += 0x100000;
		ps->c16 = 0;
	} else {
		errno = EILSEQ;
		return (size_t)-1;
	}
	/* XXX: assume wchar_t as UCS4 */
	return wcrtomb(s, wc, &ps->st);
}

int
test_mbrtoc16(void)
{
	c16_mbstate_t st;
	size_t ret;
	char *s = "\xF4\x80\x80\x80";
	size_t n = strlen(s);
	char16_t c16;

	memset(&st, 0, sizeof(st));
	for (;;) {
		ret = mbrtoc16(&c16, s, 1, &st);
		switch (ret) {
		case (size_t)-1:
			abort();
		case (size_t)-2:
			++s;
			break;
		default:
			printf("U+%X\n", c16);
			if (ret == (size_t)0)
				return;
			if (ret != (size_t)-3)
				s += ret;
		}
	}
}

int
test_c16rtomb(void)
{
	c16_mbstate_t st;
	size_t ret;
	char16_t str16[] = { 0xD800, 0xDC00, 0x0 }, *pstr16 = &str16[0];
	char s[MB_LEN_MAX];

	memset(&st, 0, sizeof(st));
	for (;;) {
		ret = c16rtomb(s, *pstr16, &st);
		if (ret == (size_t)-1)
			abort();
		if (ret != (size_t)0) {
			printf("%.*s", ret, s);
			if (*pstr16 == 0)
				break;
		}
		++pstr16;
	}
	return 0;
}

int
main(void)
{
	setlocale(LC_CTYPE, "en_US.UTF-8");
	test_mbrtoc16();
	test_c16rtomb();

	return 0;
}

2007/06/12(Tue)

[i18n] ja_JP.eucJP他


沼田さん *1のところでいろいろと資料が復活してたのでメモっとく。

*1:SC22/POSIXのとてもえらいひと

2007/06/13(Wed)

[NetBSD] Citrus iconv

を書いた、週末までにはcommitすんべ。

ちなみにCP874/CP1162の違いは

ibm874 ≠ windows-874
ibm1162 = windows-874

つーベンダ間でのgdgdが原因なので

874	cp874 ibm874
1162	cp1162 ibm1162 mscp874 windows-874

というaliasにする予定、混乱するかな。

ちなみにGNU libiconvのcp874はwindows-874
Java6のCP874はibm874になってるもより。

これでGNU libiconvとの(中略)は

MACHEBREW
MACARABIC
CP922
DEC-KANJI

だけ。
MAC{HEBREW,ARABIC}についてはM:N変換モジュールが必要なんだけど
これGNU libiconvもまともに対応してないのね。

2007/06/15(Fri)

[NetBSD] Citrus ctype

某スレ読んで不憫に思ったので、病床見舞いに citrus_utf9書いたお。
<machine/limits.h>が

#define CHAR_BIT	9		/* number of bits in a char */

でないと動かんので NetBSD/pdp10専用ということでひとつ。
CHAR_BIT=8対応版欲しい人いるんかね?

ちなみにUTF-9という名前は RFC4042だけではなく、別の Internet Draftでも
使われてるんだけど、両者は全然別物であることに注意。

KOI8-Cも2つあるのよね、 これこれ(あと ここも)。
まあ前者の方がXFree86方面で使われてて有名なので
最近では後者をKOI8-Oと呼ぶ事もあるみたいなんだが
ここのKOI8-Oとはまた別モノだったりして、非常にややこしい。

2007/06/19(Tue)

[NetBSD] Citrus iconv

つーわけでCP1129,CP1161,CP1162,CP1163をcommit。

メモ。
IBM Code page by CPGID

先週末

Magic Busに乗って小旅行。

日頃の行いの悪さが遂に整数オーバーフローしたようで、奇跡的な好天に恵まれますた。

最近買ったもの

@ねんがんのリトルフィートりますたーをてにいれたぞ!

…あーでもザ・バンドの神リマスターの時のような衝撃はないな、ちょっと残念。

@その他

2007/06/25(Mon)

[C言語] TR24731-2 Bounds Checking via dynamic allocation

ちょっとだけ 更新、fmemopen(3)を追加してみた。
ちゃんとテストケース書かねば。

2007/06/26(Tue)

[C言語] TR24731-2

fmemopen(3)の次はopen_memstream(3)でも書こうかね。
こいつらは元々glibc拡張なんでFedoraCore6で挙動を確認してみる。

なんだかバグっぽい挙動その1:

01 #define _GNU_SOURCE
02 #include <stdio.h>
03 #include <stdlib.h>
04 int
05 main(void)
06 {
07 	char *s;
08 	size_t n;
09 	FILE *fp;
10 
11 	fp = open_memstream(&s, &n);
12 	setbuf(fp, NULL);
13 	fputc('A', fp);
14 	fputc('B', fp);
15 	fclose(fp);
16 	printf("%.*s\n", n, s);
17 	free(s);
18 
19 	return 0;
20 }

11行目setbuf(3)でバッファリングを無効にすると
12行目fputc(3)による書き込みでSEGVる。

同その2:

01 #define _GNU_SOURCE
02 #include <stdio.h>
03 #include <stdlib.h>
04 int
05 main(void)
06 {
07 	char *s;
08 	size_t n;
09 	FILE *fp;
10 
11 	fp = open_memstream(&s, &n);
12 	fputc('A', fp);
13 	fputc('B', fp);
14 	fflush(fp);
15 	fseek(fp, -1, SEEK_CUR);
16 	fflush(fp);
17 	fputc('C', fp);
18 	fclose(fp);
19 	printf("%.*s\n", n, s);
20 	free(s);
21 
22 	return 0;
23 }

15行目fseek(3)でいっこ巻き戻してるはずなので、19行目printf(3)では
"AC"が出力されるべき。にもかかわらず"ABC"と出力される。

これは仕様として正しいのかねぇ、自分にはバグとしか思えんのだが。

2007/06/29(Fri)

fmemopen/open_memstream

バールStringIOみたいなものなんでしょうか?

Yes, PerlのIO::String、Python、RubyのStringIOと同じですかね、LLよう知らんけど。
Javaなんかだと

FILE				→ interface java.io.OutputStream
fopen/fdopen			→ class java.io.FileOutputStream
fmemopen/open_memstream		→ class java.io.ByteArrayOutputStream

という関係になるかと。

元々*BSD系ではFILE + fread/fwrite他はfile descriptor操作の為だけのものではありません。
funopen(3)を使うことで、どんなstreamでもfread/fwrite経由で読み書きできる汎用インタフェースになってます。

#include <sys/cdefs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>

int
readfn(void *cookie, char *s, int n)
{
	gzFile *p = (gzFile *)cookie;
	return gzread(p, (voidp)s, n);
}

int
writefn(void *cookie, const char *s, int n)
{
	gzFile *p = (gzFile *)cookie;
	return gzread(p, (voidp)__UNCONST(s), n);
}

fpos_t
seekfn(void *cookie, fpos_t offset, int whence)
{
	gzFile *p = (gzFile *)cookie;
	return (fpos_t)gzseek(p, (z_off_t)offset, whence);
}

int
closefn(void *cookie)
{
	gzFile *p = (gzFile *)cookie;
	return gzclose(p);
}

int
main(int argc, char **argv)
{
	const char *s;
	size_t n;
	FILE *fp;
	int ch;

	if (argc != 2) {
		fprintf(stderr, "usage: %s [filenname[.gz]]\n", getprogname());
		return 1;
	}
	s = argv[1];
	n = strlen(s);
	if (n > 3 && s[n - 1] == 'z' && s[n - 2] == 'g' && s[n - 3] == '.') {
		gzFile *cookie;
		cookie = gzopen(s, "r+");
		fp = funopen(cookie, &readfn, &writefn, &seekfn, &closefn);
	} else {
		fp = fopen(s, "r+");
	}
	while ((ch = fgetc(fp)) != EOF)
		printf("%c\n", ch);
	return 0;
}

↑のような使い方をすれば、zlibの存在を隠蔽できます。

glibcにもfunopen(3)とほぼ同等のfopencookie(3)がありますが、
fmemopen/open_memstreamといった文字型配列操作に特化した関数は
こんな↓使い方を想定して生まれたんじゃないかなと。

int
vsnprintf(char *s, size_t n, const char *fmt, va_list ap)
{
	struct FILE *fp;
	size_t n;

	fp = fmemopen((void *)s, n, "w");
	if (fp == NULL)
		return -1;
	return vfprintf(fp, fmt, ap);	
}

int
vasprintf(char **p, const char *fmt, va_list ap)
{
	struct FILE *fp;
	size_t n;

	fp = open_memstream(p, &n);
	if (fp == NULL)
		return -1;
	return vfprintf(fp, fmt, ap);
}

ただglibcのvsnprintf/vasprintfの実装はこうはなってませんが。

アプリでの使い方としては - 例えば次のようなインタフェースを考えた場合

/*
 * 文字列を解析して設定情報を読み取る
 */
int
parse_config_string(const char *s, size_t n)
{
	int ch;

	for (;;) {
		ch = (int)*s;
		++s, --n;
		...

		--s, ++n;
	}
}

/*
 * ファイルを解析して設定情報を読み取る
 */
int
parse_config_file(FILE *fp)
{
	int fd;
	struct stat st;
	char *s;
	size_t n;

	fd = fileno(fp);
	fstat(fd, &st);
	n = (size_t)st.size;
	s = mmap(NULL, n, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, (fpos_t)0);
	return parse_config_string(s, n);
}

上記のようにmmap(2)を使って楽してー、ってのが人情ですが、これには制限があって

にはmmap(2)に失敗しますので正常に動作しなかったりします。

その制限は困るとゆー場合

int
parse_config_file(FILE *fp)
{
	int ch;
	for (;;) {
		ch = fgetc(fp);
		...

		ungetc(ch, fp);
	}
}

int
parse_config_string(const char *s, size_t n)
{
	FILE *fp;
	fp = fmemopen(s, n);
	return parse_config_file(fp);
}

としてしまえばいいでしょう、まあ性能は落ちるでしょーが。

こまかい仕様についてはSC22/WG14の TR24731-2を参照してください。