Skip to content

Commit c94a7dd

Browse files
committed
Add format feature
- Add --format option (custom report format) - Add --perms option (report file permissions) - Add --full option - Add --version option - Change namespace from Finalclap to Vincepare - Disable shortopts on legacy
1 parent df31b63 commit c94a7dd

File tree

9 files changed

+528
-139
lines changed

9 files changed

+528
-139
lines changed

README.md

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,60 @@ Example :
1010
dirscan --deep --same-device / > "drive-content.txt"
1111
```
1212

13-
DirScan is bundled with a text reporter, but you can customize its output by creating a custom reporting class, allowing to save data to, say, CSV, XML or an SQLite database.
13+
DirScan is bundled with a text reporter, but you can customize its output by creating your own reporting class, allowing to save data to, say, CSV, XML or an SQLite database.
1414

15-
### Download ###
15+
### Download
1616
Download dirscan.phar, make it executable (chmod +x dirscan.phar) and rename it if you want. On Linux, store it to `/usr/local/bin` to make it available everywhere :
1717

1818
```
19-
wget -O dirscan https://github.com/finalclap/DirScan/releases/download/1.0.0/dirscan.phar
19+
wget -O dirscan https://github.com/vincepare/DirScan/releases/download/1.1.0/dirscan.phar
2020
chmod +x dirscan
2121
sudo mv dirscan /usr/local/bin/dirscan
2222
```
2323

24-
### Requirement ###
25-
Tested on PHP 5.3, 5.4, 5.5 & 5.6. There is also a [legacy release](https://raw.githubusercontent.com/finalclap/DirScan/master/src/legacy/dirscan) that works on PHP 5.2 with some limitations.
24+
### Requirement
25+
Tested on PHP 5.3, 5.4, 5.5 & 5.6. There is also a [legacy release](https://raw.githubusercontent.com/vincepare/DirScan/master/src/legacy/dirscan) that works on PHP 5.2 with some limitations.
2626

27-
### Usage ###
27+
### Usage
2828
```
2929
Usage :
3030
dirscan [OPTIONS] TARGET
3131
3232
Options :
3333
--help, -h This help message
34+
--version, -v Print software version
3435
--deep, -d Explore symbolic links (default : skip)
3536
--flat, -f Do not explore subdirectories
36-
--access, -a Report access time
37-
--htime Report user friendly date nearby unix timestamps
3837
--same-device Explore only directories on the same device as the start directory
3938
Useful on Linux, to ignore special mount points like /sys or /proc
39+
--access, -a Report access time
40+
--htime, -t Report user friendly date nearby unix timestamps
41+
--perms, -p Report file permissions
42+
--full Report all properties
43+
--format=STRING Custom reporting format, call with empty string to print format help
44+
```
45+
46+
#### Formats
47+
Format tokens to customize output (`--format`) :
48+
```
49+
%u Unique path
50+
%t Type
51+
%s Size
52+
%c ctime
53+
%C Change time
54+
%m mtime
55+
%M Modify time
56+
%a atime
57+
%A Access time
58+
%p Permissions
59+
%o UID
60+
%O Owner
61+
%g GID
62+
%G Group
63+
%i Inode
64+
%e Extended
4065
```
4166

42-
### About windows ###
67+
### About windows
4368

4469
DirScan is designed to work a Unix environment (Linux or Mac OS) but you can also use it on Windows. In this case, beware of NTFS junction points and symbolic links that are not handled properly by old php releases (see [readlink](http://php.net/manual/en/function.readlink.php) & [is_link](http://php.net/manual/en/function.is-link.php)). But you'd better use other tools like [WhereIsIt](http://www.whereisit-soft.com/).

src/CliReporter.php

Lines changed: 187 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,46 @@
66
* @package DirScan
77
*/
88

9-
namespace Finalclap\DirScan;
9+
namespace Vincepare\DirScan;
1010

1111
class CliReporter extends Reporter
1212
{
1313
protected $access; // bool Report node access time (default false)
14-
protected $htime; // bool Display human readble date/time beside unix timestamp (default false)
15-
protected $typeMapping; // array Short name for file types
14+
protected $htime; // bool Print human readble date/time beside unix timestamp (default false)
15+
protected $perms; // bool Report file permissions (default false)
16+
protected $format; // string Custom output format
17+
protected $settings; // array Raw report settings
18+
19+
/**
20+
* Short name for file types
21+
*/
22+
public static $typeMapping = array(
23+
'dir' => 'd',
24+
'file' => 'f',
25+
'link' => 'l',
26+
);
27+
28+
/**
29+
* Properties mapping for custom format
30+
*/
31+
public static $propMapping = array(
32+
'%u' => 'Unique path',
33+
'%t' => 'Type',
34+
'%s' => 'Size',
35+
'%c' => 'ctime',
36+
'%C' => 'Change time',
37+
'%m' => 'mtime',
38+
'%M' => 'Modify time',
39+
'%a' => 'atime',
40+
'%A' => 'Access time',
41+
'%p' => 'Permissions',
42+
'%o' => 'UID',
43+
'%O' => 'Owner',
44+
'%g' => 'GID',
45+
'%G' => 'Group',
46+
'%i' => 'Inode',
47+
'%e' => 'Extended',
48+
);
1649

1750
/**
1851
* Set report settings
@@ -23,42 +56,102 @@ public function __construct($settings)
2356
{
2457
$this->access = isset($settings['access']) ? $settings['access'] : false;
2558
$this->htime = isset($settings['htime']) ? $settings['htime'] : false;
26-
$this->typeMapping = array(
27-
'dir' => 'd',
28-
'file' => 'f',
29-
'link' => 'l'
30-
);
59+
$this->perms = isset($settings['perms']) ? $settings['perms'] : false;
60+
$this->format = isset($settings['format']) ? $settings['format'] : null;
61+
$this->settings = $settings;
62+
63+
// Use format for full report
64+
if ($settings['full'] === true) {
65+
$this->format = implode(' ', array_keys(self::$propMapping));
66+
}
3167
}
3268

3369
/**
3470
* Print report header
3571
*
3672
* @param string $target Target directory
37-
* @param array $settings List of report settings
3873
* @param array $argv Command line arguments
3974
*/
40-
public function header($target, $settings, $argv)
75+
public function header($target, $argv)
4176
{
4277
$targetStat = DirScan::stat($target);
43-
44-
echo "time: ".time()."\n";
45-
echo "date: ".date('r')."\n";
78+
$header = !empty($this->format) ? $this->getRowFormatHeader($this->format) : $this->getRowHeader();
79+
echo "date: ".date('r (U)')."\n";
4680
echo "getenv(TZ): ".getenv('TZ')."\n";
4781
echo "date_default_timezone_get: ".date_default_timezone_get()."\n";
82+
if (defined('DIRSCAN_VERSION')) {
83+
echo "dirscan version: ".DIRSCAN_VERSION."\n";
84+
}
4885
echo "php version: ".phpversion()."\n";
86+
echo "uname: ".php_uname()."\n";
4987
echo "cwd: ".getcwd()."\n";
5088
echo "target: ".$target." (realpath: ".$targetStat['realpath'].")\n";
5189
echo "start device: ".$targetStat['dev']."\n";
52-
echo "settings: ".json_encode($settings)."\n";
90+
echo "settings: ".json_encode($this->settings)."\n";
5391
echo "argv: ".json_encode($argv)."\n";
5492
echo "=====================================\n";
93+
echo implode("\t", $header)."\n";
94+
}
95+
96+
/**
97+
* Print node data
98+
*
99+
* @param array $node Data returned by DirScan::stat
100+
* @param DirScan $scanner DirScan object
101+
*/
102+
public function push($node, DirScan $scanner)
103+
{
104+
$meta = $this->getMetadata($node, $scanner);
105+
$row = !empty($this->format) ? $this->getRowFormat($this->format, $node, $meta) : $this->getRow($node, $meta);
106+
echo implode("\t", $row)."\n";
107+
}
108+
109+
/**
110+
* Return metadata from a node
111+
*
112+
* @param array $node Data returned by DirScan::stat
113+
* @param DirScan $scanner DirScan object
114+
* @return array
115+
*/
116+
protected function getMetadata($node, DirScan $scanner)
117+
{
118+
$meta = array(
119+
'type' => isset(self::$typeMapping[$node['type']]) ? self::$typeMapping[$node['type']] : $node['type'],
120+
'perms' => substr(sprintf('%o', $node['mode']), -4),
121+
'hctime' => date('d/m/Y H:i:s', $node['ctime']),
122+
'hmtime' => date('d/m/Y H:i:s', $node['mtime']),
123+
'hatime' => date('d/m/Y H:i:s', $node['atime']),
124+
'extended' => array(),
125+
);
126+
127+
if (isset($node['target'])) {
128+
$meta['extended']['target'] = $node['target'];
129+
}
55130

131+
if ($node['dev'] != $scanner->startDevice) {
132+
$meta['extended']['device'] = $node['dev'];
133+
}
134+
135+
return $meta;
136+
}
137+
138+
/**
139+
* Return the header row array
140+
*
141+
* @return array
142+
*/
143+
protected function getRowHeader()
144+
{
56145
$header = array(
57146
'Unique path',
58147
'Type',
59148
'Size',
60149
);
61150

151+
if ($this->perms) {
152+
$header[] = 'Permissions';
153+
}
154+
62155
$header[] = 'ctime';
63156
if ($this->htime) {
64157
$header[] = 'Change time';
@@ -77,72 +170,122 @@ public function header($target, $settings, $argv)
77170
}
78171

79172
$header[] = 'Extended';
80-
echo implode("\t", $header)."\n";
173+
return $header;
81174
}
82175

83176
/**
84-
* Print node data
177+
* Return the header row array, using custom format
85178
*
86-
* @param array $node Data returned by DirScan::stat
87-
* @param DirScan $scanner DirScan object
179+
* @param string $format A format string used as row template
180+
* @return array
88181
*/
89-
public function push($node, DirScan $scanner)
182+
protected function getRowFormatHeader($format)
90183
{
91-
$type = isset($this->typeMapping[$node['type']]) ? $this->typeMapping[$node['type']] : $node['type'];
92-
$perms = substr(sprintf('%o', $node['mode']), -4);
93-
if ($this->htime) {
94-
$hctime = date('d/m/Y H:i:s', $node['ctime']);
95-
$hmtime = date('d/m/Y H:i:s', $node['mtime']);
96-
$hatime = date('d/m/Y H:i:s', $node['atime']);
97-
}
98-
99-
$extended = array();
100-
if (isset($node['target'])) {
101-
$extended['target'] = $node['target'];
102-
}
103-
$startDevice = $scanner->startDevice;
104-
if ($node['dev'] != $startDevice) {
105-
$extended['device'] = $node['dev'];
184+
$header = preg_split('# #', $format);
185+
foreach ($header as $key => $val) {
186+
$header[$key] = strtr($val, self::$propMapping);
106187
}
107-
188+
return $header;
189+
}
190+
191+
/**
192+
* Return the row array for a node
193+
*
194+
* @param array $node Data returned by DirScan::stat
195+
* @param array $meta Data returned by self::getMetadata
196+
* @return array
197+
*/
198+
protected function getRow($node, $meta)
199+
{
108200
$row = array(
109201
$node['uniquepath'],
110-
$type,
202+
$meta['type'],
111203
$node['size'],
112204
);
113205

206+
if ($this->perms) {
207+
$row[] = $meta['perms'];
208+
}
209+
114210
$row[] = $node['ctime'];
115211
if ($this->htime) {
116-
$row[] = $hctime;
212+
$row[] = $meta['hctime'];
117213
}
118214

119215
$row[] = $node['mtime'];
120216
if ($this->htime) {
121-
$row[] = $hmtime;
217+
$row[] = $meta['hmtime'];
122218
}
123219

124220
if ($this->access) {
125221
$row[] = $node['atime'];
126222
if ($this->htime) {
127-
$row[] = $hatime;
223+
$row[] = $meta['hatime'];
128224
}
129225
}
130226

131-
if (!empty($extended)) {
132-
$row[] = json_encode($extended);
227+
if (!empty($meta['extended'])) {
228+
$row[] = json_encode($meta['extended']);
133229
}
134230

135-
echo implode("\t", $row)."\n";
231+
return $row;
232+
}
233+
234+
/**
235+
* Return the row array for a node, using custom format
236+
*
237+
* @param string $format A format string used as row template
238+
* @param array $node Data returned by DirScan::stat
239+
* @param array $meta Data returned by self::getMetadata
240+
* @return array
241+
*/
242+
protected function getRowFormat($format, $node, $meta)
243+
{
244+
$statMapping = array(
245+
'%u' => $node['uniquepath'],
246+
'%t' => $meta['type'],
247+
'%s' => $node['size'],
248+
'%c' => $node['ctime'],
249+
'%C' => $meta['hctime'],
250+
'%m' => $node['mtime'],
251+
'%M' => $meta['hmtime'],
252+
'%a' => $node['atime'],
253+
'%A' => $meta['hatime'],
254+
'%p' => $meta['perms'],
255+
'%o' => $node['uid'],
256+
'%O' => $node['owner'],
257+
'%g' => $node['gid'],
258+
'%G' => $node['group'],
259+
'%i' => $node['ino'],
260+
'%e' => empty($meta['extended']) ? '' : json_encode($meta['extended']),
261+
);
262+
263+
$row = preg_split('# #', $format);
264+
foreach ($row as $key => $val) {
265+
$row[$key] = strtr($val, $statMapping);
266+
}
267+
268+
// Trim array
269+
$keys = array_reverse(array_keys($row));
270+
foreach ($keys as $key) {
271+
if (empty($row[$key])) {
272+
unset($row[$key]);
273+
continue;
274+
}
275+
break;
276+
}
277+
278+
return $row;
136279
}
137280

138281
/**
139-
* Print error messages on stderr
282+
* Print error messages to stderr
140283
*
141284
* @param string $msg Error message
142285
* @param int $code Error code
143286
*/
144287
public function error($msg, $code = null)
145288
{
146-
file_put_contents('php://stderr', $msg."\n");
289+
fwrite(STDERR, $msg.PHP_EOL);
147290
}
148291
}

src/DirScan.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* @package DirScan
77
*/
88

9-
namespace Finalclap\DirScan;
9+
namespace Vincepare\DirScan;
1010

1111
class DirScan
1212
{

0 commit comments

Comments
 (0)