最近仕事してない気がする… と怯えはじめたtadsan氏は飽きるまでプログラミングの練習に励むことにしたのだった…
これまでのあらすじ
cpコマンドってなんだっけ
コマンドライン引数のパースはめんどくさいので、例によって省略することにする。
# ファイルaをファイルbにコピーする
# (ファイルaは存在しないとエラー、bはなくても良いが、bがディレクトリだったらエラー)
% cp a b
# ファイルaとファイルbをディレクトリDにコピーする
# (ファイルaとbが存在しないとエラー、Dがディレクトリじゃないとエラー)
% cp a b D
ちょっぴりめんどくさいぞー?
実装
ファイルをコピーするためのcopy函数が失敗したときの保険のためにエラーハンドラーをセットしておく。個人的には禁じ手に近いのだけれど…
bootstrap.php
<?php
namespace zonuexe;
if (!function_exists('\zonuexe\exception_error_handler')) {
/**
* @link http://php.net/manual/functions.user-defined.php
*/
function exception_error_handler($severity, $message, $file, $line)
{
if (!(error_reporting() & $severity)) {
return;
}
throw new \ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler('\zonuexe\exception_error_handler');
}
あとは、ディレクトリを指定したときに./hoge/
と書いても./hoge
と書いても問題ないように正規化するやつ。
if (is_dir($dest)) {
$pathinfo = pathinfo($dest);
$dest = $pathinfo['dirname'] . '/' . $pathinfo['basename'];
}
完成
cp
#!/usr/bin/env php
<?php
namespace zonuexe\UnixCommand;
require_once dirname(__DIR__) . '/bootstrap.php';
/**
* CP command
*
* @author USAMI KENTA <tadsan@zonu.me>
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0
* @copyright 2015 USAMI Kenta
*/
if (realpath($_SERVER['SCRIPT_FILENAME']) == realpath(__FILE__)) {
$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : [];
array_shift($argv);
exit(cp($argv));
}
/**
* @return int UNIX status code
*/
function cp(array $argv)
{
$failed = false;
$len = count($argv);
if ($len === 0) {
fwrite(STDERR, 'cp: missing file operand' . PHP_EOL);
return 1;
}
$dest = array_pop($argv);
$is_dir = is_dir($dest);
if ($len === 1) {
$message = "cp: missing destination file operand after '{$dest}'";
fwrite(STDERR, $message . PHP_EOL);
return 1;
}
if (count($argv) > 1 && !$is_dir) {
$message = "cp: target '{$dest}' is not a directory";
fwrite(STDERR, $message . PHP_EOL);
return 1;
}
if ($is_dir) {
$pathinfo = pathinfo($dest);
$dest = $pathinfo['dirname'] . '/' . $pathinfo['basename'];
}
foreach ($argv as $f) {
if (is_dir($f)) {
$message = "cp: omitting directory '{$f}'";
} elseif (!is_file($f)) {
$message = "cp: cannot stat '{$f}': No such file or directory";
} elseif ($dest == $f) {
$message = "cp: '{$f}' and '{$f}' are the same file";
}
if ($is_dir) {
$new_file = $dest . '/' . basename($f);
} else {
$new_file = $dest;
}
if (!is_writable($dest)) {
$message = "cp: cannot create regular file '{$new_file}': Permission denied";
}
if (isset($message)) {
fwrite(STDERR, $message . PHP_EOL);
$failed = true;
continue;
}
try {
copy($f, $new_file);
} catch (\ErrorException $e) {
$message = preg_replace('@\Acopy@', 'cp', $e->getMessage());
fwrite(STDERR, $message . PHP_EOL);
$failed = true;
continue;
}
}
return $failed ? 1 : 0;
}