Not only is the Internet dead, it's starting to smell really bad.:2017年05月下旬

2017/05/20(Sat)

[NetBSD] debugging BSD sed(1) (その5)

@古代遺跡で廃品回収

昔から*BSDのカビ臭いコードを弄るのって古代遺跡から出土した謎のロボットをよく判らずに動かすのに似て、どっか弄ると最悪全滅して全裸で因果地平の彼方に吹き飛ばされるからアレって前々からよく言ってるんだけど、また今日も致命傷で済んだ。

まぁGNUのどんなケミカルキメたらこのコードスタイルが美しいと感じるのかよりはマシかもしれんが。

そんでBSD sed(1)も古いコードなので今時のCならこう書き直した方がいいってコード多いのよね。 例えばmain.cのcu_fgets()、これはfgets(3)のようにストリームから一行を取り出すAPIなんだけども、それプラス

  • 入力は-fで指定されたファイルだけでなく-eで指定された文字列もある
  • これらの入力を-e/-fで指定された数だけ全部、順々にシームレスに処理していきたい

という拡張がされている。

/*
 * Like fgets, but go through the chain of compilation units chaining them
 * together.  Empty strings and files are ignored.
 */
char *
cu_fgets(char **outbuf, size_t *outsize)
{
	static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
	static FILE *f;		/* Current open file */
	static char *s;		/* Current pointer inside string */
	static char string_ident[30];
	size_t len;
	char *p;

	if (*outbuf == NULL)
		*outsize = 0;

again:
	switch (state) {
	case ST_EOF:
		if (script == NULL)
			return (NULL);
		linenum = 0;
		switch (script->type) {
		case CU_FILE:
			if ((f = fopen(script->s, "r")) == NULL)
				err(FATAL,
				    "%s: %s", script->s, strerror(errno));
			fname = script->s;
			state = ST_FILE;
			goto again;
		case CU_STRING:
			if ((snprintf(string_ident,
			    sizeof(string_ident), "\"%s\"", script->s)) >=
			    (int)(sizeof(string_ident) - 1))
				(void)strcpy(string_ident +
				    sizeof(string_ident) - 6, " ...\"");
			fname = string_ident;
			s = script->s;
			state = ST_STRING;
			goto again;
		}
	case ST_FILE:
		if ((p = fgetln(f, &len)) != NULL) {
			linenum++;
			if (len >= *outsize) {
				free(*outbuf);
				*outsize = ROUNDLEN(len + 1);
				*outbuf = xmalloc(*outsize);
			}
			memcpy(*outbuf, p, len);
			(*outbuf)[len] = '\0';
			if (linenum == 1 && p[0] == '#' && p[1] == 'n')
				nflag = 1;
			return (*outbuf);
		}
		script = script->next;
		(void)fclose(f);
		state = ST_EOF;
		goto again;
	case ST_STRING:
		if (linenum == 0 && s[0] == '#' && s[1] == 'n')
			nflag = 1;
		p = *outbuf;
		len = *outsize;
		for (;;) {
			if (len <= 1) {
				*outbuf = xrealloc(*outbuf,
				    *outsize + _POSIX2_LINE_MAX);
				p = *outbuf + *outsize - len;
				len += _POSIX2_LINE_MAX;
				*outsize += _POSIX2_LINE_MAX;
			}
			switch (*s) {
			case '\0':
				state = ST_EOF;
				if (s == script->s) {
					script = script->next;
					goto again;
				} else {
					script = script->next;
					*p = '\0';
					linenum++;
					return (*outbuf);
				}
			case '\n':
				*p++ = '\n';
				*p = '\0';
				s++;
				linenum++;
				return (*outbuf);
			default:
				*p++ = *s++;
				len--;
			}
		}
	}
	/* NOTREACHED */
	return (NULL);
}

汚いものは閉じ込めておくって意味では正解なんだけども、cu_fgets()自体の実装はとても読めたものじゃない(アロケーション周りほんとクソ)。

しかしPOSIX:2008で導入された新関数(ってもglibc由来だけど)

 NAME
      fmemopen -- open a stream that points to the given buffer
 
 LIBRARY
      Standard C Library (libc, -lc)
 
 SYNOPSIS
      #include <stdio.h>
 
      FILE *
      fmemopen(void  *restrict buffer, size_t size, const char *restrict mode);

のように

  • 文字列などのメモリオブジェクトに対してFILEの皮を被せて
  • stdioの関数を使って操作できる
  • もうポインタとダンスする必要はなく両者共通のコードで処理できる

という新兵器を使えばグッと簡単に書ける。

そしてfgetln(3)のような移植性も無い上に使えばoff-by-oneの温床になるマニュアルを読んでたらまず使う気にならんモノも捨て、やはりこちらもPOSIX:2008から導入されたgetline(3)を使う。

 NAME
      getdelim, getline -- read a delimited record from a stream
 
 LIBRARY
      Standard C Library (libc, -lc)
 
 SYNOPSIS
      #include <stdio.h>
 
      ssize_t
      getdelim(char ** restrict lineptr, size_t * restrict n, int
      delimiter, FILE * restrict stream);
 
      ssize_t
      getline(char ** restrict lineptr, size_t * restrict n, FILE * restrict
      stream);

まぁそんな感じで文字列とダンスってるコードがガッツリ削った結果がこちら。

/*
 * Like fgets, but go through the chain of compilation units chaining them
 * together.  Empty strings and files are ignored.
 */
char *
cu_fgets(char **outbuf, size_t *outsize)
{
	static FILE *f;		/* Current open file */
	static char string_ident[30];
	ssize_t len;
	char *p;

	if (f == NULL) {
again:
		if (script == NULL)
			return (NULL);
		linenum = (size_t)0;
		switch (script->type) {
		case CU_FILE:
			fname = script->s;
			f = fopen(script->s, "r");
			break;
		case CU_STRING:
			if ((snprintf(string_ident,
			    sizeof(string_ident), "\"%s\"", script->s)) >=
			    (int)(sizeof(string_ident)))
				strlcpy(string_ident +
				    sizeof(string_ident) - 6, " ...\"", 5);
			fname = string_ident;
			f = fmemopen(script->s, strlen(script->s) + 1, "r");
		}
		if (f == NULL)
			err(EXIT_FAILURE, "%s", fname);
		if ((len = getline(outbuf, outsize, f)) == -1)
			goto reacheof;
		p = *outbuf;
		if (len >= 2 && p[0] == '#' && p[1] == 'n')
			nflag = true;
	} else if ((len = getline(outbuf, outsize, f)) == -1) {
reacheof:
		if (ferror(f))
			errx(EXIT_FAILURE, "%s: %s",
			    fname, strerror(errno ? errno : EIO));
		script = script->next;
		fclose(f);
		f = NULL;
		goto again;
	}
	linenum++;
	return *outbuf;
}

コード量はきっかり半分になった(まだこれでもデバック用の文言ごときにゴチャゴチャ書いてるので邪悪)上に、2つほどメモリ関係のバグが直ってる。

これまでもファイルの方をmmap(2)してメモリで扱い、コードをポインタ操作に寄せてしまってファイルとメモリの違いを意識しないみたいなコードは書けたんだけれども、その場合はstdinなんかの扱いに困るわけでな。

@あゝNetBSD時間

ということで該当箇所をfmemopen(3) + getline(3)使うように書き直したんだけど、通常の/usr/bin/sedは問題ないのにtoolchainのnbsedだけクラッシュする。

うはーなんかバグ踏んだかなと思って検死したら、getline(3)からgetdelim(3)に入ってFILE構造体の内部を触ったとこでよく判らない死を遂げている

$ gdb -q /usr/src/obj.amd64/tooldir.NetBSD-6.1_STABLE-amd64/bin/nbsed
Reading symbols from /usr/obj.amd64/tooldir.NetBSD-6.1_STABLE-amd64/bin/nbsed...(no debugging symbols found)...done.
(gdb) run -e "s,/bin/sh,/bin/sh,g"  -e "s,{AWK:=.*},{AWK:="/usr/src/obj.amd64/tooldir.NetBSD-6.1_STABLE-amd64/bin/nbawk"},"  < /usr/src/tools/genassym/../../usr.bin/genassym/genassym.sh > genassym
Starting program: /usr/obj.amd64/tooldir.NetBSD-6.1_STABLE-amd64/bin/nbsed -e "s,/bin/sh,/bin/sh,g"  -e "s,{AWK:=.*},{AWK:="/usr/src/obj.amd64/tooldir.NetBSD-6.1_STABLE-amd64/bin/nbawk"},"  < /usr/src/tools/genassym/../../usr.bin/genassym/genassym.sh > genassym

Program received signal SIGSEGV, Segmentation fault.
0x00007f7ff7523970 in __getdelim (buf=0x6067a8, buflen=0x6067a0, sep=10, fp=0xfffffffff779a2a0)
    at /usr/src/lib/libc/stdio/getdelim.c:75
75              _SET_ORIENTATION(fp, -1);
(gdb) bt
#0  0x00007f7ff7523970 in __getdelim (buf=0x6067a8, buflen=0x6067a0, sep=10, fp=0xfffffffff779a2a0)
    at /usr/src/lib/libc/stdio/getdelim.c:75
#1  0x00007f7ff7523c1a in _getdelim (buf=0x6067a8, buflen=0x6067a0, sep=10, fp=0xfffffffff779a2a0)
    at /usr/src/lib/libc/stdio/getdelim.c:151
#2  0x00007f7ff74be008 in _getline (buf=0x6067a8, buflen=0x6067a0, fp=0xfffffffff779a2a0)
    at /usr/src/lib/libc/stdio/getline.c:44
#3  0x000000000040330f in cu_fgets ()
#4  0x0000000000401e05 in compile ()
#5  0x0000000000403189 in main ()

どう考えても俺無罪なので、ピーンときてcompat周りのコード読んだら src/tools/compat/compat_defs.hでの

/*
 * On NetBSD, ensure that _NETBSD_SOURCE does not get defined, so that
 * accidental attempts to use NetBSD-specific features instead of more
 * portable features is likely to be noticed when the tools are built
 * on NetBSD.  Define enough other feature test macros to expose the
 * features we need.
 */
#ifdef __NetBSD__
#define	_ISOC99_SOURCE
#define _POSIX_SOURCE	1
#define _POSIX_C_SOURCE	200112L
#define _XOPEN_SOURCE 600
#endif /* __NetBSD__ */

to とtoolchainのビルドにSUSv6を強制してるのが悪さしてるやんけこれ。NetBSD時間は未だに15年以上前を彷徨っているようでもう成立して10年経つPOSIX:2008の関数なぞいまだ存在しませんみたいなことやっとる。

なもんで以前の 後方互換性周りの記事で書いたようにこれらのマクロによって

#if (_POSIX_C_SOURCE - 0) >= 200809L || (_XOPEN_SOURCE - 0) >= 700 || \
    defined(_NETBSD_SOURCE)
__BEGIN_DECLS
FILE *fmemopen(void * __restrict, size_t, const char * __restrict);
FILE *open_memstream(char **, size_t *);
ssize_t	 getdelim(char ** __restrict, size_t * __restrict, int,
	    FILE * __restrict);
ssize_t	 getline(char ** __restrict, size_t * __restrict, FILE * __restrict);
__END_DECLS
#endif

今回使ったfmemopen+getlineのプロトタイプ宣言は消されてしまい、戻り値は強制的にintとしてリンクされ(そういえばbuild.sh tool中に警告出てたのを見落としていた)てたってことだ。FILE *だとポインタがintに丸められたらそらクラッシュしますわね。

これまでも getline(3)化の作業をやってたんだけど、こっちはssize_tからintにnarrowされてもまぁ問題なく動いてるようにみえたので顕在化しなかったわけだ、あーあ。

@結論

ありとあらゆるものがepoch(1970-01-01T00:00:00)に向かって退行するNetBSD時間、 ユービックスプレーをもってしても2017年の現代まで戻るのはP.K.ディックでも無理ゲー、イナゴ身重く横たわりガブルガブルガビッシュなのである。

まぁ

#define _POSIX_C_SOURCE	200809L
#define _XOPEN_SOURCE 700

にアプデすりゃいいんだが、POSIX:2008の一部機能の実装に反対して「Against the standard」までゆうた連中やし是非このまま15年前を生きていて欲しいもんである、3次元でなく4次元の世界の住人から観測すれば時間のスピードでワイと彼らが離れていく姿が観測できるわけだ、次元上げたいな。

この機能はクソだから仕様にあろうが実装しない方が賢明とかいう態度だから時間退行していくのやけどね、SolarisですらあれだけLinuxの痛々しいコスプレまでしたのにその甲斐なく無残に死んだわけでな。

まぁ未だC89で30年前の連中もいるからまだマシかもしれん、やはりCはもう捨てろ。

2017/05/21(Sun)

debugging BSD sed(1) (その6)

@モダンなコードに書き直す

お次はcompile.cのcompile_text()のリファクタリング、この部分はsedの

  • a\(append)
  • i\(insert)
  • c\(change)

の引数となるテキスト部分の境界(改行)を探して切り出すのと、エスケープ文字を消し込む処理をしている。

現在のN headのコード rev1.41あたりでchristosがどこから拾ってきたのかよく判らないバージョン(おそらくFreeBSDの実装だと思う)のBSD sed(1)と置換したようでのでいろいろデグって(最下層SIer用語)おられる。

以前はcu_fgets()に渡すlbufを固定長ではなく動的に確保する変更が入ってたんだけど、元に戻ってしまっている。 よって現在はsedに食わせるスクリプトの1行の長さは_POSIX2_LINE_MAX以上は切り詰められてしまう、滝川先生の通常営業やしどうせ誰も使ってないから問題ないよな。

/*
 * Compile the text following an a or i command.
 */
static char *
compile_text(void)
{
	size_t asize, size;
	int esc_nl;
	char *text, *p, *op, *s;
	char lbuf[_POSIX2_LINE_MAX + 1];

	asize = 2 * _POSIX2_LINE_MAX + 1;
	text = xmalloc(asize);
	size = 0;
	while (cu_fgets(lbuf, sizeof(lbuf), NULL)) {
		op = s = text + size;
		p = lbuf;
		for (esc_nl = 0; *p != '\0'; p++) {
			if (*p == '\\' && p[1] != '\0' && *++p == '\n')
				esc_nl = 1;
			*s++ = *p;
		}
		size += (size_t)(s - op);
		if (!esc_nl) {
			*s = '\0';
			break;
		}
		if (asize - size < _POSIX2_LINE_MAX + 1) {
			asize *= 2;
			text = xrealloc(text, asize);
		}
	}
	text[size] = '\0';
	p = xrealloc(text, size + 1);
	return (p);
}

こっちのコードはOpenBSDだとこんな感じ。

/*
 * Compile the text following an a, c, or i command.
 */
static char *
compile_text(void)
{
	int asize, esc_nl, size;
	char *lbuf, *text, *p, *op, *s;
	size_t bufsize;

	lbuf = text = NULL;
	asize = size = 0;
	while ((p = cu_fgets(&lbuf, &bufsize))) {
		size_t len = ROUNDLEN(strlen(p) + 1);
		if (asize - size < len) {
			do {
				asize += len;
			} while (asize - size < len);
			text = xrealloc(text, asize);
		}
		op = s = text + size;
		for (esc_nl = 0; *p != '\0'; p++) {
			if (*p == '\\' && p[1] != '\0' && *++p == '\n')
				esc_nl = 1;
			*s++ = *p;
		}
		size += s - op;
		if (!esc_nl) {
			*s = '\0';
			break;
		}
	}
	free(lbuf);
	text = xrealloc(text, size + 1);
	text[size] = '\0';
	return (text);
}

こっちは_POSIX2_LINE_MAX縛りはない、まぁlbufはstatic宣言してfree(3)せずに使い回した方が性能面で好ましいとは思うが。

どっちのコードについても

  • textの指すメモリのアロケーション周りのコードが冗長、オーバーフローチェックとか境界値の計算とかミスりやすく死ゾ
  • 以前に記事にした20年モノのバグ修正絡みのesc_nlフラグを使った文字列のNUL terminateまわりのコードがこちらも冗長、やはりミスるとオーバーランでやはり死ゾ

ちゅー感じで、いつチェーンソーで自分の両足切り落とさんか心配になるコードよね。

これもPOSIX:2008の新機能であるopen_memstream(3)を使えばコードはもっとシンプルかつ安全になるんですわ。

このコードをopen_memstream(3)を使って書き直すとこんな感じ。

/*
 * Compile the text following an a, c, or i command.
 */
static char *
compile_text(void)
{
	static char *lbuf;
	static size_t bufsize;
	char *text, *s;
	size_t len;
	FILE *fp;
	_Bool esc_nl;

	text = NULL;
	len = 0;
	fp = open_memstream(&text, &len);
	if (fp == NULL)
		err(EXIT_FAILURE, NULL);

	while ((s = cu_fgets(&lbuf, &bufsize))) {
		for (esc_nl = false; *s != '\0'; ++s) {
			if (*s != '\\')
				continue;
			if (*++s == '\0')
				break;
			if (*s == '\n')
				esc_nl = true;
			if (fputc(*s, fp) == EOF)
				err(EXIT_FAILURE, NULL);
		}
		if (!esc_nl)
			break;
	}
	fclose(fp);
	return (text);
}

まずopen_memstream(3)にメモリ管理をお任せしてしまうことでROUNDLEN()マクロとlen, asize, sizeそしてxreallocで死神と舞踏(ダンス)ってるコードが不要になる。そしてNUL terminateもお任せできるんで、こっちのややこしいフラグ処理も不要になる。

ただし *s = *p → fputc(*p) に書換えただけだと関数呼出が多くなるので

static inline void
efwrite(const void *p, size_t size, size_t nmemb, FILE *fp)
{
	if (nmemb > 0 && fwrite(p, size, nmemb, fp) != nmemb)
		err(EXIT_FAILURE, NULL);
}

/*
 * Compile the text following an a, c, or i command.
 */
static char *
compile_text(void)
{
	static char *lbuf;
	static size_t bufsize;
	char *text, *t, *s;
	size_t len;
	FILE *fp;
	_Bool esc_nl;

	text = NULL;
	len = 0;
	fp = open_memstream(&text, &len);
	if (fp == NULL)
		err(EXIT_FAILURE, NULL);

	while ((s = cu_fgets(&lbuf, &bufsize))) {
		for (t = s; *t != '\0'; ++t) {
			if (*t != '\\')
				continue;
			if (*++t == '\0')
				break;
			if (*t == '\n')
				esc_nl = true;
			efwrite(s, sizeof(*s), (t - 1) - s, fp);
			s = t;
		}
		efwrite(s, sizeof(*s), t - s, fp);
		if (!esc_nl)
			break;
	}
	fclose(fp);
	return (text);
}

くらいのことはしといた方がいいか、可読性は若干落ちるけど。

@GNU sed の拡張

ちゅーことで書き直してテストしてて、 その4でなぜFが引数をまるっとコピーして強制改行してるかの理由に思い当ってしまった。

これおそらくGNU sedの拡張と同じような動作をしようとして四苦八苦してるんやな、今テストしてる a\ i\ c\コマンドの後のテキスト引数だけど、GNU sedは複数の-eで渡された文字列を連結して次の行として解釈するんだなこれ。

$ (echo 1; echo 2; echo 3) | gsed -e '2c\' -e 'unko'
1
unko
3
$

つまり-eは行として解釈しないと出力結果が一致しない、現にNとOでは改行がつかないので

$ (echo 1; echo 2; echo 3) | sed -e '2c\' -e 'unko'
1
unko3
$

ちゅーかんじでくっついてしまわれる。

そもそもPOSIXの仕様にはそんな事書いてないので、Solarisのxpg4 sedなんかは-eはそれぞれ別として完結して扱うから

$ (echo 1; echo 2; echo 3) | sed -e '2c\' -e 'unko'
sed: 編集スクリプトの終わりにエスケープ文字が見つかりました

とc\に引数が無いせいでシンタックスエラーで終了してしまう、なのでコマンドと引数は分けずに

$ (echo 1; echo 2; echo 3) | sed -e '2c\
> unko'
1
unko
3
$

といっこの-eで書く必要があるわけで、移植性無いからどうでもいいっちゃいいんだが。

なわけでさぞかしFはGNUと同じ挙動をしてくれるんだろうと確認してみたんだが

$ (echo 1; echo 2; echo 3) | sed -e '2c\' -e 'unko'
sed: 1: "unko
": invalid command code u
$

ファーwww同じcu_fgets()で処理してんのになぜか継続行の扱いにならない上に、引数に全部改行つけてるせいでエラーメッセージがおかしくなっておられる、全然意味ないじゃんこれ。

@sed(1) is 闇深

あと前述の20年物のバグちゅーて魔球先生とボス先生が送って来た「テキストの最後が改行を伴なわないエスケープ文字\で終わるケース」をGNU sedで試すと

$ echo foo | gsed -e '/foo/c\
> unko\
> unko\
> unko\'
unko\
unko\
unko
$

ちゅーかんじでそもそも

  • 問題の改行を伴なわないエスケープ文字は表示されない → わかる
  • 2行以上あると最終行以外の本来出力されてはならないエスケープ文字が出力されてしまう →バグだろこれ

ちゅー感じ、最後のエスケープがなければ

$ echo foo | gsed -e '/foo/c\
> unko\
> unko\
> unko'
unko
unko
unko
$

なのでどういう処理してんだか…

SolarisのXPG4 sedだとキストの最後が改行を伴なわないエスケープ文字\はエラーやね

$ echo foo | /usr/xpg4/bin/sed -e '/foo/c\
> unko\
> unko\
> unko\'
sed: 編集スクリプトの終わりにエスケープ文字が見つかりました

うーんsed(1) is 闇深、同じ闇ならワイはperlのようなもので書きますわ…

とにかくこのテキスト周りは仕様とか歴史的経緯とか拡張の使われ具合とか全部一度整理して整合性つけないとダメだわこれ。

2017/05/22(Mon)

[やきう] セ・パ交流戦間近

千葉ロッテマリーンズ自力優勝消滅、5月にこれはワイまで暗黒TBSベイス時代の90敗の記憶がフラッシュバックしてPTSDになりますわ…

しかし忘れないで欲しい、今年のオープン戦で 大洋ホエールズ横浜DeNAベイスターズはそんなロッテに一方的に虐殺され、ハマスタ0勝でシーズンを迎える羽目になった事を。

ほんと横浜クッソ弱くてもう今年心が折れている、横浜千葉名古屋大阪(神戸)みんな一緒にクソまみれになろうや…

[プログラミング幻語C][NetBSD] C2X宣言

C2X宣言だとさ、C99ももうすぐ成人式でとっくに淫行条例に引っかからない年とはね(しろめ)

まぁ202X年頃には野垂れ死にしとると思うしどうでもいい、ただ最低でもいい加減C99 __VA_ARGS__のクソさ(trailing comma問題ね)だけはどうにかして欲しい。 VC++と同じにしてくれ、あるいはgccの##__VA_ARGS__でも構わん、そういやこれC11に提案すら無かったんか…

もはやCはC++の尻拭い的案件(Thread Aware Localeやchar{16,32}_t他)を粛々と入れてくだけってのはC11ではっきりしたしな。

なんかの提案に胸 焼けを躍らせていた日々を返してほしいやな。

そいえばAppleがObjective-CからSwiftに舵切ったのって、これN1370が否決されてAppleはWG14(C)はWG21(C++)の金魚の糞でちゅーことで見切った可能性? どうなんやろね。

あとThread Aware LocaleというPTSDワードで思いだしたが、 誤解があるのようなので死人に口無しになる前に書いておく。

結果だけ見るといかにも

という意見を「彼が」主張したかのようにみえるけど、それは全然違うのよね。

そもそも彼の元々の主張は「glibc2なんかのダサいAPIなんか入れるなよ、俺が考えた最強のAPI *1ができるまでお前はコード触るな *2」だったのよ、そこから話が二転三転してるのでな…

そもそもuselocale(3)に反対と言いだしたのも私のプロポーザルから大分経過してからだったはず、最初はvaxのTLSサポートを理由にし、それが通らないとみるやどっかで聞きかじったFreeBSDでのctype性能問題の話を持ってきた *3しかも具体的な数値は一切出さずに。

「uselocale(3)以外はマージする」という結論は、膠着状態を仲裁すべく尽力してくださったcoreのyamtさんが引っ張り出した妥協点。それが「正しい」と感じたのであれば、yamtさんこそが称賛されるべき。 それだけははっきりと言っておきたい。

あと こっちも誤解やね。

ちゅーとこ。

*1:何度か個人メールでiconv(WCHAR_T)的なAPIを主張されたが、一行たりともコードは彼書いてなかったな、俺に書かせる気だったのかな…
*2:ワイの担当ワークエリアなのにそこで作業してはならないと、そのワークエリアの人間でもコアチームでも無い人間に言われて従うすじあいも無いんだけどね…
*3:そもそもプロポーザルの時点ではFreeBSDはまだ実装なかったからね

2017/05/23(Tue)

[音楽] The Smiths/Suffer Little Children

もう30年以上昔だけどイギリスのThe Smithsというバンドが「Suffer Little Children」という曲を書いた *1

この曲はかつてマンチェスターを震撼させた 連続殺人事件の被害者の少年少女について書かれたもので、犯人どころか被害者の実名まで登場する歌詞は悪趣味だと発表当初は大いに批判された。

この事件が起きたのはその当時からでも20年も前の話で今となっては半世紀も昔の事件なのだけども、数日前に犯人の最後の一人 イアン・ブレディが獄中死したとのニュースが流れ、そしてまだ存命である被害者の遺族、そして危うく殺されかけた生存者からのコメントが報道され、ああまだ終わっていない事件だったんだなぁと。

しょーもないSNSに毒された現代では大量殺人事件だろうが3日も経てば新しいニュースに埋もれ忘れ去ってしまうもんだけど、命を奪われた悲しみなんてのは当事者ならば50年どころの話でない苦しみなんだなという事に改めて気づかされた。

そういえばこの曲のサビ(といってもこの曲はサビらしいサビ無いんだけど)の歌詞には

Oh Manchester, So much to answer for

というフレーズが繰り返される部分がある、日本盤の歌詞カードではうろ覚えだけど

マンチェスターよ、お前は償うべきことが多過ぎる

という訳だったかと記憶している。

この翻訳について「なぜ犯人達でなくなぜマンチェスターが擬人化して責められるのか」という部分にひっかかりを感じて

おお(一部始終を見ていただろう)マンチェスター、お前は(事件の真実について)答えなければならないことが多過ぎる

のように「answer for」を「~の責めを負う」でなく「~について説明をする」と解釈してる人もみられる。

確かに歌詞の前後には未だ発見されない被害者がムーアの沼地から見つけてくれと呼びかけるセリフがあったり、この解釈も興味深い。

しかしマンチェスターが償わなければならないという解釈もそれはそれで妥当な気もしている、そのあたりの歴史についてはまたいずれ何か書こうかと思う。

そしてこの歌詞を書いた、今やイギリス史上最高の 皮肉屋詩人の一人として称賛されるまでになったThe SmithsのボーカルMorrisseyの誕生日である5月22日に何たる偶然か新たな悲劇が起きた、 自爆テロで22名が死亡、59名が負傷という痛ましいニュースだ。

この事件による被害者たちの苦しみはこれからまた50年以上は続くのだろう、今日はこの曲を聴いて寝る。

神による救いを自爆テロで得ようとする男の歌である、何という皮肉。

*1:この曲のタイトルで検索したら漫画「絶対可憐チルドレン」のエロ同人がヒットしたんですが、日本はこの手の検索汚染が酷過ぎませんかね…

2017/05/24(Wed)

[NetBSD] debugging sed(1) (その7)

先日のa\i\c\コマンドの実装、OpenGroupの sed仕様とかGNU sedの infoを読みながらどうしたら もっとも限りなく正解に近いCカップを実装できるか考えてラーラララーララ歌ってたんだけど、余計な昔語り書いたことでまたやる気が消し飛んだ。なので気分転換に他の簡単な作業をやってリハビリする。

ところでBSD sed(1)では正規表現中の文字クラスについて、libc regex(3)との差異を消し込む処理がcompile_ccl()という関数で実装されている。 この部分についてN HEADでは 前回も書いたけどchristosがおそらくfree BSDから持ってきたと思われるコードでまるっと置き換えたせいでデグっとるように見受けられる。

@@ -452,40 +443,32 @@ compile_ccl(char **sp, char *t)
 			for (c = *s; (*t = *s) != ']' || c != d; s++, t++)
 				if ((c = *s) == '\0')
 					return NULL;
-		} else if (*s == '\\' && s[1] == 'n')
-			    *t = '\n', s++;
+		}
 	return (*s == ']') ? *sp = ++s, ++t : NULL;
 }
 
...

この消えたコードはsedに渡された正規表現中に"\n"という文字列出現した場合、libc regex(3)が解釈できるように改行コード(通常は0xa)に置換するというコードなんだけど、そもそもPOSIX仕様においては

The special characters '.', '*', '[', and '\\' ( <period>, <asterisk>, <left-square-bracket>, and <backslash>, respectively)
shall lose their special meaning within a bracket expression.

とあって

よってこれまでのバグが修正されたといっても過言ではないんだな。

N6でのテスト結果

$ echo n | sed -e '/[\n]/c\
> unko'
n
$

はい[\n]は改行コードと解釈されてるので入力のnにマッチしてないね。

同様のコードをいまのN HEADと同じコードと思われるFreeBSD 11で試すと

$ echo n | sed -e '/[\n]/c\
> unko'
unko
$

となって正しくnとマッチしてc\(change)が実行されました。

しかしですな、このN6までの挙動って由緒正しき伝統的なsed動作であって、UNIX v7 sedの ソースにおいても

		switch (c) {

		case '\\':
...
			if(c == 'n') {
				c = '\n';
			}
			goto defchar;

と特別扱いしてるんですよな、というか関係ないけど Ancient UNIXで参照できるソースコードいつの間にか増えてませんか…

そもそもPOSIX(Portable Operation System Interface [for uniX])やSUS(Single Unix Spec)ってのは、XPG(X/Open Portability Guide)から発展したものでこいつらは

の違いをまとめた移植性ガイド、よって両者共に違うってことはPOSIX自体の間違いなんだよね。

そして実装も普通は後方互換性を考えて挙動は変更しないんだけどね、この手の作業は過去の歴史的経緯を調べないヤツにやらせちゃダメよ。

ちなみにGNU sedも

$ echo n | sed -e '/[\n]/c\
> unko'
n
$

と伝統的なUNIX v7と同じ動作をする、そしてGNU sedのinfoの エスケープシーケンスの部分を読むと

 The list of these escapes is:
 
 \a
 	Produces or matches a BEL character, that is an “alert” (ASCII 7).
 
 \f
 	Produces or matches a form feed (ASCII 12).
 
 \n
 	Produces or matches a newline (ASCII 10).
 
 \r
 	Produces or matches a carriage return (ASCII 13).
 
 \t
 	Produces or matches a horizontal tab (ASCII 9).
 
 \v
 	Produces or matches a so called “vertical tab” (ASCII 11).
 
 \cx
 	Produces or matches CONTROL-x, where x is any character. The precise effect of ‘\cx’ is as follows: if x is a lower case letter, it is converted to upper case. Then bit 6 of the character (hex 40) is inverted. Thus ‘\cz’ becomes hex 1A, but ‘\c{’ becomes hex 3B, while ‘\c;’ becomes hex 7B.
 
 \dxxx
 	Produces or matches a character whose decimal ASCII value is xxx.
 
 \oxxx
 	Produces or matches a character whose octal ASCII value is xxx.
 
 \xxx
 	Produces or matches a character whose hexadecimal ASCII value is xx.
 
 ‘\b’ (backspace) was omitted because of the conflict with the existing “word boundary” meaning.

イッパイアルネー、わあい(しろめ)。

これらのエスケープは環境変数POSIXLY_COLLECTあるいは--posixオプションをつけることで回避できるそうな。

余談だけど、このPOSIXLY_COLLECTという環境変数はGNUプロダクツ全般に使われてて

    Many GNU programs suppress extensions that conflict with POSIX if the
 environment variable 'POSIXLY_CORRECT' is defined (even if it is defined
 with a null value).  Please make your program recognize this variable if
 appropriate.

と、POSIX自身にバグがあるとかGNUの特殊仕様との整合性がアレな場合、こいつで動作を切替することができるよという。

さっきも書いたけどXPGはそもそもSysVとBSDの為のもので、GNUの挙動なんてのは蚊帳の外でケアされてなかったんだよね、SysVもBSDももはや完全に死んでLinuxがデファクトである時代からすると隔世の感があるけども。

この辺の顛末は 笛吹き髭おじさんのインタビューあたりから辿ってどうぞ、昔はPOSIX_ME_HARDER(私をもっと強くPOSIXにして)というクソ皮肉な名前だったのには草生えますよ。

ちなみにNおいても、この手の明らかにPOSIX仕様自体がアレだったケースについて、POSIX_MISTAKEというのがありますが これは環境変数ではなくifdef用のプリプロセッサマクロですし、もう現存してるとこってlibc regexくらいだと思う。

ということでオレオレN6は伝統的な動作をデフォルトにする方がよさげ、あと\n以外のGNU拡張のエスケープは後方互換考えるとNGなんだけど どうせ世の中GNU sedがデファクトやろうし、どうせPOSIXLY_CORRECT実装すんなら入れちまえって気にもなってきた。

2017/05/25(Thu)

[音楽][映画] Talking Heads/Once In A Lifetime

先月の話に今更気づいたんだけど ジョナサン・デミ監督死去、一般的には「羊たちの沈黙」が有名だけど、自分はやっぱりこっちの映画「 ストップ・メイキング・センス」だな。

この曲のPVも傑作なのよね、デヴィッド・バーンのスーツ姿と痙攣ダンスは、自分がスーツ買う時かならず試着室で真似している。

歌詞もとてもいい、空の間違った側を飛び続けて墜落した男はやがて水の渦に飲まれて消えるのである。

日々をやり過ごせ
水の流れに身を任せよう
日々をやり過ごせ
やがて水は低きへと流れ
いつか跡形もなく消える
気がつくと財布はスッカラカン
人生はたった一度きり
覆水は盆には還らない

2017/05/26(Fri)

[映画][音楽] Grant Gee/Joy Division

これはマンチェスター出身のバンド、ジョイ・ディヴィジョンに起きた悲劇を追ったドキュメンタリー映画だ、自分はおよそ10年前(もうそんなに経つのか…)に渋谷の小劇場で上映かかったのをギリギリ最終日に観た。

彼らを襲った悲劇、それは成功を目前とした初のアメリカ・ツアーに出発の前夜に起きた。メンバーのイアン・カーティス(Vocal)は自宅で首吊り自殺を図り、その死によってバンドはそのまま終焉を迎えた。

この映画の後日談となる話だが、残されたメンバー達は生活の為に演奏を続ける他になく新しいバンドの名前もまだ決まらぬままステージで

僕の友達は今日来れませんでした、ここに居るのは這い回る混乱からの唯一の生き残りです(Our mates couldn't make it, we're the only surviving members of Crawling Chaos)

この悲劇をクトゥルフの邪神の渾名に例え、そしてイアンが残していった曲を歌った。

残った中で一番歌がマシなバーナード・サムナー(Guitar)がVocalも兼任することになったが音程とくにライブは壊滅的で、新バンドの最初の10年は「史上最も音痴なバンド」とまで称される事になる。

監督のグラント・ジーという人はU2やレディオヘッド(彼らも熱心なジョイ・ディヴィジョンのファンだ)のPVなどを手掛けた映像作家で、インタビュー場面ですらも気を抜かず映像と音響のクオリティは高い。バンドが活動していた70年代から80年代にかけてのマンチェスターという街を記録した8mmフィルムの映像を集めそして丹念に繋ぎ、消えた風景ををスクリーン上に再構築している。

ちなみにインタビューの内容そのものはこの前年に撮影された別の監督によるドキュメンタリー「 Shadowplayers」の方がより詳細な内容なんだけど、なにせ関係者の自宅でホームビデオ回しただけの体裁であってマニアにしかお勧めはできない。

同年にはやはり同じ悲劇を題材とした映画「 Control」が上映されている。

監督はアントン・コービン(こちらもやはりU2のPVで有名な映像作家で、これまでもジョイ・ディヴィジョンのPVも手掛けている)、面白いことに興行的リスクもあっただろうにこの映画をカラーでなくモノクロで撮影した。その理由として彼の記憶しているマンチェスターはモノクロだったからと述べている。また再現ドラマなので失われてしまった風景はよく似た別の場所で撮影されてるし、ドラマとしての脚色もある。ピーター・フック(Bass)曰く「95%事実だ」そうだ。

イギリス人であり自分も同時代を同じ場所で過ごしたグラント・ジー監督に対して、アントン・コービン監督はオランダ人の異邦人で写真家としてモノクロ写真のフレームに彼らを収めてきたの視点の違いがそこにはある、比べて鑑賞するとそこも面白い。

彼らバンドの歴史の中で、イアン・カーティス、そしてマネージャーのロブ・グレットン、プロデューサーのマーティン・ハネットといった仲間達を失ったように *1、マンチェスターという街もまた数多くの風景を失なった。

ドキュメンタリーの冒頭、スティーヴン・モリス(Drums)がマンチェスターについて「レンガ積みの住宅街がやがて瓦礫の山となり、コンクリートの要塞が作られ未来的に思えたけども、やがてそれもコンクリートが腐り醜悪になった」と語る場面、ここで映し出される映像はイアン・カーティスの実家であるビクトリア・パーク・エステートという集合住宅である。

Victoria Park Estate, Macclesfield

Victoria Park Estate, Macclesfield

ここもすでに取り壊された過去の風景であり、映画「コントロール」のオープニングは別の場所でロケしている *2

マンチェスターの建築家団体らが設計したこのレトロ近未来的な建物は、60~70年代のいわゆる プレハブ工法による巨大な集合住宅であり、マックルズフィールドという衰退した紡績の街に立ち並ぶ旧来のレンガ積みの低層住宅の中では異形の存在だったことは想像に難くない。

今の日本も不況による製造業の空洞化で、海外に逃げ出した工場の跡地にグロテスクなタワーマンションが雨後の筍のように建っている真っ最中。そして自分が育った街も日本の高度経済成長の最中に全国で判で押したようなデザインで作られたプレハブな集合住宅だから強いデジャヴュを感じるシーン。

それ以外にも数多くの「すでにない場所」がこのドキュメンタリーには記録されている。彼らが初めてライブをしたライブハウス「エレクトリック・サーカス」、デヴィッド・ボウイの曲からバンド名をとり「ワルシャワ」の名前で参加し、ネオナチバンドと危険視されるような発言をステージ上で飛ばし、時にはスキンヘッドとの乱闘にまで発展した。

そして自主制作のデビューシングル(音は最悪だ)を持ち込んだレコード店「ピップ」、そしてギグの依頼も無いままひたすら練習と曲を書き続けた練習スペース(彼らの代表曲ラブ・ウィル・テア・アス・アパートにも登場する)「リハーサルルーム」、同じパンクムーブメントのバンドでしのぎを削ったライブハウス「ラフターズ」などが次々と画面の登場する。

最も印象的なのは自主制作8mmフィルム「 No City Fun」の映像だろう、イベントで一度だけ流されただけでこれまで一度も流通に乗ったことはないようで、私も観たのは初めてだ。

このフィルムは同人誌「 City Fun」に寄せられた、パンクという音楽ムーブメントとマンチェスターという街の関係性について書かれた記事を元に、ジョイ・ディヴィジョンの曲をバックにマンチェスターの街並みの映像のコラージュが映し出される。

イギリスの産業革命はマンチェスターからはじまった、そして紡績産業が栄えたもののやがて不況で衰退してしまった。マンチェスターの名前が最後に新聞に載ったのは、1963年から1965年にかけて全英を恐怖に陥れた ムーアズ殺人事件くらいと揶揄されるくらいである( 先日の記事でも書いた)。

この事件の犯人のひとりで先日獄中死したイアン・ブレイディは文学趣味をきっかけにネオナチ思想に染まったという、これは不況下のマンチェスターでは珍しい話では無かったようだ。日本のインターネットでもちょいと石を投げればネットウヨサヨに当たるみたいなもんだ。

そもそもジョイ・ディヴィジョンの連中もバンド名からしてナチスの慰安施設を指す隠語だし、初めて作った自主制作レコード( An Ideal for Living)のジャケットはヒトラーユーゲントだし、一曲目「Warsaw」のオープニングの乱数カウントダウンはルドルフ・ヘスの囚人番号だという。

なのでモリッシーが(元相棒のジョニー・マーとは対照的に)インタビューで同郷のジョイ・ディヴィジョンに関する質問には、徹底して音楽的無関心と人格への不快感を示したのはこの初期のネオナチ趣味のせいだろうね。

またブレイディともうひとりの犯人ヒンドリーの屈折の原因としては幼少期の家庭内における虐待が挙げられるが、モリッシーは「残忍性は家庭で植え付けられる(Barbarism Begins At Home)」という曲を書いてるように、この凶行の原因をマンチェスターという環境に求めるだろうことは想像に難くない。

マンチェスターの不況と停滞、そして住人の心の荒廃という環境因子があったからこそ「Suffer Little Children」の歌詞で

マンチェスターよ、お前は償うべきことが多過ぎる(Oh Manchester, So much to answer for)

とモリッシーは歌ったのではないか。

そもそもイアン・カーティスが死を選んだのも、悪化する一方の持病のてんかん発作でバンド活動が困難になった事や経済的な苦境と結婚生活の冷え込みから自分の将来に悲観した為といわれている。

彼が最後にTVで観た番組はヴェルナー・ヘルツォークの映画「 シュトロツェクの不思議な旅」で、ストーリーは大道芸人がアメリカへ渡るも一文無しになり銀行強盗も失敗し自殺するという陰鬱な映画だ。 明日からアメリカツアーに出かける自分の姿の将来もこの大道芸人のような失敗になるのではないか?

そして彼はイギー・ポップのアルバム「イディオット(愚か者)」を流しながら縊死した、彼の死にマンチェスターは償うべきことはなかったのか?

話を戻そう、産業革命と連続殺人事件に続いて再びマンチェスターの名前を世界に知らしめることになったのは、このパンクを着火点として90年代に頂点を迎えた「マッドチェスター」「セカンド・サマー・オブ・ラブ」などと呼ばれる音楽×ドラッグの一大ムーブメントであり、前述の「City Fun」の記事はその未来を予見するものだった。

新しいバンドはイアンの死んだ翌日の陰鬱な気持ちをよりによって能天気なディスコビートに合わせて歌う「Blue Monday」の世界的なヒットで成功を手中にした。

そしてその稼ぎを全て注ぎ込んでマンチェスターが世界の音楽の中心となるきっかけの一つになったクラブ・ハシエンダの運営をはじめる、それはもうデタラメとしか言いようのないほどの放漫経営で。

その顛末は映画「 24 Hour Party People」が詳しいので省略するが、それは清々しいくらいの笑える破綻劇である。

ハシエンダはこの映画の撮影中に再開発業者によって取り壊され(解体工事の写真はニュー・オーダーを脱退したピーター・フックのEP「 1102|2011」のジャケットとして使われている)跡地は高級アパートメントになっている。

どんなムーブメントにも終わりがある、しかしその熱が去った後もマンチェスターは「音楽の街」として世界的な知名度を保ったままであり、もはやビートルズのリバプール、ストーンズのロンドンの陰に隠れることは無い。ハシエンダ閉店と入れ替るようにオープンしたマンチェスター・アリーナは2000年代において「最も忙しいコンサート会場」と言われるほどになった。

しかし今週まさにそのマンチェスター・アリーナを狙いムーアズ殺人事件すら霞んでしまうような大量殺人事件が発生し、再び世界中の新聞がマンチェスターの文字で埋まってしまった。

実行犯は両親はリビア移民の両親とマンチェスターで生まれ育った価値観の相違による軋轢と大学生活からの落伍によって過激な宗教思想へ傾倒と報道されている。 半世紀経ってもまた同じ理由だ。

またしてもマンチェスター、いや世界は償うべきことが多過ぎる。

*1:そしてこのドキュメンタリーから10年も経ち、インタビューに答えるレーベル経営者のトニー・ウィルソンとイアンの彼女アニーク・オノレも故人となってしまっている。
*2:劇中ではアビー・コートという名前になってるけどこれはロケ地そのままなのかな

2017/05/28(Sun)

[音楽] King Crimson/Heroes

おお、新生King CrimsonによるDavid Bowie/Heroesのカバーとは…

David BowieのライブだとギターのEarl Slickが EBow(日本だとE-Bow表記が多いかな)というギズモを使ってあの音の滝のようなフィードバック演奏を再現してるんだけど、やっぱりRobert Fripp本人が演奏するとアルバムと完全に同じ音が出るなぁ *1

この曲におけるRobert Frippのギター演奏については、プロデューサーのTonny Viscontiがマルチトラックを操作しながら解説する BBC ARTSの動画が詳しいです *2、あのフィードバックノイズは

という職人技なんだよね、ギターソロトラックだけ抜き出して聴かせてくれるの本当にありがたい。

ちなみに英語が不得手な方は、文章だけですがほぼ同じ内容のインタビューがリットーミュージック「サウンド & レコーディング・マガジン」の 【デヴィッド・ボウイ追悼】名曲「HEROES」の制作秘話で読めます。

あと先日これまでStation To Stationの限定ボックスに入ってた Live Nassau Coliseum '76の名演が独立したCDとしてリリースされたけど、今度は Live Los Angels '74がリリースされるのか、めでたい。

まぁ昔から同年のフィラデルフィア公演がアルバム「David Live」としてリリースされててセトリもほとんど一緒なんだけどね。とはいえマニア的には

という違いはあって、当時製作中だったものの結局お蔵入りにしたアルバム「The Gouster」に収録される予定だった曲の「It's Gonna Be Me」が聴けるあたりがポイントかな。 曲自体はRykodiscから再発されてた「Young Americans」のボーナストラックに入ってたし、先日出たボックスセット「Who Can I Be Now? (1974-1976)」でもリマスターされてはいるけど。

追悼商法という面もあるけど、これまでのRCA時代のボウイの音源ってEMIへ移管された後ほんとに酷い扱いだったことを考えるとほんと今はいい時期である、買う金ないのが問題だが(しろめ)。

ちなみにEMIのやらかしたクソな所業は覚えてる限りでこんだけある。

そらまぁジョニー・ロットンも「拝啓EMI殿」歌いますわな、こんな状態じゃEMIの経営は傾くのも当然だし「あの東芝」ですら提携止めて逃げ出したレベル。

その後EMIは金融危機やら詐欺騒動もあってめでたく解体の上でユニバーサルミュージックに買収され保有するカタログは傘下のパーロフォンに移ったものの、欧州独禁法の問題でパーロフォンごと売り出され現在はワーナー傘下にという流れ。

ワーナーは過去カタログは容赦なく時には中古対策に安売りし過ぎ(5CDセットで2000円とかおかしいやろ)と思うくらいには出し惜しみせず売るので、今後も期待したい。

*1:全般的にはリズム隊とくにベースのトニー・レヴィンの演奏がけっこう微妙やけど。
*2:一部を 翻訳したんだけどちょうどイーノとフリップの話の前で飽きてしまった…

2017/05/30(Tue)

[やきう] 交流戦

一昨年はポンセ屋舗、去年は平松遠藤斎藤だったけど、今年はなんと クルーンが来るンゴゴゴゴwww

TBSフロントとかいう衰えた佐々木というそびえたつピーに大金投じた挙句、ウッズやクルーンの年俸をケチって同一リーグに流出させる無能集団、ほんとトラウマですわ。