tohokuaikiのチラシの裏

技術的ネタとか。

PHPで最小限のls -laを書いてみた

オプションは、rauth1に対応で。

ファイルのパーミションは、PHPマニュアルをそのままコピペしたんだけど、手元だとシンボリックリンクがうまく出ない・・・・。

うーん、意外と面倒・・・・。つかコード長いし。

Ref:
最小限のls -laを書いてみた - Islands in the byte stream
404 Blog Not Found:perl - 勝手に添削 - 最小限のls -laを書いてみた

<?php
require_once 'Console/Getopt.php';

$shortoptions = "l|a|t|r|u|h|1";
$longoptions  = array('');

$options = Console_Getopt::getopt($_SERVER['argv'], $shortoptions, $longoptions);

if (is_a($opts, 'PEAR_Error')){
    error_log(sprintf("invalid option. %s", $opts->getMessage()));
    exit();
}

$opts = array();
foreach ($options[0] as $o){
    $opts[$o[0]] = $o[1];
}

$glob_exp = '*';
if ($options[1]){
    $target = $options[1][0];
    if (!file_exists($target)){
        error_log(sprintf("ls: %s: No such file or directory", $target));
        exit();
    }
    if (is_dir($target)){
        chdir($target);
    }
    else if (is_file($target)){
        $glob_exp = $target;
        if (in_array('a', array_keys($opts))){
            unset($opts['a']);
        }
    }
}

$files = array();
$padding = array(
    'nlink'  => 0,
    'user'  => 0,
    'group' => 0,
    'size' => 0,
    );

$glob = glob($glob_exp);
if (in_array('a', array_keys($opts))){
    $glob = array_merge($glob, glob('.*'));
}
foreach ($glob as $file){
    $key = $file;
    $stat = lstat($file);
    if (in_array('u', array_keys($opts))){
        $key = $stat['atime'];
    }
    else if (in_array('t', array_keys($opts))){
        $key = $stat['mtime'];
    }
    $stat['user']  = posix_getpwuid($stat['uid']);
    $stat['group'] = posix_getgrgid($stat['gid']);
    $stat['fmode'] = fmode($file);
    $stat['filename'] = $file . (is_dir($file) ? '/' : '');
    if (in_array('h', array_keys($opts))){
        $stat['size'] = fsize($stat['size']);
    }
    
    $files[$key] = $stat;
    
    if ($padding['user'] < strlen($stat['user']['name'])){
        $padding['user'] = strlen($stat['user']['name']);
    }
    if ($padding['group'] < strlen($stat['group']['name'])){
        $padding['group'] = strlen($stat['group']['name']);
    }
    if ($padding['size'] < strlen(strval($stat['size']))){
        $padding['size'] = strlen(strval($stat['size']));
    }
    if ($padding['nlink'] < strlen(strval($stat['nlink']))){
        $padding['nlink'] = strlen(strval($stat['nlink']));
    }
}

$sort_method = in_array('r', array_keys($opts)) ? 'ksort' : 'krsort';
$sort_method($files);

$formats = array(
    'short'  => "%s\t",
    'short1' => "%s\n",
    'long'   => "%s %{$padding['nlink']}s %-{$padding['user']}s %-{$padding['group']}s %{$padding['size']}s %s %5s %s\n"
    );

$now = time();

foreach ($files as $f) {
    if (in_array('l', array_keys($opts))){
        $params = array($formats['long'],
                        $f['fmode'],
                        $f['nlink'],
                        $f['user']['name'],
                        $f['group']['name'],
                        $f['size'],
                        date('m-d', $f['mtime']),
                        date( ($f['mtime'] > $now -86400*365 ? 'H:i' : 'Y'),  $f['mtime']),
                        $f['filename'],
                        );
    }
    else if (in_array('1', array_keys($opts))){
        $params = array(
            $formats['short1'],
            $f['filename']
            );
    }
    else {
        $params = array(
            $formats['short'],
            $f['filename']
            );
    }
    call_user_func_array('printf', $params);
}
if (!in_array('l', array_keys($opts)) && !in_array('1', array_keys($opts))){
    echo "\n";
}

/**
 * @brief filemode
 * @param int filesize
 * @retval string filesize
 */
function fmode($file)
{
    $info  = '';
    $perms = fileperms($file);

    if (($perms & 0xC000) == 0xC000) {
        // ソケット
        $info = 's';
    } elseif (($perms & 0xA000) == 0xA000) {
        // シンボリックリンク
        $info = 'l';
    } elseif (($perms & 0x8000) == 0x8000) {
        // 通常のファイル
        $info = '-';
    } elseif (($perms & 0x6000) == 0x6000) {
        // ブロックスペシャルファイル
        $info = 'b';
    } elseif (($perms & 0x4000) == 0x4000) {
        // ディレクトリ
        $info = 'd';
    } elseif (($perms & 0x2000) == 0x2000) {
        // キャラクタスペシャルファイル
        $info = 'c';
    } elseif (($perms & 0x1000) == 0x1000) {
        // FIFO パイプ
        $info = 'p';
    } else {
        // 不明
        $info = 'u';
    }

    // 所有者
    $info .= (($perms & 0x0100) ? 'r' : '-');
    $info .= (($perms & 0x0080) ? 'w' : '-');
    $info .= (($perms & 0x0040) ?
              (($perms & 0x0800) ? 's' : 'x' ) :
              (($perms & 0x0800) ? 'S' : '-'));

    // グループ
    $info .= (($perms & 0x0020) ? 'r' : '-');
    $info .= (($perms & 0x0010) ? 'w' : '-');
    $info .= (($perms & 0x0008) ?
              (($perms & 0x0400) ? 's' : 'x' ) :
              (($perms & 0x0400) ? 'S' : '-'));

    // 全体
    $info .= (($perms & 0x0004) ? 'r' : '-');
    $info .= (($perms & 0x0002) ? 'w' : '-');
    $info .= (($perms & 0x0001) ?
              (($perms & 0x0200) ? 't' : 'x' ) :
              (($perms & 0x0200) ? 'T' : '-'));

    return  $info;
}

/**
 * @brief filesizeを見やすく
 * @param int filesize
 * @retval string filesize
 */
function fsize($string)
{
    $size = intval($string);
    $unit = "";

    $units = array(
        1             => "",
        1000          => "K",
        1000000       => "M",
        1000000000    => "G",
        1000000000000 => "T",
        );

    foreach ($units as $key=>$value){
        if ($size/$key<1000){
            $s = sprintf("%f", $size/$key);
            return trim(substr($s, 0, 3),".") . $value;
            break;
        }
    }

    return $string;

}
[itoh@testserver test]$ php ~/work/ls.php -alth
drwxr-xr-x 32 smbuser smbuser 4.0K 05-17 12:08 ../
drwxr-xr-x  6 smbuser smbuser 4.0K 03-24 13:59 ./
drwxrwxr-x  2 smbuser smbuser 4.0K 03-24 12:43 ab/
-rw-r--r--  1 smbuser smbuser   59 03-08 17:56 index.php
-rw-rw-r--  1 smbuser smbuser  11K 01-10 12:57 40745_.jpg
-rw-r--r--  1 smbuser smbuser  671 01-10 12:56 jpeg_progressve_checker.php
-rw-r--r--  1 smbuser smbuser  10K 01-10 12:03 40745.jpg
-rw-rw-r--  1 smbuser smbuser  10K 01-05 20:51 44165.jpg
-rw-rw-r--  1 smbuser smbuser   58 12-01 22:01 createdb.sql
drwxrwxr-x  2 smbuser smbuser 4.0K 10-28 12:32 tmp/
-rw-r--r--  1 smbuser smbuser   80 10-28 12:32 smarty_test.tpl
-rw-r--r--  1 smbuser smbuser  494 10-28 12:32 smarty.php
-rw-rw-r--  1 smbuser smbuser   35 10-28 12:21 dump
-rw-r--r--  1 smbuser smbuser 1.1K 10-22  2009 test.php
-rw-r--r--  1 smbuser smbuser  677 10-21  2009 create_serial_num.sql
-rw-r--r--  1 smbuser smbuser  263 10-21  2009 create_serial_num.table.sql
-rw-r--r--  1 smbuser smbuser  351 10-21  2009 create_serial_num.php
-rw-r--r--  1 smbuser smbuser  135 07-21  2009 at_include.php
-rw-r--r--  1 smbuser smbuser  612 07-21  2009 vars.inc.php
-rw-r--r--  1 smbuser smbuser 6.1K 03-31  2009 .DS_Store
-rwx------  1 smbuser smbuser  599 11-05  2008 get.sh
[itoh@testserver test]$ ls -alth
合計 124K
drwxr-xr-x 32 smbuser smbuser 4.0K  517 12:08 ..
drwxr-xr-x  6 smbuser smbuser 4.0K  324 13:59 .
drwxrwxr-x  2 smbuser smbuser 4.0K  324 12:43 ab
-rw-r--r--  1 smbuser smbuser   59  38 17:56 index.php
-rw-rw-r--  1 smbuser smbuser  12K  110 12:57 40745_.jpg
-rw-r--r--  1 smbuser smbuser  671  110 12:56 jpeg_progressve_checker.php
-rw-r--r--  1 smbuser smbuser  11K  110 12:03 40745.jpg
-rw-rw-r--  1 smbuser smbuser  11K  15 20:51 44165.jpg
-rw-rw-r--  1 smbuser smbuser   58 121 22:01 createdb.sql
drwxrwxr-x  2 smbuser smbuser 4.0K 1028  2010 tmp
-rw-r--r--  1 smbuser smbuser   80 1028  2010 smarty_test.tpl
-rw-r--r--  1 smbuser smbuser  494 1028  2010 smarty.php
-rw-rw-r--  1 smbuser smbuser   35 1028  2010 dump
-rw-r--r--  1 smbuser smbuser 1.1K 1022  2009 test.php
-rw-r--r--  1 smbuser smbuser  677 1021  2009 create_serial_num.sql
-rw-r--r--  1 smbuser smbuser  263 1021  2009 create_serial_num.table.sql
-rw-r--r--  1 smbuser smbuser  351 1021  2009 create_serial_num.php
-rw-r--r--  1 smbuser smbuser  135  721  2009 at_include.php
-rw-r--r--  1 smbuser smbuser  612  721  2009 vars.inc.php
-rw-r--r--  1 smbuser smbuser 6.1K  331  2009 .DS_Store
-rwx------  1 smbuser smbuser  599 115  2008 get.sh

うーん、こんなもんかな。