#!/usr/bin/env perl my $copyright = <<'COPYRIGHT'; # Copyright 2021 by Christian Jaeger # Published under the same terms as perl itself COPYRIGHT use strict; use warnings FATAL => 'uninitialized'; use experimental 'signatures'; my ($email_full) = $copyright =~ / by ([^\n]*)/s; my ($mydir, $myname); BEGIN { $0 =~ /(.*?)([^\/]+)\z/s or die "?"; ($mydir, $myname) = ($1, $2); } sub usage { print STDERR map {"$_\n"} @_ if @_; print "$myname command [args] Print and read a Perl syntax file containing stat values of all files under dirpath, recursively: { \$path => [ lstat \$path ], ... } Does not follow symlinks when recursing. Commands: print-tree \$basedir prints the stat values at \$basedir to stdout. repl \$file... open a repl with the parsed \$file... in \@ts Options: --no-chdir By default, treestat uses chdir in print-tree then use '.' as the base folder name. This option turns that off. ($email_full) "; exit(@_ ? 1 : 0); } use Getopt::Long; our $verbose = 0; my $opt_no_chdir; #our $opt_dry; GetOptions( "verbose" => \$verbose, "help" => sub {usage}, "no-chdir" => \$opt_no_chdir, #"dry-run"=> \$opt_dry, ) or exit 1; usage unless @ARGV >= 1; use lib '/opt/functional-perl/lib'; # new stuff, or when fperl_noinstall use Chj::xperlfunc qw(xlstat xprintln xprint xgetfile_utf8 xchdir); use FP::IOStream qw(xdirectory_paths); use FP::Ops qw(string_cmp); use FP::List qw(cons null); use FP::Lazy; use FP::Stream ":all"; use Chj::singlequote qw(singlequote); use FP::Show; use FP::Repl; use JSON::PP qw(encode_json decode_json); sub tree_to_records ($dirpath, $tail) { lazy { xdirectory_paths($dirpath, \&string_cmp)->fold_right( sub ($path, $tail) { my $s = xlstat $path; cons([$path, [@$s]], $s->is_dir ? tree_to_records($path, $tail) : $tail) }, $tail ) } } sub print_tree ($dirpath) { my $base; if ($opt_no_chdir) { $base = $dirpath; } else { # Should we fork to scope the effect? xchdir $dirpath; $base = "."; } # Stream out JSON (should make a module for this!): binmode STDOUT, ":encoding(UTF-8)" or die "binmode: $!"; xprintln "{"; tree_to_records($base, null)->for_each_with_islast( sub ($record, $islast) { my ($path, $s_ary) = @$record; xprintln " ", encode_json($path), ": ", encode_json($s_ary), ($islast ? () : ","); } ); xprintln "}"; } sub load_json ($path) { decode_json xgetfile_utf8($path) } sub parse_treestat ($path) { my $hash = load_json($path); # turn values back into stat objects for (values %$hash) { @$_ == 13 or die "invalid array with other than 13 elements: " . show($_); bless $_, 'Chj::xperlfunc::xstat'; } $hash } sub trees_repl { my @ts = map { parse_treestat $_ } @_; repl; } my $command = shift @ARGV; my $proc = +{ "print-tree" => \&print_tree, "repl" => \&trees_repl, }->{$command} or usage "unknown command '$command'"; $proc->(@ARGV); #use Chj::ruse; #use Chj::Backtrace;