カテゴリー別アーカイブ: プログラミング

SourceTreeで日本語ファイル名を正しく扱う方法

SourceTreeは、MacOSX用のgitフロントエンド。
例に漏れずUTF8-MAC問題を抱えている。他OSで作成されたリポジトリに日本語ファイル名や、その他マルチバイト文字、アクセント記号付きアルファベット等を含むファイル名が含まれている場合、正しく扱うことができない。
それは、バックエンドのgitが古いバージョンだから。

この記事では設定方法を紹介する。

続きを読む SourceTreeで日本語ファイル名を正しく扱う方法

git 1.7.12でUTF8-MAC問題が解決

gitの唯一の弱点は日本語ファイル名に弱いことだ。
つい最近WindowsではUTF-8ファイル名に対応したが、MacではUTF8-MAC(UTF-8-MAC)問題という持病を抱えていた。
このため、WindowsやLinuxで濁点、半濁点とかの入った日本語ファイル名のファイルを含むリポジトリを作成し、Mac上にcloneすると、次の画像のようにこれらのファイルをリポジトリ内のファイルとして見なしてくれないと言う問題があった。

日本語ファイル名が使えなかった旧バージョンのgit
日本語ファイル名が使えなかった旧バージョンのgit

簡単に説明すると、ファイル名の見た目はLinux等と一緒だが、文字コード的に濁点の扱いが微妙に違うためである。ある意味、文字化けの類の問題である。この説明はかなり端折っていて不正確なので、技術的な詳細はこちらを参照して欲しい。

これまで、解決するにはnfsをマウントする方法や、パッチはあったが、本家では取り込まれていなかったし、誰もが諦め掛けていた。

しかし、2012/08/19。ついに、この問題が解決した!

続きを読む git 1.7.12でUTF8-MAC問題が解決

SendWinKey公開。Windows 8 向けチャームバー起動ショートカット等に

Windows 8ではチャームバーやスタート画面を開くためにマウスを画面の隅に持っていく必要がありますが、キーボードショートカットでも開くことができます。

Winキー = スタート画面
Winキー+C = チャームバー

しかし、Androidのサードパーティアプリなどからリモートデスクトップ接続するとき、このようなショートカットキーは入力しづらいということがあります。

そこで、Winキーと任意のキーを同時に押すキーストロークをシミュレートするだけの単純なプログラムを作成しました。
その名もWinKeySendです。

Windows 8なら動くと思います。

Vectorからダウンロード出来ます。
http://www.vector.co.jp/soft/winnt/util/se499006.html

ソースはこちらから。
https://bitbucket.org/butaman/sendwinkey

Ubuntu 11.10 64bitでInspircdをmakeしたときのエラーと脆弱性を回避する

普通に./configureとmakeとかすればいいのだけど、引っかかったところをメモ。

バージョンは、inspircd-1.2.8。
普通にコンパイルしようとすると以下のビルドエラーが出る。

        LINK:               inspircd
command_parse.o: In function `CommandParser::FindSym(void**, void*, std::stringconst&)':
command_parse.cpp:(.text+0x2a50): undefined reference to `dlsym'
command_parse.cpp:(.text+0x2a5c): undefined reference to `dlerror'
command_parse.o: In function `CommandParser::ReloadCommand(std::string, User*)':
command_parse.cpp:(.text+0x2d80): undefined reference to `dlclose'
command_parse.o: In function `CommandParser::LoadCommand(char const*)':
command_parse.cpp:(.text+0x3257): undefined reference to `dlopen'
command_parse.cpp:(.text+0x3271): undefined reference to `dlerror'
dynamic.o: In function `DLLManager::DLLManager(InspIRCd*, char const*)':
dynamic.cpp:(.text+0x68): undefined reference to `dlopen'
dynamic.cpp:(.text+0x82): undefined reference to `dlerror'
dynamic.o: In function `DLLManager::~DLLManager()':
dynamic.cpp:(.text+0xc9): undefined reference to `dlclose'
dynamic.o: In function `DLLManager::GetSymbol(void**, char const*)':
dynamic.cpp:(.text+0x130): undefined reference to `dlerror'
dynamic.cpp:(.text+0x147): undefined reference to `dlsym'
dynamic.cpp:(.text+0x153): undefined reference to `dlerror'
collect2: ld returned 1 exit status
make[1]: *** [inspircd] Error 1
make: *** [ircd] Error 2

./configure を実行後、以下のファイルを修正する必要がある。
./src/Makefike

修正前

        $(RUNCC) $(FLAGS)   -rdynamic -L. -o inspircd $(LDLIBS)  bancache.o base.o channelmanager.o channels.o cidr.o command_parse.o commands.o configreader.o cull_list.o dns.o dynamic.o filelogger.o hashcomp.o helperfuncs.o inspircd.o inspsocket.o inspstring.o listensocket.o logger.o mode.o modules.o server.o snomasks.o socket.o socketengine.o testsuite.o threadengine.o timer.o user_resolver.o usermanager.o userprocess.o users.o wildcard.o xline.o modes/modeclasses.a socketengine_epoll.o threadengine_pthread.o

修正後

        $(RUNCC) $(FLAGS)   -rdynamic -L. -o inspircd  bancache.o base.o channelmanager.o channels.o cidr.o command_parse.o commands.o configreader.o cull_list.o dns.o dynamic.o filelogger.o hashcomp.o helperfuncs.o inspircd.o inspsocket.o inspstring.o listensocket.o logger.o mode.o modules.o server.o snomasks.o socket.o socketengine.o testsuite.o threadengine.o timer.o user_resolver.o usermanager.o userprocess.o users.o wildcard.o xline.o modes/modeclasses.a socketengine_epoll.o threadengine_pthread.o $(LDLIBS) 

変わったところは、$(LDLIBS)の場所。
どうやら、gccの仕様変更で、リンクのコマンドオプションはオブジェクトよりも後にしなければならないらしい。
モジュールとかもその要領で変更する必要があるのがあるかも。なんか、LDLIBSに自分でライブラリ追加指定しても使ってなかったらリンクされないとかいう仕様になってるっぽいし、ちょっと不便。

あと、Inspircdには、セキュリティ上の問題点(脆弱性)が指摘されている。
JVNVU#212651 InspIRCd にメモリ破損の脆弱性

これについては、ここを参考に、./src/dns.cppを修正する必要がある。

修正前

                case DNS_QUERY_PTR:
                        o = 0;
                        q = 0;
                       while (q == 0 && i < length && o + 256 < 1023)

修正後

                case DNS_QUERY_PTR:
                        o = 0;
                        q = 0;
                       while (q == 0 && i >= 0 && i < length && o + 256 < 511)

iが0以下になったり、512以上になったりするということなのかな?

一週間が終わった。初めてのミスなど。

やっと終わった一週間。
でも土日休んだらまた仕事。つかれるね!

木曜日の昼のこと。
僕が作ったプログラム(というかエクセル)にミスが発覚し、いろいろ予定外の仕事が入ってしまった。
このミスというのが非常に単純で、エクセルのシートを修正するときに誤って合計セルに埋め込まれていた数式を消してしまったということ。何故消してしまったのかは記憶がない。
続きを読む 一週間が終わった。初めてのミスなど。

WordPressプラグインMT Style Post Nameの重複タグ問題を解決(WP3.2)

WordPressユーザーの間では有名な(たぶん)プラグインである「MT Style Post Name」。一応説明しておくと、これは、スラッグ(パーマリンク文字列)の生成をMTっぽくするプラグインである。

さて、これだけ有名なプラグインではあるが、古いためか、WP3系で使用すると、重大なバグが発生する。例えば、管理画面のJavascriptが誤動作するというバグや、全角文字のみのタグの場合投稿するたびに同名のタグがどんどん増殖してゆくというバグである。前者に対しては、カスタム投稿タイプでpermalink設定がうまくできないときの対策を無い知恵絞って考えた。 | Liglogでも対応が試みられている。本稿ではこのプログラムをさらに改良し、後者に対応してゆきたい。

本家のコードでは、単純に正規表現で全角文字を除去するだけの実装だ。一方、Liglogのコードでは、postidに置き換えられることを期待して、全角文字が含まれていた場合には問答無用で空文字を返却するという処理を行っている。確かに記事投稿の場合はこの処理で問題ない。しかし、タグやカテゴリのスラッグ生成にもここで使用されている関数が用いられているらしく、どうやらこのタグ登録時の処理に異常を来してしまうらしい。

実際にソースを確認したわけではないのだが、入力文字列から一意に決定されるスラッグ文字列を出力しなければ出力されるスラッグ文字列は入力文字列に対して1つに定まらなければ都合が悪いようだ。postidやtagidなどは可変であるため、同じ入力文字列でも異なるスラッグが出力されてしまう。このあたりの挙動が、重複タグ問題を起こしているらしい。

そこで、入力文字列に対して一意な文字列を出力すれば良いということになる。bin2hexを用いて文字列を16進数文字列化しても良いし、md5ハッシュを求めても良い。ただ、それでは桁数が増えすぎるため、今回はCRC32チェックサムを用いることにした。ソースコードを次に示す。

/*
* Plugin Name: MT Style Post Name Kai
* Description: MT Style Post Nameのタグ重複問題対応版。スラッグに強制的にタイトルのCRC32値を出力する
* Author: 449, Modified By Butaman-kun
* Plugin URI: http://pc10.2ch.net/test/read.cgi/blog/1163599919
* Version: 0.2.1
* */
add_filter('sanitize_title','sanitize_title_numalpha_only',9);
function sanitize_title_numalpha_only($title) {
    if(strlen($title) == 0){
        return '';//空文字の入力時は空文字を返す
    }elseif(mb_strlen($title) == strlen($title)){
        return $title;//2バイト文字が含まれていない場合はそのまま値を返す
    }else{
        $title_old = $title;

        //とりあえず2バイト文字を置換する処理
        $title = preg_replace('/[^%a-zA-Z0-9 \(\)_-]/', '-', $title);
        $title = preg_replace('/\-+/', '-', $title);
        $title = trim($title, '-');

        if(mb_strlen($title) == 0) {
            //何も残らなかったらCRC32のチェックサムに変換
            //(同じ入力に対して同じ出力になればいいのでmd5とかでも良い)
            $title = sprintf("%x", crc32($title_old));
        }
        return $title;
    }
}

さて、元の処理から正規表現による置換が増えたことが気になる方もおられるだろう。もともとのコードでは返却先で不正な文字列は除去されることを期待して、完璧なスラッグ文字列を出力していなかった。たとえば「あいう」という入力に対しては「—」と返却されていたのだ。しかし、これでは「すべてパーマリンクに使用出来ない文字(全角文字など)か」という判定を行うには都合が悪い。

そこで、2連続以上の連続ハイフンの除去、行頭、行末のハイフンの除去を行う処理を追加した。この時点で空文字になっていれば、それは入力文字列がすべて不正文字であったと判定できる(そう言う意味では、正規表現を使わずにハイフンを空文字に置換して判定しても良いかも知れない)。すべて全角であったと判定されたときに限り、CRC32チェックサムを返却する。

なお、入力文字列が空文字の場合のみ空文字を返却するようにしている。(さもなくば、0が返却されてしまうので)

パフォーマンスはあまり考慮していないコードだが、とりあえず適当にテストしたところ、うまく動いているようだ。

何か問題点があればご指摘願いたい。

追記 (2011.09.19)
掲載しているソースコードを差し替え。動作に変更はないが、置換処理の正規表現を減らした。

その後、ソースを確認してみた。結論から言うと、(1)タグやカテゴリのスラッグ生成ではPostの場合と異なり最初にterm_id(タグやカテゴリのID)が補完されるわけではないこと、(2)タグやカテゴリは名前ではなくスラッグによって重複確認がされていること、が誤動作の原因であった。

まず、wp-includes/formatting.php内のsanitize_title関数は、次の通りである。

/**
 * Sanitizes title or use fallback title.
 *
 * Specifically, HTML and PHP tags are stripped. Further actions can be added
 * via the plugin API. If $title is empty and $fallback_title is set, the latter
 * will be used.
 *
 * @since 1.0.0
 *
 * @param string $title The string to be sanitized.
 * @param string $fallback_title Optional. A title to use if $title is empty.
 * @param string $context Optional. The operation for which the string is sanitized
 * @return string The sanitized string.
 */
function sanitize_title($title, $fallback_title = '', $context = 'save') {
	$raw_title = $title;

	if ( 'save' == $context )
		$title = remove_accents($title);

	$title = apply_filters('sanitize_title', $title, $raw_title, $context);

	if ( '' === $title || false === $title )
		$title = $fallback_title;

	return $title;
}

これを見る限り、第1引数($title)の文字列を各フィルタ(sanitize_title_numalpha_only関数やsanitize_title_with_dashes関数など)に通した結果が空文字であれば、第2引数($fallback_title)の文字列を結果として返却するという実装になっているようだ。
次に、postのスラッグを生成する処理をwp-includes/post.phpから抜粋すると次のようになっている。

$post_name = sanitize_title($post_title, $post_ID);

したがって、sanitize_title_numalpha_only関数で空文字を返却すると、post_idがslugとして使用されるのだ。

一方で、タグやカテゴリの場合はどうなっているのだろうか。タグやカテゴリの追加処理を行うのは、wp-includes/taxonomy.php内のwp_insert_term関数のようだ。その中で、スラッグが未指定の場合は、sanitize_title関数を使用してスラッグを生成する処理がある。しかし、ここでは第2引数は指定されていない

	if ( empty($slug) )
		$slug = sanitize_title($name);

したがって、ここで$slugには空文字が代入されてしまう。これが後々誤動作を引き起こす。
次に$slugが使用されるのは次の箇所。

	if ( $term_id = term_exists($slug) ) {

ここで重複確認が行われているのだが、$slugが空の時は0になり、重複は存在しないと判断される。
このif文のelse句は以下のようになっている。

	} else {
		// This term does not exist at all in the database, Create it.
		$slug = wp_unique_term_slug($slug, (object) $args);
		if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) )
			return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
		$term_id = (int) $wpdb->insert_id;
	}

wp_unique_term_slug関数は、既に同じスラッグが存在していれば、ユニークになるように末尾に数字を付け足したりする関数なのだが、この場合は$slugは空文字でDB上には存在しないスラッグであり、なにもしないでそのまま空文字が返却される。この時点でまだ$slugは空文字である。ここで、スラッグが空のままデータベースに登録(ここで、増殖が起こってしまった)され、その時に振られたterm_idを取得している。

そして、次の部分でやっとこのことでスラッグにterm_idが入力される。

	// Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string.
	if ( empty($slug) ) {
		$slug = sanitize_title($slug, $term_id);
		do_action( 'edit_terms', $term_id );
		$wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
		do_action( 'edited_terms', $term_id );
	}

このときには既に増殖してしまった後なので、もはや後の祭りである。

ただ、不思議なのは、空文字を返さないようにすれば、上記のコードにもかかわらず正常に動作することだ。
タグ名やカテゴリ名が同じものが存在すれば、sanitize_title関数の結果とスラッグが異なっていても、重複したタグが生成されることはない。上記のコードの所々で空文字かどうかというのが判定基準に使われているところがあり、それが関係しているのだろう。
とりあえずうまく動いているのでこれ以上深追いはしないことにする。

追記(2011.09.20)
本文を少し手直し。