/*
ちょっと調査。
まずemacsでひらがなの「が」をファイルに書くとLinux
とOSXでは違いがあるのかどうか。そのファイルをodで比
べてみる。
まずOSX。
----
$ od -t x1 -t c HIRAGANA-LETTER-GA.txt
0000000 61 62 63 64 0a e3 81 8c 0a
a b c d \n が ** ** \n
0000011
----
続いてLinux。
----
$ od -t x1 -t c HIRAGANA-LETTER-GA.txt
0000000 61 62 63 64 0a e3 81 8c 0a
a b c d \n 343 201 214 \n
0000011
----
違いは無い。
さて、e3 81 8c をUTF-8にて解釈してみる。
ビットにすると、
----
CL-USER(13): (dolist (n (list #xe3 #x81 #x8c))
(format t "~b " n))
11100011 10000001 10001100
NIL
----
である。このビットパターンは、UTF-8としては
1110yyyy 10yxxxxx 10xxxxxx
であり、最大16bitのコードポイントを表現している。
具体的にコードポイント対応部分を抽出すると、
0011 000001 001100
さらにこれを右詰めoctetになおすと、
00110000 01001100
----
CL-USER(14): (dolist (n (list #b00110000 #b01001100))
(format t "~x " n))
30 4c
NIL
----
というわけで、これはNFCの「が」(U+304C)だ。
とどのつまりファイルの中身にはOSは関知していないか
ら、emacsやodが文字をUTF-8のNFCで取り扱っており、そ
こに一貫性がある、ということだろう。
続いてgccのプリプロセッサまでの処理における文字の取
り扱いを確認したい。
前章で実験したところ、gccはC99に完全準拠していなかっ
た。ソースファイルの識別子に多バイト文字が使われて
いた場合は、それをプリプロセス前に国際文字名
(\uxxxx)に変換しなければいけないのだが、それをやっ
てくれていないようだ。それを確認する。
*/
----
#include <stdio.h>
int main(void)
{
int が;
for (が=1;が<10;が++)
printf("%d ", が);
return (0);
}
----
/*
こんなソースでためす。これのod。
----
$ od -t x1 -t c i18n-character-name-2.c
0000000 23 69 6e 63 6c 75 64 65 20 3c 73 74 64 69 6f 2e
# i n c l u d e < s t d i o .
0000020 68 3e 0a 69 6e 74 20 6d 61 69 6e 28 76 6f 69 64
h > \n i n t m a i n ( v o i d
0000040 29 0a 7b 0a 20 20 20 20 20 69 6e 74 20 e3 81 8c
) \n { \n i n t が ** **
0000060 3b 0a 20 20 20 20 20 66 6f 72 20 28 e3 81 8c 3d
; \n f o r ( が ** ** =
0000100 31 3b e3 81 8c 3c 31 30 3b e3 81 8c 2b 2b 29 20
1 ; が ** ** < 1 0 ; が ** ** + + )
0000120 0a 20 20 20 20 20 20 20 20 20 20 70 72 69 6e 74
\n p r i n t
0000140 66 28 22 25 64 20 22 2c 20 e3 81 8c 29 3b 0a 0a
f ( " % d " , が ** ** ) ; \n \n
0000160 20 20 20 20 20 72 65 74 75 72 6e 20 28 30 29 3b
r e t u r n ( 0 ) ;
0000200 0a 7d 0a
\n } \n
0000203
$
----
これをgcc -Eで処理。
includeしているから長くなるが、とにかく変換はしていないようだ。
*/
----
# 1 "i18n-character-name-2.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "i18n-character-name-2.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 64 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/_types.h" 1 3 4
# 27 "/usr/include/_types.h" 3 4
# 1 "/usr/include/sys/_types.h" 1 3 4
# 32 "/usr/include/sys/_types.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 33 "/usr/include/sys/_types.h" 2 3 4
# 1 "/usr/include/machine/_types.h" 1 3 4
# 34 "/usr/include/machine/_types.h" 3 4
# 1 "/usr/include/i386/_types.h" 1 3 4
# 37 "/usr/include/i386/_types.h" 3 4
typedef signed char __int8_t;
... 中略 ...
static __inline int __sputc(int _c, FILE *_p) {
if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n'))
return (*_p->_p++ = _c);
else
return (__swbuf(_c, _p));
}
# 2 "i18n-character-name-2.c" 2
int main(void)
{
int が;
for (が=1;が<10;が++)
printf("%d ", が);
return (0);
}
---
/*
では、文字列定数の中だとちゃんとやってくれるのか?
というところで同じソースを変更して確認する
*/
----
#include <stdio.h>
int main(void)
{
int i;
for (i=1;i<10;i++)
printf("%d が", i);
return (0);
}
----
*/
/*
あり、だめだ。次のように、変換していない。
*/
----
前略 ...
static __inline int __sputc(int _c, FILE *_p) {
if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n'))
return (*_p->_p++ = _c);
else
return (__swbuf(_c, _p));
}
# 2 "i18n-character-name-3.c" 2
int main(void)
{
int i;
for (i=1;i<10;i++)
printf("%d が", i);
return (0);
}
----
/*
というわけで、gccの字句関係処理はC99の要求まんまに
実装されているのではなさそうだ。ソース文字集合が
UTF-8そのものである、という作りに見受けられる。
さて、次にファイル名の取り扱いを確認したい。ファイ
ルの中身とちがって、ここはOSの関与がある部分だ。
"が.txt"というファイルを作成する。これの中身を表示
するプログラムで振舞いを確認してみる。APIはCの標準
ライブラリを使う。これは、予測としては不一致なくう
まくいくはず。gccの標準ライブラリがfopenの引数たる
ファイル名をOSXならOSX向けに取り扱ってくれるという
予測だ。
----が.txt---
abcd
が
-------------
*/
----ga-opne.c----
#include <stdio.h>
int main(void)
{
FILE *fp;
fp = fopen("が.txt", "r");
if (fp == NULL)
printf("failed.\n");
else {
printf("Succeed.\n");
fclose(fp);
}
return (0);
}
--------
/*
----
$ gcc -std=c99 ga-open.c
$ ./a.out
Succeed.
$
----
うまくいった。
さて、ファイル名の取得はどうだろう。ディレクトリに
含まれるファイルの名前を取得する方法は標準Cにはない。
よって、ここから先は処理系次第となる。ただし処理系/環境
がPOSIXだとかSUSとかに対応しているなら、それらAPIを
使うということである程度のポータビリティは期待でき
る。
さて、この領域は、Advanced Programming in the UNIX
Environment (apue.2e)だろう、ということでapue.2eか
らサンプルをとってくる。
----ls1.c----
*/
#include "apue.h"
#include <dirent.h>
int
main(int argc, char *argv[])
{
DIR *dp;
struct dirent *dirp;
if (argc != 2)
err_quit("usage: ls directory_name");
if ((dp = opendir(argv[1])) == NULL)
err_sys("can't open %s", argv[1]);
while ((dirp = readdir(dp)) != NULL)
printf("%s\n", dirp->d_name);
closedir(dp);
exit(0);
}
/*
--------
apue.2eのサイトにあがっているソースをLeopard上でコ
ンパイルするには多少調整が必要。apue.2eが発刊された
ときは、10.3.xだったのだ。それから10.4、10.5とかわ
るなかでOSXはPOSIX対応を進めたため、ヘッダ関係で混
乱があるようだ。
いくつか調整作業をしてmakeと次のコマンドでls1.cをコ
ンパイルした。
gcc -ansi -I/Users/aka/scratch/c-ref/apue.2e/include -Wall -g -DMACOS -L../lib ls1.c ../lib/libapue.a -o ls1
で、そのls1で、が.txtを含むga-testディレクトリの中
身をOSから取得する。
(gdb) n
..
(gdb) p dirp->d_name
$10 = "..\000\000?\r\000\024\000\b\nが.txt\000*", '\0' <repeats 231 times>
(gdb) n
(gdb) p dirp->d_name
$11 = "が.txt\000*", '\0' <repeats 243 times>
(gdb) p/x dirp->d_name
$12 = {0xe3, 0x81, 0x8b, 0xe3, 0x82, 0x99, 0x2e, 0x74, 0x78, 0x74, 0x0, 0x2a, 0x0 <repeats 244 times>}
(gdb)
で、ここでメモリに入ってる
0xe3, 0x81, 0x8b, 0xe3, 0x82, 0x99
が、"が"なのだが、これを先程と同じ方法でUTF-8として
読み解くと、
U+304b U+3099
[HIRAGANA LETTER KA] [COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK]
であることがわかる。ここでNFDがあらわれた!
そのあとは、printfで%sしてるだけなので、この
UTF-8(NFD)が正しく表示されるかは、その表示を担当す
るものによりけりになる。上記のls1->gdb->emacs->screen->Terminal
では実は正常に表示されず、が.txtの"が"と"."の間に妙な文字が表示
されてしまう。(コピペした際に上のgdb出力のこの異常は消えちゃった)
Terminal直ならば問題なくて、
------------
$ ./ls1 ga-test
.
..
が.txt
$
------------
となる。
さて、ここまで来たところで、apue.2eのreaddirの出自
を確認する必要がある。
apue.2eによると、POSIX.1とのこと。
とするとmanにあるのか? man readdir、あった。
-----------
DIRECTORY(3) BSD Library Functions Manual DIRECTORY(3)
NAME
closedir, dirfd, opendir, readdir, readdir_r, rewinddir, seekdir, telldir --
directory operations
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include <dirent.h>
...
-----------
するとdirentもmanにあるのか? あった。
-----------
DIR(5) BSD File Formats Manual DIR(5)
NAME
dir, dirent -- directory file format
SYNOPSIS
#include <sys/types.h>
#include <sys/dir.h>
DESCRIPTION
Directories provide a convenient hierarchical method of grouping files while
obscuring the underlying details of the storage medium. A directory file is
differentiated from a plain file by a flag in its inode(5) entry. It consists
of records (directory entries) each of which contains information about a file
...
-----------
で、
-----------
*/
struct dirent {
ino_t d_ino; /* file number of entry */
u_int64_t d_seekoff; /* length of this record */
u_int16_t d_reclen; /* length of this record */
u_int16_t d_namlen; /* length of string in d_name */
u_int8_t d_type; /* file type, see below */
char d_name[MAXPATHLEN]; /* name must be no longer than this */
};
/*
-----------
なそうであり、d_nameの中身がどうあるべきかは無い。
まあ、ファイルシステムは環境によって違うからなぁ。
というわけで、このPOSIX.1のreaddir経由でOXとアプリ
がファイル名をやりとりする場合は、エンコードについ
てはアプリ側がOSが何を選択しているかをどこかで知っ
た上で使わないといけないということだ。
*/
これで、Subversionにおけるファイル名の取り扱いを探る準備ができたのかなぁ。
こつこつ。
0 件のコメント:
コメントを投稿