任意のデータを表現するURL

RFC 2397 に規定される data:// から始まる URL を利用すると、任意のデータを URL として表現できます。

書式:
“data://” [MIME] [“;base64”] “,” data

たとえば

<a href=”https://www.ecoop.net/”>hello!</a>

という HTML テキストは data スキームを使うと以下のようになります。

data://text/html,%3Ca+href%3D%22http%3A%2F%2Fwww.ecoop.net%2F%22%3Ehello%21%3C%2Fa%3

また、base64 形式でも指定できます。

data://text/html;base64,PGEgaHJlZj0iaHR0cDovL3d3dy5lY29vcC5uZXQvIj5oZWxsbyE8L2E+

 
IMG タグのソースURLとして利用する例(RFC2397より引用)

   <IMG
   SRC=”data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAw
   AAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFz
   ByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSp
   a/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJl
   ZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uis
   F81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PH
   hhx4dbgYKAAA7″
   ALT=”Larry”>

テキストファイルだけでなく画像データや音声データなど、任意のデータファイルを表現することができます。ただし A タグのHREF属性で利用する場合など利用環境によって URIの文字列長制限がある場合もあるため、比較的小さいデータを表現するときに利用するのがいいでしょう。
 
参考:
RFC 2397 The “data” URL scheme

デッドロックのテスト用コード

test_lock.php:

<?php
error_reporting(E_ALL);
$f = array();
$f[0] = null;
$f[] = fopen(“lock_1”, “r+”);
$f[] = fopen(“lock_2”, “r+”);
 
if(@intval($_SERVER[‘argv’][1]) >= 2){
$task = array(2, 1);
}else{
$task = array(1 ,2);
}
 
foreach($task as $n){
echo “Locking $n … “;
 
echo flock($f[$n], LOCK_EX, $t=true)? “OK”: “Fail”;
echo “\n”;
echo “waiting some seconds…”;
sleep (5);
echo “OK\n”;
}
echo “Finished!\n”;

実行:

$ php test_lock.php &
$ php test_lock.php 2 &

 
ひとつのみ起動した場合 Finished! と表示されますが、上のように並列で実行した場合は正しく動作しなくなります。
 
手元の環境では FreeBSD 6.2 と Windows XP SP2 の場合はデッドロックして固まり、Linux 2.4.21-50.ELsmp の場合は 2番目のロックに fail し、デッドロックにはなりませんでした。
 
参考:
– flock のモード切替え時の挙動は環境によって異なる[2008-01-08-1]

flock のモード切替え時の挙動は環境によって異なる

flock() でロック獲得中にロック状態を切り替える(LOCK_SH から LOCK_EX にする、など)と、環境や状況によってロックが保持されない場合があります。
 
検証用コード:
A… ロック切り替えプロセス(keeper.php )

<?php
$f=fopen(“t.txt”,”r+”);
for($i = 0; $i < 10; $i++){
  // ロック切り替えまたは取得(LOCK_EX -> LOCK_SH)
  echo “shared($i)\n”;
  flock($f, LOCK_SH);
  sleep(3);
  // ロック切り替え(LOCK_SH -> LOCK_EX)
  echo “exclusive($i)\n”;
  flock($f, LOCK_EX);sleep(3);
}
echo “Done! the treasure was protected!\n”; // ここまで theif.php にロックがとられなければ期待通り。
?>

B…ロック奪取プロセス(thief.php)

<?php
$f=fopen(“t.txt”, “r+”);
flock($f, LOCK_EX); // ロック待ち
// ロック奪取成功
echo “lock obtained!\n”;
echo “im doing something, hehehe.”;
for($i=0;$i<10;$i++){
  sleep(1);
  echo “.”;
}
echo “\nfinished! bye ;P\n”;’
fclose($f); // ロック開放
?>

実行:

$ touch t.txt
$ nice php locker.php &
$ php thief.php

結果:
Windows XP SP2 + Cygwin + PHP 5.1.2 (FS: NTFS) … ロック保持されない(thief won)
Linux 2.4.21-50.ELsmp + PHP 4.4.6 (FS: ext3) … ロック保持される(keeper won)
FreeBSD 6.2 + PHP 5.2.5 (FS: ufs) … ロック保持される(keeper won)
 
Windows XP (FS: NTFS) はロック自体はできますが、LOCK_EX <-> LOCK_SH の切り替えでいったんロックが解除されるという挙動になりました。
 
なお、PHP マニュアルの flock() の項にもあるとおりファイルシステムが FAT の場合など同じ Windows でも環境によっては結果が変わるため、この結果を鵜呑みにせず必ずご自身の環境でテストしてください。


追記(2008-01-09):
ちなみに、PHP 5.2.5 でソースをたどってみたところ PHP の flock() は ext/standard/flock_compat.c で flock (2) ではなく fcntl (2) を使って実装されていました。Linux や BSD など fcntl でロック変換がアトミックにできる場合は問題なく動作しそうです。
なお Windows で、かつ fcntl がない(Cygwin など UNIX 擬似環境ビルドではない)場合は、 UNIX と互換性があるように LockFileEx() で実装されています。

tips – grep で前後の行も表示させるには

マッチ行直前の行を表示するには -B {行数}(または –before-context={行数})を、直後の行を表示するには -A {行数}(または –after-context={行数}) を指定します。
 
例: sh のマニュアル(manpage)から grep 検索する。
オプションなし(ただし -n は行番号を表示してわかりやすくするため付加しています)

$ man sh | grep -n UNIX
1378: A sh command, the Thompson shell, appeared in Version 1 AT&T UNIX. It
1379: was superseded in Version 7 AT&T UNIX by the Bourne shell, which inher-
1383: Bourne shell from AT&T System V.4 UNIX.

 
直前と直後 1 行ずつ表示

$ man sh | grep -n -A 1 -B 1 UNIX
1377-HISTORY
1378: A sh command, the Thompson shell, appeared in Version 1 AT&T UNIX. It
1379: was superseded in Version 7 AT&T UNIX by the Bourne shell, which inher-
1380- ited the name sh.

1382- This version of sh was rewritten in 1989 under the BSD license after the
1383: Bourne shell from AT&T System V.4 UNIX.
1384-

CVS 入出力 を支援する PEAR パッケージ

PHP5.1 以降には fgetcsv(), fputcsv() という関数が標準で提供されていますが、別の選択肢として PEAR でも File_CSV というクラスが提供されています。
 
このクラスは File パッケージに含まれています。

pear install File

 
■利用例(tests/parser.php を改変したもの)
ファイルから読み込む

<?php
require_once ‘File/CSV.php’;
 
$file = “test.csv”;
 
$conf = File_CSV::discoverFormat($file); // ファイルからデータ構造を判別(区切文字やクォーテーションなど)
while ($fields = File_CSV::read($file, $conf)) { // 取得したデータ構造を利用して行単位で読み取る
    print_r($fields);
}

ファイルから配列に読み込む

<?php
require_once ‘File/CSV.php’;
 
$file = “test.csv”;
 
$conf = File_CSV::discoverFormat($file); // ファイルからデータ構造を判別(区切文字やクォーテーションなど)
while ($fields = File_CSV::read($file, $conf)) { // 取得したデータ構造を利用して行単位で読み取る
    print_r($fields);
}

CSV ファイルの中身をすべて配列として読み込む

<?php
require_once ‘File/CSV.php’;
 
$file = “test.csv”;
 
$conf = File_CSV::discoverFormat($file); // ファイルからデータ構造を判別(区切文字やクォーテーションなど)
while ($fields = File_CSV::read($file, $conf)) { // 取得したデータ構造を利用して行単位で読み取る
    print_r($fields);
}

ファイルから配列に読み込む

<?php
require_once ‘File/CSV.php’;
 
$file = “test.csv”;
 
$conf = File_CSV::discoverFormat($file); // ファイルからデータ構造を判別(区切文字やクォーテーションなど)
$data = File_CSV::readAll($file, $conf); // 取得したデータ構造を利用し、配列として全て取得する。
var_dump($data);
<<
ファイルに書きこむ例:

<?php
require_once ‘File/CSV.php’;
 
$file = “test.csv”;
 
// 書き込む内容
$data = array(
array(“foo”, “boo”, “woo”),
array(“fo\”o”, “bo,o”, “woo”), // エスケープが必要な文字を含むレコード
);
// CSV 書式
$conf = array(
    ‘fields’ => count($data[0]), // カラム数
    ‘sep’ => “,”, // 区切り子
    ‘quote’ => ‘”‘, // 囲み文字
    // ‘header’ => false, // NULL 以外を指定すると, read 時に一行目をヘッダとみなして各要素のキーとして扱う
);
// 書きこみ実行
foreach($data as $row) {
    File_CSV::write($file, $row, $conf); //
}

 
ファイルの書きこみ制御ができなかったり、設計上気になる点もありますが、
単純に読み書きするのには十分そうです。

FreeBSD で FUSE

FreeBSD 6 で仮想ファイルシステム(VFS) を使いたくなり、ports から FUSE をインストールしました。 emulator/fuse ではなく, sysutils/fusefs-kmod が正解。 FUSE 用のファイルシステムも sysutils-fusefs-* にあります。
 
参考:
Google SoC 2005からFUSE for FreeBSDなど – 若者に与えられた活躍の場

CUI 上の日本語表示/入力関係の ports

FreeBSD の CUI 環境から(X 一切なしで)コンソールで日本語入力ができないものか、と試行錯誤した結果次のような構成で落ち着きました。
  
OS: FreeBSD 6.2
日本語入力サーバ(Canna): japanese/canna (/etc/rc.conf で canna_enable=”YES” とする)
Canna 用辞書: japanese/cannadic (インストール方法は /usr/local/share/doc/cannadic/README.ja を参照)
日本語表示: japanese/kon2-16dot または japanese/kon2-14dot
日本語入力(UIM + Canna): japanese/uim-canna
  
CUI での日本語入力は kinput2 が主流ですが、X サーバが必要だったので今回は UIM を選択しました。
  
– その他あると便利なもの:

w3m: www/w3m-m17n
jless(less): japanese/jless
jvim (vi,vim): japanese/jvim3-canna
emcws (emacs): japanese/emacs-emcws

  
– 使い方:

$ kon
$ uim-fep
$ echo “ハローワールド”

とし、 canna[ C – R] のような文字列が下に表示されたら Ctrl-\ で日本語入力ができます。また、uim-fep は tty に依存しないのでリモートからも使えます。
 
ちなみに kon 以外の選択肢として, Linux の Framebuffer を用いた jfbtermがあり、FreeBSD 版も Yusuke Baba 氏により公開されています。)。
jfbterm と w3m-img for FreeBSD framebufferと組み合わせることで画像も表示できるようですが、jfbterm の画面切り替え時のオーバーヘッドに耐え切れず利用は断念しました。

cygwin で nice

nice / renice を使うと起動するプロセスや起動済のプロセスの優先度を変更することができます。
 
nice の書式:

nice [-n 数値 ] 実行するコマンド [引数] ..

例:

$ nice -n 10 sleep 100 # nice 10 sleep 100 は間違い

 
renice: の書式:

renice 数値 [ [-p] PID ] [ [-g] pgrps] [ [-u] ユーザ]

指定した PID (またはプロセスグループ(PGID)、ユーザ)のプロセスの優先度を指定した数値に基づいて変更します。
Cygwin で実行する場合、PID にはWindows 上の実 PID (WINPID)ではなく、Cygwin で ps すると見られる仮想 PID を指定します。
 
例1: PID で指定

$ sleep 50 &
[1] 1448
$ renice 10 -p 1448

例2: PGID で指定

$ ps
      PID PPID PGID WINPID TTY UID STIME COMMAND
     2264 2248 2264 316 con 11363 13:31:29 /usr/bin/sleep
     2168 2248 2168 2168 con 11363 13:31:33 /usr/bin/bash
     2948 2168 2168 2920 con 11363 13:31:33 /usr/bin/sleep
$ renice 19 -g 2168 # PGID が 2168 の全てのプロセスが対象

例3: ユーザ名で指定( cygwin 経由で起動しているプロセスのうち実行ユーザが john のプロセス全ての優先度を -10 (通常以上)にする)

$ renice -10 -u john

 
Windows XP で実行した場合のプロセス優先度の対応表:

nice -n

プロセス優先度

12以上

4 〜 11

通常以下

-4 〜 3

通常

-12 〜 -5

通常以上

-19 〜-13

-20 以下

リアルタイム

参考:
起動するプログラムの優先度を変更する方法(@IT)
タスクマネージャを使った変更方法と、cmd.exe の start コマンドを使った優先度指定方法についてかかれています。