#!/usr/bin/perl
# 99aeabc9ec7fe80b1b39f5e53dc7e49e <- self-modifying Perl magic
# This is a self-modifying Perl file. I'm sorry you're viewing the source (it's
# really gnarly). If you're curious what it's made of, I recommend reading
# http://github.com/spencertipping/writing-self-modifying-perl.
#
# If you got one of these from someone and don't know what to do with it, send
# it to spencer@spencertipping.com and I'll see if I can figure out what it
# does.
# For the benefit of HTML viewers (this is hack):
#
$|++;
my %data;
my %transient;
my %externalized_functions;
my %datatypes;
my %locations; # Maps eval-numbers to attribute names
sub meta::define_form {
my ($namespace, $delegate) = @_;
$datatypes{$namespace} = $delegate;
*{"meta::${namespace}::implementation"} = $delegate;
*{"meta::$namespace"} = sub {
my ($name, $value, %options) = @_;
chomp $value;
$data{"${namespace}::$name"} = $value unless $options{no_binding};
&$delegate($name, $value) unless $options{no_delegate}}}
sub meta::eval_in {
my ($what, $where) = @_;
# Obtain next eval-number and alias it to the designated location
@locations{eval('__FILE__') =~ /\(eval (\d+)\)/} = ($where);
my $result = eval $what;
$@ =~ s/\(eval \d+\)/$where/ if $@;
warn $@ if $@;
$result}
meta::define_form 'meta', sub {
my ($name, $value) = @_;
meta::eval_in($value, "meta::$name")};
meta::meta('configure', <<'__');
# A function to configure transients. Transients can be used to store any number of
# different things, but one of the more common usages is type descriptors.
sub meta::configure {
my ($datatype, %options) = @_;
$transient{$_}{$datatype} = $options{$_} for keys %options;
}
__
meta::meta('externalize', <<'__');
# Function externalization. Data types should call this method when defining a function
# that has an external interface.
sub meta::externalize {
my ($name, $attribute, $implementation) = @_;
my $escaped = $name;
$escaped =~ s/[^A-Za-z0-9:]/_/go;
$externalized_functions{$name} = $externalized_functions{$escaped} = $attribute;
*{"::$name"} = *{"::$escaped"} = $implementation || $attribute;
}
__
meta::meta('functor::editable', <<'__');
# An editable type. This creates a type whose default action is to open an editor
# on whichever value is mentioned. This can be changed using different flags.
sub meta::functor::editable {
my ($typename, %options) = @_;
meta::configure $typename, %options;
meta::define_form $typename, sub {
my ($name, $value) = @_;
$options{on_bind} && &{$options{on_bind}}($name, $value);
meta::externalize $options{prefix} . $name, "${typename}::$name", sub {
my $attribute = "${typename}::$name";
my ($command, @new_value) = @_;
return &{$options{default}}(retrieve($attribute)) if ref $options{default} eq 'CODE' and not defined $command;
return edit($attribute) if $command eq 'edit' or $options{default} eq 'edit' and not defined $command;
return associate($attribute, @new_value ? join(' ', @new_value) : join('', )) if $command eq '=' or $command eq 'import' or $options{default} eq 'import' and not defined $command;
return retrieve($attribute)}}}
__
meta::meta('type::alias', <<'__');
meta::configure 'alias', inherit => 0;
meta::define_form 'alias', sub {
my ($name, $value) = @_;
meta::externalize $name, "alias::$name", sub {
# Can't pre-tokenize because shell::tokenize doesn't exist until the library::
# namespace has been evaluated (which will be after alias::).
shell::run(shell::tokenize($value), shell::tokenize(@_));
};
};
__
meta::meta('type::bootstrap', <<'__');
# Bootstrap attributes don't get executed. The reason for this is that because
# they are serialized directly into the header of the file (and later duplicated
# as regular data attributes), they will have already been executed when the
# file is loaded.
meta::configure 'bootstrap', extension => '.pl', inherit => 1;
meta::define_form 'bootstrap', sub {};
__
meta::meta('type::cache', <<'__');
meta::configure 'cache', inherit => 0;
meta::define_form 'cache', \&meta::bootstrap::implementation;
__
meta::meta('type::data', 'meta::functor::editable \'data\', extension => \'\', inherit => 0, default => \'cat\';');
meta::meta('type::function', <<'__');
meta::configure 'function', extension => '.pl', inherit => 1;
meta::define_form 'function', sub {
my ($name, $value) = @_;
meta::externalize $name, "function::$name", meta::eval_in("sub {\n$value\n}", "function::$name");
};
__
meta::meta('type::hook', <<'__');
meta::configure 'hook', extension => '.pl', inherit => 0;
meta::define_form 'hook', sub {
my ($name, $value) = @_;
*{"hook::$name"} = meta::eval_in("sub {\n$value\n}", "hook::$name");
};
__
meta::meta('type::inc', <<'__');
meta::configure 'inc', inherit => 1, extension => '.pl';
meta::define_form 'inc', sub {
use File::Path 'mkpath';
use File::Basename qw/basename dirname/;
my ($name, $value) = @_;
my $tmpdir = basename($0) . '-' . $$;
my $filename = "/tmp/$tmpdir/$name";
push @INC, "/tmp/$tmpdir" unless grep /^\/tmp\/$tmpdir$/, @INC;
mkpath(dirname($filename));
unless (-e $filename) {
open my $fh, '>', $filename;
print $fh $value;
close $fh;
}
};
__
meta::meta('type::indicator', <<'__');
# Shell indicator function. The output of each of these is automatically
# appended to the shell prompt.
meta::configure 'indicator', inherit => 1, extension => '.pl';
meta::define_form 'indicator', sub {
my ($name, $value) = @_;
*{"indicator::$name"} = meta::eval_in("sub {\n$value\n}", "indicator::$name");
};
__
meta::meta('type::internal_function', <<'__');
meta::configure 'internal_function', extension => '.pl', inherit => 1;
meta::define_form 'internal_function', sub {
my ($name, $value) = @_;
*{$name} = meta::eval_in("sub {\n$value\n}", "internal_function::$name");
};
__
meta::meta('type::library', <<'__');
meta::configure 'library', extension => '.pl', inherit => 1;
meta::define_form 'library', sub {
my ($name, $value) = @_;
meta::eval_in($value, "library::$name");
};
__
meta::meta('type::message_color', <<'__');
meta::configure 'message_color', extension => '', inherit => 1;
meta::define_form 'message_color', sub {
my ($name, $value) = @_;
terminal::color($name, $value);
};
__
meta::meta('type::meta', <<'__');
# This doesn't define a new type. It customizes the existing 'meta' type
# defined in bootstrap::initialization. Note that horrible things will
# happen if you redefine it using the editable functor.
meta::configure 'meta', extension => '.pl', inherit => 1;
__
meta::meta('type::note', 'meta::functor::editable \'note\', extension => \'.sdoc\', inherit => 0, default => \'edit\';');
meta::meta('type::parent', <<'__');
meta::define_form 'parent', \&meta::bootstrap::implementation;
meta::configure 'parent', extension => '', inherit => 1;
__
meta::meta('type::retriever', <<'__');
meta::configure 'retriever', extension => '.pl', inherit => 1;
meta::define_form 'retriever', sub {
my ($name, $value) = @_;
$transient{retrievers}{$name} = meta::eval_in("sub {\n$value\n}", "retriever::$name");
};
__
meta::meta('type::state', <<'__');
# Allows temporary or long-term storage of states. Nothing particularly insightful
# is done about compression, so storing alternative states will cause a large
# increase in size. Also, states don't contain other states -- otherwise the size
# increase would be exponential.
# States are created with the save-state function.
meta::configure 'state', inherit => 0, extension => '.pl';
meta::define_form 'state', \&meta::bootstrap::implementation;
__
meta::meta('type::watch', 'meta::functor::editable \'watch\', prefix => \'watch::\', inherit => 1, extension => \'.pl\', default => \'cat\';');
meta::bootstrap('html', <<'__');
__
meta::bootstrap('initialization', <<'__');
#!/usr/bin/perl
# 99aeabc9ec7fe80b1b39f5e53dc7e49e <- self-modifying Perl magic
# This is a self-modifying Perl file. I'm sorry you're viewing the source (it's
# really gnarly). If you're curious what it's made of, I recommend reading
# http://github.com/spencertipping/writing-self-modifying-perl.
#
# If you got one of these from someone and don't know what to do with it, send
# it to spencer@spencertipping.com and I'll see if I can figure out what it
# does.
# For the benefit of HTML viewers (this is hack):
#
$|++;
my %data;
my %transient;
my %externalized_functions;
my %datatypes;
my %locations; # Maps eval-numbers to attribute names
sub meta::define_form {
my ($namespace, $delegate) = @_;
$datatypes{$namespace} = $delegate;
*{"meta::${namespace}::implementation"} = $delegate;
*{"meta::$namespace"} = sub {
my ($name, $value, %options) = @_;
chomp $value;
$data{"${namespace}::$name"} = $value unless $options{no_binding};
&$delegate($name, $value) unless $options{no_delegate}}}
sub meta::eval_in {
my ($what, $where) = @_;
# Obtain next eval-number and alias it to the designated location
@locations{eval('__FILE__') =~ /\(eval (\d+)\)/} = ($where);
my $result = eval $what;
$@ =~ s/\(eval \d+\)/$where/ if $@;
warn $@ if $@;
$result}
meta::define_form 'meta', sub {
my ($name, $value) = @_;
meta::eval_in($value, "meta::$name")};
__
meta::bootstrap('perldoc', <<'__');
=head1 Self-modifying Perl script
=head2 Original implementation by Spencer Tipping L
The prototype for this script is licensed under the terms of the MIT source code license.
However, this script in particular may be under different licensing terms. To find out how
this script is licensed, please contact whoever sent it to you. Alternatively, you may
run it with the 'license' argument if they have specified a license that way.
You should not edit this file directly. For information about how it was constructed, go
to L. For quick usage guidelines,
run this script with the 'usage' argument.
=cut
__
meta::cache('parent-identification', <<'__');
/home/spencertipping/bin/notes a9e5975593ed5d90d943ad98405c71e5
object 99aeabc9ec7fe80b1b39f5e53dc7e49e
__
meta::data('author', 'Spencer Tipping');
meta::data('default-action', 'queue');
meta::data('license', <<'__');
MIT License
Copyright (c) 2010 Spencer Tipping
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
__
meta::data('permanent-identity', '2be21c5404f946ec5887e93fc3e452e0');
meta::data('watching', '1');
meta::function('ad', <<'__');
return @{$transient{path}} = () unless @_;
push @{$transient{path}}, @_;
__
meta::function('alias', <<'__');
my ($name, @stuff) = @_;
@_ ? @stuff ? around_hook('alias', @_, sub {associate("alias::$name", join(' ', @stuff), execute => 1)})
: retrieve("alias::$name") // "Undefined alias $name"
: table_display([select_keys('--namespace' => 'alias')], [map retrieve($_), select_keys('--namespace' => 'alias')]);
__
meta::function('cat', 'join "\\n", retrieve(@_);');
meta::function('cc', <<'__');
# Stashes a quick one-line continuation. (Used to remind me what I was doing.)
@_ ? associate('data::current-continuation', hook('set-cc', join(' ', @_))) : retrieve('data::current-continuation');
__
meta::function('ccc', 'rm(\'data::current-continuation\');');
meta::function('child', <<'__');
around_hook('child', @_, sub {
my ($child_name) = @_;
clone($child_name);
enable();
qx($child_name update-from $0 -n);
disable()});
__
meta::function('clone', <<'__');
for (grep length, @_) {
around_hook('clone', $_, sub {
hypothetically(sub {
rm('data::permanent-identity');
file::write($_, serialize(), noclobber => 1);
chmod(0700, $_)})})}
__
meta::function('cp', <<'__');
my $from = shift @_;
my $value = retrieve($from);
associate($_, $value) for @_;
__
meta::function('create', <<'__');
my ($name, $value) = @_;
around_hook('create', $name, $value, sub {
return edit($name) if exists $data{$name};
associate($name, defined $value ? $value : '');
edit($name) unless defined $value});
__
meta::function('current-state', 'serialize(\'-pS\');');
meta::function('disable', 'hook(\'disable\', chmod_self(sub {$_[0] & 0666}));');
meta::function('edit', <<'__');
my ($name, %options) = @_;
my $extension = extension_for($name);
die "$name is virtual or does not exist" unless exists $data{$name};
die "$name is inherited; use 'edit $name -f' to edit anyway" unless is($name, '-u') || is($name, '-d') || exists $options{'-f'};
around_hook('edit', @_, sub {
associate($name, invoke_editor_on($data{$name} // '', %options, attribute => $name, extension => $extension), execute => 1)});
save() unless $data{'data::edit::no-save'};
'';
__
meta::function('enable', 'hook(\'enable\', chmod_self(sub {$_[0] | $_[0] >> 2}));');
meta::function('export', <<'__');
# Exports data into a text file.
# export attr1 attr2 attr3 ... file.txt
my $name = pop @_;
@_ or die 'Expected filename';
file::write($name, join "\n", retrieve(@_));
__
meta::function('extern', '&{$_[0]}(retrieve(@_[1 .. $#_]));');
meta::function('grep', <<'__');
# Looks through attributes for a pattern. Usage is grep pattern [options], where
# [options] is the format as provided to select_keys.
my ($pattern, @args) = @_;
my ($options, @criteria) = separate_options(@args);
my @attributes = select_keys(%$options, '--criteria' => join('|', @criteria));
$pattern = qr/$pattern/;
my @m_attributes;
my @m_line_numbers;
my @m_lines;
for my $k (@attributes) {
next unless length $k;
my @lines = split /\n/, retrieve($k);
for (0 .. $#lines) {
next unless $lines[$_] =~ $pattern;
push @m_attributes, $k;
push @m_line_numbers, $_ + 1;
push @m_lines, '' . ($lines[$_] // '')}}
unless ($$options{'-C'}) {
s/($pattern)/\033[1;31m\1\033[0;0m/g for @m_lines;
s/^/\033[1;34m/o for @m_attributes;
s/^/\033[1;32m/o && s/$/\033[0;0m/o for @m_line_numbers}
table_display([@m_attributes], [@m_line_numbers], [@m_lines]);
__
meta::function('hash', 'fast_hash(@_);');
meta::function('hook', <<'__');
my ($hook, @args) = @_;
$transient{active_hooks}{$hook} = 1;
dangerous('', sub {&$_(@args)}) for grep /^hook::${hook}::/, sort keys %data;
@args;
__
meta::function('hooks', 'join "\\n", sort keys %{$transient{active_hooks}};');
meta::function('identity', 'retrieve(\'data::permanent-identity\') || associate(\'data::permanent-identity\', fast_hash(rand() . name() . serialize()));');
meta::function('import', <<'__');
my $name = pop @_;
associate($name, @_ ? join('', map(file::read($_), @_)) : join('', ));
__
meta::function('import-bundle', <<'__');
eval join '', ;
die $@ if $@;
__
meta::function('initial-state', '$transient{initial};');
meta::function('is', <<'__');
my ($attribute, @criteria) = @_;
my ($options, @stuff) = separate_options(@criteria);
exists $data{$attribute} and attribute_is($attribute, %$options);
__
meta::function('load-state', <<'__');
around_hook('load-state', @_, sub {
my ($state_name) = @_;
my $state = retrieve("state::$state_name");
terminal::state('saving current state into _...');
save_state('_');
delete $data{$_} for grep ! /^state::/, keys %data;
%externalized_functions = ();
terminal::state("restoring state $state_name...");
meta::eval_in($state, "state::$state_name");
terminal::error(hook('load-state-failed', $@)) if $@;
reload();
verify()});
__
meta::function('lock', 'hook(\'lock\', chmod_self(sub {$_[0] & 0555}));');
meta::function('ls', <<'__');
my ($options, @criteria) = separate_options(@_);
my ($external, $shadows, $sizes, $flags, $long, $hashes, $parent_hashes) = @$options{qw(-e -s -z -f -l -h -p)};
$sizes = $flags = $hashes = $parent_hashes = 1 if $long;
return table_display([grep ! exists $data{$externalized_functions{$_}}, sort keys %externalized_functions]) if $shadows;
my $criteria = join('|', @criteria);
my @definitions = select_keys('--criteria' => $criteria, '--path' => $transient{path}, %$options);
my %inverses = map {$externalized_functions{$_} => $_} keys %externalized_functions;
my @externals = map $inverses{$_}, grep length, @definitions;
my @internals = grep length $inverses{$_}, @definitions;
my @sizes = map sprintf('%6d %6d', length(serialize_single($_)), length(retrieve($_))), @{$external ? \@internals : \@definitions} if $sizes;
my @flags = map {my $k = $_; join '', map(is($k, "-$_") ? $_ : '-', qw(d i m u))} @definitions if $flags;
my @hashes = map fast_hash(retrieve($_)), @definitions if $hashes;
my %inherited = parent_attributes(grep /^parent::/o, keys %data) if $parent_hashes;
my @parent_hashes = map $inherited{$_} || '-', @definitions if $parent_hashes;
join "\n", map strip($_), split /\n/, table_display($external ? [grep length, @externals] : [@definitions],
$sizes ? ([@sizes]) : (), $flags ? ([@flags]) : (), $hashes ? ([@hashes]) : (), $parent_hashes ? ([@parent_hashes]) : ());
__
meta::function('ls-a', 'ls(\'-ad\', @_);');
meta::function('mv', <<'__');
my ($from, $to) = @_;
die "'$from' does not exist" unless exists $data{$from};
associate($to, retrieve($from));
rm($from);
__
meta::function('name', <<'__');
my $name = $0;
$name =~ s/^.*\///;
$name;
__
meta::function('note', <<'__');
# Creates a note with a given name, useful for jotting things down.
create("note::$_[0]");
__
meta::function('notes', 'ls(\'-a\', \'^note::\');');
meta::function('parents', 'join "\\n", grep s/^parent:://o, sort keys %data;');
meta::function('perl', <<'__');
my $result = eval(join ' ', @_);
$@ ? terminal::error($@) : $result;
__
meta::function('rd', <<'__');
if (@_) {my $pattern = join '|', @_;
@{$transient{path}} = grep $_ !~ /^$pattern$/, @{$transient{path}}}
else {pop @{$transient{path}}}
__
meta::function('reload', 'around_hook(\'reload\', sub {execute($_) for grep ! /^bootstrap::/, keys %data});');
meta::function('rm', <<'__');
around_hook('rm', @_, sub {
exists $data{$_} or terminal::warning("$_ does not exist") for @_;
delete @data{@_}});
__
meta::function('rmparent', <<'__');
# Removes one or more parents.
my ($options, @parents) = separate_options(@_);
my $clobber_divergent = $$options{'-D'} || $$options{'--clobber-divergent'};
my %parents = map {$_ => 1} @parents;
my @other_parents = grep !$parents{$_}, grep s/^parent:://, select_keys('--namespace' => 'parent');
my %kept_by_another_parent;
$kept_by_another_parent{$_} = 1 for grep s/^(\S+)\s.*$/\1/, split /\n/o, cat(@other_parents);
for my $parent (@parents) {
my $keep_parent_around = 0;
for my $line (split /\n/, retrieve("parent::$parent")) {
my ($name, $hash) = split /\s+/, $line;
next unless exists $data{$name};
my $local_hash = fast_hash(retrieve($name));
if ($clobber_divergent or $hash eq $local_hash or ! defined $hash) {rm($name) unless $kept_by_another_parent{$name}}
else {terminal::info("local attribute $name exists and is divergent; use rmparent -D $parent to delete it");
$keep_parent_around = 1}}
$keep_parent_around ? terminal::info("not deleting parent::$parent so that you can run", "rmparent -D $parent if you want to nuke divergent attributes too")
: rm("parent::$parent")}
__
meta::function('save', 'around_hook(\'save\', sub {dangerous(\'\', sub {file::write($0, serialize()); $transient{initial} = state()}) if verify()});');
meta::function('save-state', <<'__');
# Creates a named copy of the current state and stores it.
my ($state_name) = @_;
around_hook('save-state', $state_name, sub {
associate("state::$state_name", current_state(), execute => 1)});
__
meta::function('serialize', <<'__');
my ($options, @criteria) = separate_options(@_);
my $partial = $$options{'-p'};
my $criteria = join '|', @criteria;
my @attributes = map serialize_single($_), select_keys(%$options, '-m' => 1, '--criteria' => $criteria), select_keys(%$options, '-M' => 1, '--criteria' => $criteria);
my @final_array = @{$partial ? \@attributes : [retrieve('bootstrap::initialization'), @attributes, 'internal::main();', '', '__END__']};
join "\n", @final_array;
__
meta::function('serialize-single', <<'__');
# Serializes a single attribute and optimizes for content.
my $name = $_[0] || $_;
my $contents = $data{$name};
my $meta_function = 'meta::' . namespace($name);
my $invocation = attribute($name);
my $escaped = $contents;
$escaped =~ s/\\/\\\\/go;
$escaped =~ s/'/\\'/go;
return "$meta_function('$invocation', '$escaped');" unless $escaped =~ /\v/;
my $delimiter = '__' . fast_hash($contents);
my $chars = 2;
++$chars until $chars >= length($delimiter) || index("\n$contents", "\n" . substr($delimiter, 0, $chars)) == -1;
$delimiter = substr($delimiter, 0, $chars);
"$meta_function('$invocation', <<'$delimiter');\n$contents\n$delimiter";
__
meta::function('sh', 'system(@_);');
meta::function('shb', <<'__');
# Backgrounded shell job.
exec(@_) unless fork;
__
meta::function('shell', <<'__');
my ($options, @arguments) = separate_options(@_);
$transient{repl_prefix} = $$options{'--repl-prefix'};
terminal::cc(retrieve('data::current-continuation')) if length $data{'data::current-continuation'};
around_hook('shell', sub {shell::repl(%$options)});
__
meta::function('size', <<'__');
my $size = 0;
$size += length $data{$_} for keys %data;
sprintf "% 7d % 7d % 7d", length(serialize()), $size, length(serialize('-up'));
__
meta::function('snapshot', <<'__');
my ($name) = @_;
file::write(my $finalname = temporary_name($name), serialize(), noclobber => 1);
chmod 0700, $finalname;
hook('snapshot', $finalname);
__
meta::function('state', <<'__');
my @keys = sort keys %data;
my $hash = fast_hash(fast_hash(scalar @keys) . join '|', @keys);
$hash = fast_hash("$data{$_}|$hash") for @keys;
$hash;
__
meta::function('touch', 'associate($_, \'\') for @_;');
meta::function('unlock', 'hook(\'unlock\', chmod_self(sub {$_[0] | 0200}));');
meta::function('update', <<'__');
update_from(@_, grep s/^parent:://o, sort keys %data);
__
meta::function('update-from', <<'__');
# Upgrade all attributes that aren't customized. Customization is defined when the data type is created,
# and we determine it here by checking for $transient{inherit}{$type}.
# Note that this assumes you trust the remote script. If you don't, then you shouldn't update from it.
around_hook('update-from-invocation', separate_options(@_), sub {
my ($options, @targets) = @_;
my %parent_id_cache = cache('parent-identification');
my %already_seen;
@targets or return;
my @known_targets = grep s/^parent:://, parent_ordering(map "parent::$_", grep exists $data{"parent::$_"}, @targets);
my @unknown_targets = grep ! exists $data{"parent::$_"}, @targets;
@targets = (@known_targets, @unknown_targets);
my $save_state = ! ($$options{'-n'} || $$options{'--no-save'});
my $no_parents = $$options{'-P'} || $$options{'--no-parent'} || $$options{'--no-parents'};
my $force = $$options{'-f'} || $$options{'--force'};
my $clobber_divergent = $$options{'-D'} || $$options{'--clobber-divergent'};
save_state('before-update') if $save_state;
for my $target (@targets) {
dangerous("updating from $target", sub {
around_hook('update-from', $target, sub {
my $identity = $parent_id_cache{$target} ||= join '', qx($target identity);
next if $already_seen{$identity};
$already_seen{$identity} = 1;
my $attributes = join '', qx($target ls -ahiu);
my %divergent;
die "skipping unreachable $target" unless $attributes;
for my $to_rm (split /\n/, retrieve("parent::$target")) {
my ($name, $hash) = split(/\s+/, $to_rm);
next unless exists $data{$name};
my $local_hash = fast_hash(retrieve($name));
if ($clobber_divergent or $hash eq $local_hash or ! defined $hash) {rm($name)}
else {terminal::info("preserving local version of divergent attribute $name (use update -D to clobber it)");
$divergent{$name} = retrieve($name)}}
associate("parent::$target", $attributes) unless $no_parents;
dangerous('', sub {eval qx($target serialize -ipmu)});
dangerous('', sub {eval qx($target serialize -ipMu)});
map associate($_, $divergent{$_}), keys %divergent unless $clobber_divergent;
reload()})})}
cache('parent-identification', %parent_id_cache);
if (verify()) {hook('update-from-succeeded', $options, @targets);
terminal::info("Successfully updated. Run 'load-state before-update' to undo this change.") if $save_state}
elsif ($force) {hook('update-from-failed', $options, @targets);
terminal::warning('Failed to verify: at this point your object will not save properly, though backup copies will be created.',
'Run "load-state before-update" to undo the update and return to a working state.') if $save_state}
else {hook('update-from-failed', $options, @targets);
terminal::error('Verification failed after the upgrade was complete.');
terminal::info("$0 has been reverted to its pre-upgrade state.", "If you want to upgrade and keep the failure state, then run 'update-from $target --force'.") if $save_state;
return load_state('before-update') if $save_state}});
__
meta::function('usage', '"Usage: $0 action [arguments]\\nUnique actions (run \'$0 ls\' to see all actions):" . ls(\'-u\');');
meta::function('verify', <<'__');
file::write(my $other = $transient{temporary_filename} = temporary_name(), my $serialized_data = serialize());
chomp(my $observed = join '', qx|perl '$other' state|);
unlink $other if my $result = $observed eq (my $state = state());
terminal::error("Verification failed; expected $state but got $observed from $other") unless $result;
hook('after-verify', $result, observed => $observed, expected => $state);
$result;
__
meta::indicator('cc', 'length ::retrieve(\'data::current-continuation\') ? "\\033[1;36mcc\\033[0;0m" : \'\';');
meta::indicator('locked', 'is_locked() ? "\\033[1;31mlocked\\033[0;0m" : \'\';');
meta::indicator('path', <<'__');
join "\033[1;30m/\033[0;0m", @{$transient{path}};
__
meta::internal_function('around_hook', <<'__');
# around_hook('hookname', @args, sub {
# stuff;
# });
# Invokes 'before-hookname' on @args before the sub runs, invokes the
# sub on @args, then invokes 'after-hookname' on @args afterwards.
# The after-hook is not invoked if the sub calls 'die' or otherwise
# unwinds the stack.
my $hook = shift @_;
my $f = pop @_;
hook("before-$hook", @_);
my $result = &$f(@_);
hook("after-$hook", @_);
$result;
__
meta::internal_function('associate', <<'__');
my ($name, $value, %options) = @_;
die "Namespace does not exist" unless exists $datatypes{namespace($name)};
$data{$name} = $value;
execute($name) if $options{'execute'};
$value;
__
meta::internal_function('attribute', <<'__');
my ($name) = @_;
$name =~ s/^[^:]*:://;
$name;
__
meta::internal_function('attribute_is', <<'__');
my ($a, %options) = @_;
my %inherited = parent_attributes(grep /^parent::/o, sort keys %data) if grep exists $options{$_}, qw/-u -U -d -D/;
my $criteria = $options{'--criteria'} || $options{'--namespace'} && "^$options{'--namespace'}::" || '.';
my %tests = ('-u' => sub {! $inherited{$a}},
'-d' => sub {$inherited{$a} && fast_hash(retrieve($a)) ne $inherited{$a}},
'-i' => sub {$transient{inherit}{namespace($a)}},
'-s' => sub {$a =~ /^state::/o},
'-m' => sub {$a =~ /^meta::/o});
return 0 unless scalar keys %tests == scalar grep ! exists $options{$_} || &{$tests{$_}}(), keys %tests;
return 0 unless scalar keys %tests == scalar grep ! exists $options{uc $_} || ! &{$tests{$_}}(), keys %tests;
$a =~ /$_/ || return 0 for @{$options{'--path'}};
$a =~ /$criteria/;
__
meta::internal_function('cache', <<'__');
my ($name, %pairs) = @_;
if (%pairs) {associate("cache::$name", join "\n", map {$pairs{$_} =~ s/\n//g; "$_ $pairs{$_}"} sort keys %pairs)}
else {map split(/\s/, $_, 2), split /\n/, retrieve("cache::$name")}
__
meta::internal_function('chmod_self', <<'__');
my ($mode_function) = @_;
my (undef, undef, $mode) = stat $0;
chmod &$mode_function($mode), $0;
__
meta::internal_function('complete', <<'__');
my @functions = sort keys %externalized_functions;
my @attributes = sort keys %data;
sub match {
my ($text, @options) = @_;
my @matches = sort grep /^$text/, @options;
if (@matches == 0) {return undef;}
elsif (@matches == 1) {return $matches [0];}
elsif (@matches > 1) {return ((longest ($matches [0], $matches [@matches - 1])), @matches);}
}
sub longest {
my ($s1, $s2) = @_;
return substr ($s1, 0, length $1) if ($s1 ^ $s2) =~ /^(\0*)/;
return '';
}
# This is another way to implement autocompletion.
#
# my $attribs = $term->Attribs;
# $attribs->{completion_entry_function} = $attribs->{list_completion_function};
# $attribs->{completion_word} = [sort keys %data, sort keys %externalized_functions];
my ($text, $line) = @_;
if ($line =~ / /) {
# Start matching attribute names.
match ($text, @attributes);
} else {
# Start of line, so it's a function.
match ($text, @functions);
}
__
meta::internal_function('dangerous', <<'__');
# Wraps a computation that may produce an error.
my ($message, $computation) = @_;
terminal::info($message) if $message;
my @result = eval {&$computation()};
terminal::warning(translate_backtrace($@)), return undef if $@;
wantarray ? @result : $result[0];
__
meta::internal_function('debug_trace', <<'__');
terminal::debug(join ', ', @_);
wantarray ? @_ : $_[0];
__
meta::internal_function('execute', <<'__');
my ($name, %options) = @_;
my $namespace = namespace($name);
eval {&{$datatypes{$namespace}}(attribute($name), retrieve($name))};
warn $@ if $@ && $options{'carp'};
__
meta::internal_function('exported', <<'__');
# Allocates a temporary file containing the concatenation of attributes you specify,
# and returns the filename. The filename will be safe for deletion anytime.
my $filename = temporary_name();
file::write($filename, cat(@_));
$filename;
__
meta::internal_function('extension_for', <<'__');
my $extension = $transient{extension}{namespace($_[0])};
$extension = &$extension($_[0]) if ref $extension eq 'CODE';
$extension || '';
__
meta::internal_function('fast_hash', <<'__');
my ($data) = @_;
my $piece_size = length($data) >> 3;
my @pieces = (substr($data, $piece_size * 8) . length($data), map(substr($data, $piece_size * $_, $piece_size), 0 .. 7));
my @hashes = (fnv_hash($pieces[0]));
push @hashes, fnv_hash($pieces[$_ + 1] . $hashes[$_]) for 0 .. 7;
$hashes[$_] ^= $hashes[$_ + 4] >> 16 | ($hashes[$_ + 4] & 0xffff) << 16 for 0 .. 3;
$hashes[0] ^= $hashes[8];
sprintf '%08x' x 4, @hashes[0 .. 3];
__
meta::internal_function('file::read', <<'__');
my $name = shift;
open my($handle), "<", $name;
my $result = join "", <$handle>;
close $handle;
$result;
__
meta::internal_function('file::write', <<'__');
use File::Path 'mkpath';
use File::Basename 'dirname';
my ($name, $contents, %options) = @_;
die "Choosing not to overwrite file $name" if $options{noclobber} and -f $name;
mkpath(dirname($name)) if $options{mkpath};
open my($handle), $options{append} ? '>>' : '>', $name or die "Can't open $name for writing";
print $handle $contents;
close $handle;
__
meta::internal_function('fnv_hash', <<'__');
# A rough approximation to the Fowler-No Voll hash. It's been 32-bit vectorized
# for efficiency, which may compromise its effectiveness for short strings.
my ($data) = @_;
my ($fnv_prime, $fnv_offset) = (16777619, 2166136261);
my $hash = $fnv_offset;
my $modulus = 2 ** 32;
$hash = ($hash ^ ($_ & 0xffff) ^ ($_ >> 16)) * $fnv_prime % $modulus for unpack 'L*', $data . substr($data, -4) x 8;
$hash;
__
meta::internal_function('hypothetically', <<'__');
# Applies a temporary state and returns a serialized representation.
# The original state is restored after this, regardless of whether the
# temporary state was successful.
my %data_backup = %data;
my ($side_effect) = @_;
my $return_value = eval {&$side_effect()};
%data = %data_backup;
die $@ if $@;
$return_value;
__
meta::internal_function('internal::main', <<'__');
disable();
$SIG{'INT'} = sub {snapshot(); exit 1};
$transient{initial} = state();
chomp(my $default_action = retrieve('data::default-action'));
my $function_name = shift(@ARGV) || $default_action || 'usage';
terminal::warning("unknown action: '$function_name'") and $function_name = 'usage' unless $externalized_functions{$function_name};
around_hook('main-function', $function_name, @ARGV, sub {
dangerous('', sub {
chomp(my $result = &$function_name(@ARGV));
print "$result\n" if $result})});
save() unless state() eq $transient{initial};
END {
enable();
}
__
meta::internal_function('invoke_editor_on', <<'__');
my ($data, %options) = @_;
my $editor = $options{editor} || $ENV{VISUAL} || $ENV{EDITOR} || die 'Either the $VISUAL or $EDITOR environment variable should be set to a valid editor';
my $options = $options{options} || $ENV{VISUAL_OPTS} || $ENV{EDITOR_OPTS} || '';
my $attribute = $options{attribute};
$attribute =~ s/\//-/g;
my $filename = temporary_name() . "-$attribute$options{extension}";
file::write($filename, $data);
system("$editor $options '$filename'");
my $result = file::read($filename);
unlink $filename;
$result;
__
meta::internal_function('is_locked', '!((stat($0))[2] & 0222);');
meta::internal_function('namespace', <<'__');
my ($name) = @_;
$name =~ s/::.*$//;
$name;
__
meta::internal_function('parent_attributes', <<'__');
my $attributes = sub {my ($name, $value) = split /\s+/o, $_; $name => ($value || 1)};
map &$attributes(), split /\n/o, join("\n", retrieve(@_));
__
meta::internal_function('parent_ordering', <<'__');
# Topsorts the parents by dependency chain. The simplest way to do this is to
# transitively compute the number of parents referred to by each parent.
my @parents = @_;
my %all_parents = map {$_ => 1} @parents;
my %parents_of = map {
my $t = $_;
my %attributes = parent_attributes($_);
$t => [grep /^parent::/, keys %attributes]} @parents;
my %parent_count;
my $parent_count;
$parent_count = sub {
my ($key) = @_;
return $parent_count{$key} if exists $parent_count{$key};
my $count = 0;
$count += $parent_count->($_) + exists $data{$_} for @{$parents_of{$key}};
$parent_count{$key} = $count};
my %inverses;
push @{$inverses{$parent_count->($_)} ||= []}, $_ for @parents;
grep exists $all_parents{$_}, map @{$inverses{$_}}, sort keys %inverses;
__
meta::internal_function('retrieve', <<'__');
my @results = map defined $data{$_} ? $data{$_} : retrieve_with_hooks($_), @_;
wantarray ? @results : $results[0];
__
meta::internal_function('retrieve_with_hooks', <<'__');
# Uses the hooks defined in $transient{retrievers}, and returns undef if none work.
my ($attribute) = @_;
my $result = undef;
defined($result = &$_($attribute)) and return $result for map $transient{retrievers}{$_}, sort keys %{$transient{retrievers}};
return undef;
__
meta::internal_function('select_keys', <<'__');
my %options = @_;
grep attribute_is($_, %options), sort keys %data;
__
meta::internal_function('separate_options', <<'__');
# Things with one dash are short-form options, two dashes are long-form.
# Characters after short-form are combined; so -auv4 becomes -a -u -v -4.
# Also finds equivalences; so --foo=bar separates into $$options{'--foo'} eq 'bar'.
# Stops processing at the -- option, and removes it. Everything after that
# is considered to be an 'other' argument.
# The only form not supported by this function is the short-form with argument.
# To pass keyed arguments, you need to use long-form options.
my @parseable;
push @parseable, shift @_ until ! @_ or $_[0] eq '--';
my @singles = grep /^-[^-]/, @parseable;
my @longs = grep /^--/, @parseable;
my @others = grep ! /^-/, @parseable;
my @singles = map /-(.{2,})/ ? map("-$_", split(//, $1)) : $_, @singles;
my %options;
/^([^=]+)=(.*)$/ and $options{$1} = $2 for @longs;
++$options{$_} for grep ! /=/, @singles, @longs;
({%options}, @others, @_);
__
meta::internal_function('strip', 'wantarray ? map {s/^\\s*|\\s*$//g; $_} @_ : $_[0] =~ /^\\s*(.*?)\\s*$/ && $1;');
meta::internal_function('table_display', <<'__');
# Displays an array of arrays as a table; that is, with alignment. Arrays are
# expected to be in column-major order.
sub maximum_length_in {
my $maximum = 0;
length > $maximum and $maximum = length for @_;
$maximum;
}
my @arrays = @_;
my @lengths = map maximum_length_in(@$_), @arrays;
my @row_major = map {my $i = $_; [map $$_[$i], @arrays]} 0 .. $#{$arrays[0]};
my $format = join ' ', map "%-${_}s", @lengths;
join "\n", map strip(sprintf($format, @$_)), @row_major;
__
meta::internal_function('temporary_name', <<'__');
use File::Temp 'tempfile';
my (undef, $temporary_filename) = tempfile("$0." . 'X' x 4, OPEN => 0);
$temporary_filename;
__
meta::internal_function('translate_backtrace', <<'__');
my ($trace) = @_;
$trace =~ s/\(eval (\d+)\)/$locations{$1 - 1}/g;
$trace;
__
meta::internal_function('with_exported', <<'__');
# Like exported(), but removes the file after running some function.
# Usage is with_exported(@files, sub {...});
my $f = pop @_;
my $name = exported(@_);
my $result = eval {&$f($name)};
terminal::warning("$@ when running with_exported()") if $@;
unlink $name;
$result;
__
meta::library('shell', <<'__');
# Functions for shell parsing and execution.
package shell;
use Term::ReadLine;
sub tokenize {grep length, split /\s+|("[^"\\]*(?:\\.)?")/o, join ' ', @_};
sub parse {
my ($fn, @args) = @_;
s/^"(.*)"$/\1/o, s/\\\\"/"/go for @args;
{function => $fn, args => [@args]}}
sub execute {
my %command = %{$_[0]};
die "undefined command: $command{function}" unless exists $externalized_functions{$command{function}};
&{"::$command{function}"}(@{$command{args}})}
sub run {execute(parse(tokenize(@_)))}
sub prompt {
my %options = @_;
my $name = $options{name} // ::name();
my $indicators = join '', map &{"::$_"}(), ::select_keys('--namespace' => 'indicator');
my $prefix = $transient{repl_prefix} // '';
"$prefix\033[1;32m$name\033[0;0m$indicators "}
sub repl {
my %options = @_;
my $term = new Term::ReadLine "$0 shell";
$term->ornaments(0);
my $attribs = $term->Attribs;
$attribs->{completion_entry_function} = $attribs->{list_completion_function};
my $autocomplete = $options{autocomplete} || sub {[sort(keys %data), grep !/-/, sort keys %externalized_functions]};
my $prompt = $options{prompt} || \&prompt;
my $parse = $options{parse} || sub {parse(tokenize(@_))};
my $command = $options{command} || sub {my ($command) = @_; ::around_hook('shell-command', $command, sub {print ::dangerous('', sub {execute($command)}), "\n"})};
length $_ && &$command(&$parse($_)) while ($attribs->{completion_word} = &$autocomplete(), defined($_ = $term->readline(&$prompt())))}
__
meta::library('terminal', <<'__');
# Functions for nice-looking terminal output.
package terminal;
my $process = ::name();
sub message {print STDERR "[$_[0]] $_[1]\n"}
sub color {
my ($name, $color) = @_;
*{"terminal::$name"} = sub {chomp($_), print STDERR "\033[1;30m$process(\033[1;${color}m$name\033[1;30m)\033[0;0m $_\n" for map join('', $_), @_}}
my %preloaded = (info => 32, progress => 32, state => 34, debug => 34, warning => 33, error => 31);
color $_, $preloaded{$_} for keys %preloaded;
__
meta::message_color('cc', '36');
meta::message_color('state', 'purple');
meta::message_color('states', 'yellow');
meta::message_color('watch', 'blue');
meta::note('queue', <<'__');
Queue.
This stores todo items in whatever format you care to use.
__
meta::parent('/home/spencertipping/bin/notes', <<'__');
function::note 5e2737593e8d13fc43bb10e97603e53a
function::notes 7229b326ac8686b2db6de98496bc7527
meta::type::note f81bea58841a438e4ee34608ab4f54c0
parent::object 7374722eec54c39ab752e3fb032c1886
__
meta::parent('object', <<'__');
bootstrap::html f44dd03cb0c904b3a5f69fbda5f018d0
bootstrap::initialization 1cf74e7209f32722a79b6e49e3907fd3
bootstrap::perldoc 5793df44bdd2526bb461272924abfd4b
function::ad 77a05d9a6fef7871b2c3e8e94b56870a
function::alias 8eeeeb4e064ef3aba7edf8f254427bc2
function::cat f684de6c8776617a437b76009114f52e
function::cc 12ea9176e388400704d823433c209b7a
function::ccc d151a9793edd83f80fb880b7f0ab9b34
function::child f5764adf0b4e892f147a9b6b68d4816f
function::clone bb42e04e10a8e54e88786b6fbc4fb213
function::cp 3fe69d1b58d90045ad520048977538c4
function::create 3010d55f4dfa59a998742e07823ed54d
function::current-state 6f03f86f1901e9ef07fdb5d4079a914c
function::disable 53b449708cc2ffdefa352e53bb7d847d
function::edit 9ce5ba1ae4607e8cf1975080bcde1cf4
function::enable 7de1cedc36841f5de8f9fdfbc3b65097
function::export 2374cd1dbf7616cb38cafba4e171075d
function::extern 1290a5223e2824763eecfb3a54961eff
function::grep 55c3cea8ff4ec2403be2a9d948e59f14
function::hash 6ee131d093e95b80039b4df9c7c84a02
function::hook 675cdb98b5dd8567bdd5a02ead6184b5
function::hooks 3d989899c616f7440429a2d9bf1cc44b
function::identity 6523885762fcc2f354fc25cf6ed126ce
function::import 5d0f0634cbd01274f2237717507198a2
function::initial-state 03d8ed608855a723124e79ca184d8e73
function::is 41564c8f21b12ab80824ac825266d805
function::load-state b6cf278a1f351f316fa6e070359b6081
function::lock 5d8db258704e6a8623fac796f62fac02
function::ls 01a23d51d5b529e03943bd57e33f92df
function::mv 4a0e338a6edb89ad1e2c779d51d4d47b
function::name 955ba2d1fe1d67cd78651a4042283b00
function::parents 3da9e63b5aae9e2f5dcc946a86d166aa
function::perl a0f341ea54391b63b6195e7992b6a686
function::rd 2adb16d7e819d2e87a27201744a581e7
function::reload 1589f4cf8374e0011991cb8907afca3e
function::rm 6f6fd7a6c25558eb469d78ea888f8551
function::rmparent fc2884910a6939a47898a778f277332c
function::save 778c0e1043b9c6c96fb8f266f8061624
function::save-state 5af59ebc4ad8965767e4dc106d3b557e
function::serialize a19ada2d2558ea9da3a7942fb913e15f
function::serialize-single aa77af032272f5a2664e21713739a223
function::sh 1b2f542ca9dd63ad437058b7f6f61aac
function::shb 7b2685a4041c25bc495816e472bdace5
function::shell a87f389b94713e5855e62241d649d01d
function::size 8d4bd7a84ece556717f8ba3bf258d33c
function::snapshot 56939a47f2758421669641e15ebd66eb
function::state 8c68044dccae28f33244d0c7e9e9acfb
function::touch 3991b1b7c7187566f50e5e58ce01fa06
function::unlock b4aac02f7f3fb700acf4acfd9b180ceb
function::update ac391dc90e507e7586c81850e7c2ecdd
function::update-from 631721c4dc30e11b2023a6703cbcef52
function::usage 5bdd370f5a56cfbf199e08d398091444
function::verify 0c0cc1dfeab7d705919df122f7850a4f
indicator::cc 3db7509c521ee6abfedd33d5f0148ed3
indicator::locked fc2b4f4ca0d6a334b9ac423d06c8f18c
indicator::path 9ec891df17cd45895f03a6124f9d065f
internal_function::around_hook 7cc876e7c5f78c34654337fc95255587
internal_function::associate 05a75afb70daee635eefec8ae037f593
internal_function::attribute dd6f010f9688977464783f60f5b6d3dd
internal_function::attribute_is 40bda8226322505e323ea6d405388f08
internal_function::cache eb9da45580a9ac0882baf98acd2ecd60
internal_function::chmod_self 2035e861eedab55ba0a9f6f5a068ca70
internal_function::dangerous 46c4baaa214ab3d05af43e28083d5141
internal_function::debug_trace 0faf9d9f4159d72dfe4481f6f3607ce1
internal_function::execute f0924e087d978ff2ab1e117124db3042
internal_function::exported ae35afef7d4762f2818aee5872c75be0
internal_function::extension_for 9de8261d69cc93e9b92072b89c89befd
internal_function::fast_hash ee5eba48f837fda0fe472645fdd8899a
internal_function::file::read e647752332c8e05e81646a3ff98f9a8e
internal_function::file::write 3e290fdcb353c6f842eb5a40f2e575f8
internal_function::fnv_hash c36d56f1e13a60ae427afc43ba025afc
internal_function::hypothetically b83e3f894a6df8623ccd370515dfd976
internal_function::internal::main f31f2945a19a668d92505f114ab29c78
internal_function::invoke_editor_on 5eb976796f0ec172d6ec036116a2f41e
internal_function::is_locked da12ced6aa38295251f7e748ffd22925
internal_function::namespace 784d2e96003550681a4ae02b8d6d0a27
internal_function::parent_attributes f6ccfaa982ab1a4d066043981aaca277
internal_function::parent_ordering 57b6da88f76b59f3fed9abfa61280e5e
internal_function::retrieve 8a34d1fe047fe1b40c3d2957c4a789eb
internal_function::retrieve_with_hooks 0f1b0220ccd973d57a2e96ff00458cf2
internal_function::select_keys a5e3532ec6d58151d0ee24416ea1e2b5
internal_function::separate_options 34ec41a6edaa15adde607a0db3ccfa36
internal_function::strip 14f490b10ebd519e829d8ae20ea4d536
internal_function::table_display d575f4dc873b2e0be5bd7352047fd904
internal_function::temporary_name 6f548d101fc68356515ffd0fc9ae0c93
internal_function::translate_backtrace d77a56d608473b3cd8a3c6cb84185e10
internal_function::with_exported df345d5095d5ed13328ddd07ea922b36
library::shell 6b9f3befb61a01e9132a440601f8ea0a
library::terminal 7e2d045782405934a9614fe04bcfe559
message_color::cc 2218ef0f7425de5c717762ffb100eb43
message_color::state 03621cd6ac0b1a40d703f41e26c5807f
message_color::states ac66eeeff487b5f43f88a78ea18b3d56
meta::configure 69c2e727c124521d074fde21f8bbc4db
meta::externalize aa44e27e0bbee6f0ca4de25d603a1fc7
meta::functor::editable 48246c608f363de66511400e00b26164
meta::type::alias 889d26d2df385e9ff8e2da7de4e48374
meta::type::bootstrap 51108ab2ddb8d966e927c8f62d9ef3e5
meta::type::cache 9267171f2eace476f64a1a670eaaf2c7
meta::type::data 120e1649a468d3b3fd3fb783b4168499
meta::type::function 8ea626198861dc59dd7f303eecb5ff88
meta::type::hook ff92aef328b6bdc6f87ddd0821f3e42f
meta::type::inc 78e0375b6725487cb1f0deca41e96bbe
meta::type::indicator feb54a2624e6983617685047c717427f
meta::type::internal_function eff3cf31e2635f51c83836f116c99d2f
meta::type::library 7622e8d65e03066668bade74715d65ad
meta::type::message_color 557a1b44979cbf77a7251fbdc4c5b82c
meta::type::meta c6250056816b58a9608dd1b2614246f8
meta::type::parent 09d1d03379e4e0b262e06939f4e00464
meta::type::retriever 71a29050bf9f20f6c71afddff83addc9
meta::type::state 84da7d5220471307f1f990c5057d3319
retriever::file 3bbc9d8a887a536044bafff1d54def7e
retriever::id 4da6080168d32445150cc4200af7af6e
retriever::object c7633990b4e01bdc783da7e545799f4f
retriever::perl f41938e6dbad317f62abffc1e4d28cca
__
meta::retriever('file', '-f $_[0] ? file::read($_[0]) : undef;');
meta::retriever('id', '$_[0] =~ /^id::/ ? substr($_[0], 4) : undef;');
meta::retriever('object', <<'__');
# Fetch a property from another Perl object. This uses the 'cat' function.
return undef unless $_[0] =~ /^object::(.*?)::(.*)$/ && -x $1 && qx|$1 is '$2'|;
join '', qx|$1 cat '$2'|;
__
meta::retriever('perl', <<'__');
# Lets you use the result of evaluating some Perl expression
return undef unless $_[0] =~ /^perl::(.*)$/;
eval $1;
__
meta::state('before-update', <<'__29a');
meta::meta('configure', <<'__25976e07665878d3fae18f050160343f');
# A function to configure transients. Transients can be used to store any number of
# different things, but one of the more common usages is type descriptors.
sub meta::configure {
my ($datatype, %options) = @_;
$transient{$_}{$datatype} = $options{$_} for keys %options;
}
__25976e07665878d3fae18f050160343f
meta::meta('externalize', <<'__9141b4e8752515391385516ae94b23b5');
# Function externalization. Data types should call this method when defining a function
# that has an external interface.
sub meta::externalize {
my ($name, $attribute, $implementation) = @_;
$externalized_functions{$name} = $attribute;
*{"::$name"} = $implementation || $attribute;
}
__9141b4e8752515391385516ae94b23b5
meta::meta('functor::editable', <<'__e3d2ede6edf65ffe2123584b2bd5dab7');
# An editable type. This creates a type whose default action is to open an editor
# on whichever value is mentioned. This can be changed using different flags.
sub meta::functor::editable {
my ($typename, %options) = @_;
meta::configure $typename, %options;
meta::define_form $typename, sub {
my ($name, $value) = @_;
$options{on_bind} && &{$options{on_bind}}($name, $value);
meta::externalize $options{prefix} . $name, "${typename}::$name", sub {
my $attribute = "${typename}::$name";
my ($command, @new_value) = @_;
return &{$options{default}}(retrieve($attribute)) if ref $options{default} eq 'CODE' and not defined $command;
return edit($attribute) if $command eq 'edit' or $options{default} eq 'edit' and not defined $command;
return associate($attribute, @new_value ? join(' ', @new_value) : join('', )) if $command eq '=' or $command eq 'import' or $options{default} eq 'import' and not defined $command;
return retrieve($attribute)}}}
__e3d2ede6edf65ffe2123584b2bd5dab7
meta::meta('type::alias', <<'__28fe15dd61f4902ed5180d8604d15d97');
meta::configure 'alias', inherit => 0;
meta::define_form 'alias', sub {
my ($name, $value) = @_;
meta::externalize $name, "alias::$name", sub {
# Can't pre-tokenize because shell::tokenize doesn't exist until the library::
# namespace has been evaluated (which will be after alias::).
shell::run(shell::tokenize($value), shell::tokenize(@_));
};
};
__28fe15dd61f4902ed5180d8604d15d97
meta::meta('type::bootstrap', <<'__297d03fb32df03b46ea418469fc4e49e');
# Bootstrap attributes don't get executed. The reason for this is that because
# they are serialized directly into the header of the file (and later duplicated
# as regular data attributes), they will have already been executed when the
# file is loaded.
meta::configure 'bootstrap', extension => '.pl', inherit => 1;
meta::define_form 'bootstrap', sub {};
__297d03fb32df03b46ea418469fc4e49e
meta::meta('type::data', 'meta::functor::editable \'data\', extension => \'\', inherit => 0, default => \'cat\';');
meta::meta('type::function', <<'__d93b3cc15693707dac518e3d6b1f5648');
meta::configure 'function', extension => '.pl', inherit => 1;
meta::define_form 'function', sub {
my ($name, $value) = @_;
meta::externalize $name, "function::$name", meta::eval_in("sub {\n$value\n}", "function::$name");
};
__d93b3cc15693707dac518e3d6b1f5648
meta::meta('type::hook', <<'__f55a3f728ddfb90204dff3fe5d86845c');
meta::configure 'hook', extension => '.pl', inherit => 0;
meta::define_form 'hook', sub {
my ($name, $value) = @_;
*{"hook::$name"} = meta::eval_in("sub {\n$value\n}", "hook::$name");
};
__f55a3f728ddfb90204dff3fe5d86845c
meta::meta('type::inc', <<'__c95915391b969734305f2f492d5ca8e3');
meta::configure 'inc', inherit => 1, extension => '.pl';
meta::define_form 'inc', sub {
use File::Path 'mkpath';
use File::Basename qw/basename dirname/;
my ($name, $value) = @_;
my $tmpdir = basename($0) . '-' . $$;
my $filename = "/tmp/$tmpdir/$name";
push @INC, "/tmp/$tmpdir" unless grep /^\/tmp\/$tmpdir$/, @INC;
mkpath(dirname($filename));
unless (-e $filename) {
open my $fh, '>', $filename;
print $fh $value;
close $fh;
}
};
__c95915391b969734305f2f492d5ca8e3
meta::meta('type::internal_function', <<'__34abb44c67c7e282569e28ef6f4d62ab');
meta::configure 'internal_function', extension => '.pl', inherit => 1;
meta::define_form 'internal_function', sub {
my ($name, $value) = @_;
*{$name} = meta::eval_in("sub {\n$value\n}", "internal_function::$name");
};
__34abb44c67c7e282569e28ef6f4d62ab
meta::meta('type::library', <<'__b6dd78120e6d787acdb5c1629f7f1896');
meta::configure 'library', extension => '.pl', inherit => 1;
meta::define_form 'library', sub {
my ($name, $value) = @_;
meta::eval_in($value, "library::$name");
};
__b6dd78120e6d787acdb5c1629f7f1896
meta::meta('type::message_color', <<'__794bf137c425293738f07636bcfb5c55');
meta::configure 'message_color', extension => '', inherit => 1;
meta::define_form 'message_color', sub {
my ($name, $value) = @_;
terminal::color($name, $value);
};
__794bf137c425293738f07636bcfb5c55
meta::meta('type::meta', <<'__640f25635ce2365b0648962918cf9932');
# This doesn't define a new type. It customizes the existing 'meta' type
# defined in bootstrap::initialization. Note that horrible things will
# happen if you redefine it using the editable functor.
meta::configure 'meta', extension => '.pl', inherit => 1;
__640f25635ce2365b0648962918cf9932
meta::meta('type::note', 'meta::functor::editable \'note\', extension => \'.sdoc\', inherit => 0, default => \'edit\';');
meta::meta('type::parent', <<'__607e9931309b1b595424bedcee5dfa45');
meta::define_form 'parent', \&meta::bootstrap::implementation;
meta::configure 'parent', extension => '', inherit => 1;
__607e9931309b1b595424bedcee5dfa45
meta::meta('type::retriever', <<'__6e847a9d205e4a5589765a3366cdd115');
meta::configure 'retriever', extension => '.pl', inherit => 1;
meta::define_form 'retriever', sub {
my ($name, $value) = @_;
$transient{retrievers}{$name} = meta::eval_in("sub {\n$value\n}", "retriever::$name");
};
__6e847a9d205e4a5589765a3366cdd115
meta::meta('type::state', <<'__c1f29670be26f1df6100ffe4334e1202');
# Allows temporary or long-term storage of states. Nothing particularly insightful
# is done about compression, so storing alternative states will cause a large
# increase in size. Also, states don't contain other states -- otherwise the size
# increase would be exponential.
# States are created with the save-state function.
meta::configure 'state', inherit => 0, extension => '.pl';
meta::define_form 'state', \&meta::bootstrap::implementation;
__c1f29670be26f1df6100ffe4334e1202
meta::meta('type::watch', 'meta::functor::editable \'watch\', prefix => \'watch::\', inherit => 1, extension => \'.pl\', default => \'cat\';');
meta::bootstrap('html', <<'__aa347c4339e71e7acc9ea4fc3d593347');
__aa347c4339e71e7acc9ea4fc3d593347
meta::bootstrap('initialization', <<'__8774229a1a0ce7fd056d81ba0b077f79');
#!/usr/bin/perl
# Run perldoc on this file for documentation.
$|++;
my %data;
my %transient;
my %externalized_functions;
my %datatypes;
my %locations; # Maps eval-numbers to attribute names
sub meta::define_form {
my ($namespace, $delegate) = @_;
$datatypes{$namespace} = $delegate;
*{"meta::${namespace}::implementation"} = $delegate;
*{"meta::$namespace"} = sub {
my ($name, $value, %options) = @_;
chomp $value;
$data{"${namespace}::$name"} = $value unless $options{no_binding};
$delegate->($name, $value) unless $options{no_delegate}}}
sub meta::eval_in {
my ($what, $where) = @_;
# Obtain next eval-number and alias it to the designated location
@locations{eval('__FILE__') =~ /\(eval (\d+)\)/} = ($where);
my $result = eval $what;
$@ =~ s/\(eval \d+\)/$where/ if $@;
warn $@ if $@;
$result}
meta::define_form 'meta', sub {
my ($name, $value) = @_;
meta::eval_in($value, "meta::$name")};
__8774229a1a0ce7fd056d81ba0b077f79
meta::bootstrap('perldoc', <<'__c63395cbc6f7160b603befbb2d9b6700');
=head1 Self-modifying Perl script
=head2 Original implementation by Spencer Tipping L
The prototype for this script is licensed under the terms of the MIT source code license.
However, this script in particular may be under different licensing terms. To find out how
this script is licensed, please contact whoever sent it to you. Alternatively, you may
run it with the 'license' argument if they have specified a license that way.
You should not edit this file directly. For information about how it was constructed, go
to L. For quick usage guidelines,
run this script with the 'usage' argument.
=cut
__c63395cbc6f7160b603befbb2d9b6700
meta::data('author', 'Spencer Tipping');
meta::data('default-action', 'queue');
meta::data('license', <<'__3c6177256de0fddb721f534c3ad8c0ee');
MIT License
Copyright (c) 2010 Spencer Tipping
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
__3c6177256de0fddb721f534c3ad8c0ee
meta::data('permanent-identity', '2be21c5404f946ec5887e93fc3e452e0');
meta::data('watching', '1');
meta::function('alias', <<'__28744564997657da45ab16cd5b441104');
my ($name, @stuff) = @_;
return ls('-a', '^alias::') unless defined $name;
@stuff ? around_hook('alias', @_, sub {associate("alias::$name", join ' ', @stuff)}) : retrieve("alias::$name") || "Undefined alias $name";
__28744564997657da45ab16cd5b441104
meta::function('cat', 'join "\\n", retrieve(@_);');
meta::function('cc', <<'__c4d52b1d8f52a480f07b81b93c3aac7b');
# Stashes a quick one-line continuation. (Used to remind me what I was doing.)
@_ ? associate('data::current-continuation', hook('set-cc', join(' ', @_))) : retrieve('data::current-continuation');
__c4d52b1d8f52a480f07b81b93c3aac7b
meta::function('ccc', 'rm(\'data::current-continuation\');');
meta::function('child', <<'__f69646398c3123d3d939a7f2b3156606');
around_hook('child', @_, sub {
my ($child_name) = @_;
clone($child_name);
enable();
qx($child_name update-from $0 -n);
disable()});
__f69646398c3123d3d939a7f2b3156606
meta::function('clone', <<'__54e00ff2103e54423d4c9febb97ce063');
for (grep length, @_) {
around_hook('clone', $_, sub {
hypothetically(sub {
rm('data::permanent-identity');
file::write($_, serialize(), noclobber => 1);
chmod(0700, $_)})})}
__54e00ff2103e54423d4c9febb97ce063
meta::function('cp', <<'__e5fee448a74ecbf4ae215e6b43dfc048');
my $from = shift @_;
my $value = retrieve($from);
associate($_, $value) for @_;
__e5fee448a74ecbf4ae215e6b43dfc048
meta::function('create', <<'__090c342a2dc304b39c643d53350474a0');
my ($name, $value) = @_;
around_hook('create', $name, $value, sub {
return edit($name) if exists $data{$name};
associate($name, defined $value ? $value : '');
edit($name) unless defined $value});
__090c342a2dc304b39c643d53350474a0
meta::function('current-state', <<'__d83ae43551c0f58d1d0ce576402a315a');
my @valid_keys = grep ! /^state::/, sort keys %data;
my @ordered_keys = (grep(/^meta::/, @valid_keys), grep(! /^meta::/, @valid_keys));
join "\n", map serialize_single($_), @ordered_keys;
__d83ae43551c0f58d1d0ce576402a315a
meta::function('disable', 'hook(\'disable\', chmod_self(sub {$_[0] & 0666}));');
meta::function('edit', <<'__36bd3f2c1165d0c041b0fa9a96a85378');
my ($name, %options) = @_;
my $extension = extension_for($name);
die "$name is virtual or does not exist" unless exists $data{$name};
die "$name is inherited; use 'edit $name -f' to edit anyway" unless is($name, '-u') || exists $options{'-f'};
around_hook('edit', @_, sub {
associate($name, invoke_editor_on($data{$name} || "# Attribute $name", %options, attribute => $name, extension => $extension),
execute => $name !~ /^bootstrap::/)});
save() unless $data{'data::edit::no-save'};
'';
__36bd3f2c1165d0c041b0fa9a96a85378
meta::function('enable', 'hook(\'enable\', chmod_self(sub {$_[0] | $_[0] >> 2}));');
meta::function('export', <<'__388e0cc60507443cb1c0cc3e2658cfef');
# Exports data into a text file.
# export attr1 attr2 attr3 ... file.txt
my $name = pop @_;
@_ or die 'Expected filename';
file::write($name, join "\n", retrieve(@_));
__388e0cc60507443cb1c0cc3e2658cfef
meta::function('extern', '&{$_[0]}(retrieve(@_[1 .. $#_]));');
meta::function('grep', <<'__f2f28b81f4bcf4cf9fbe9e27bc9447b0');
# Looks through attributes for a pattern. Usage is grep pattern [options], where
# [options] is the format as provided to select_keys.
my ($pattern, @args) = @_;
my ($options, @criteria) = separate_options(@args);
my @attributes = select_keys(%$options, '--criteria' => join('|', @criteria));
$pattern = qr/$pattern/;
my @m_attributes, @m_line_numbers, @m_lines;
for my $k (@attributes) {
next unless length $k;
my @lines = split /\n/, retrieve($k);
for (0 .. $#lines) {
next unless $lines[$_] =~ $pattern;
push @m_attributes, $k;
push @m_line_numbers, $_ + 1;
push @m_lines, $lines[$_] // ''}}
if ($$options{'-c'}) {
s/($pattern)/\033[1;31m\1\033[0;0m/g for @m_lines;
s/^/\033[1;34m/o for @m_attributes;
s/^/\033[1;32m/o && s/$/\033[0;0m/o for @m_line_numbers}
table_display([@m_attributes], [@m_line_numbers], [@m_lines]);
__f2f28b81f4bcf4cf9fbe9e27bc9447b0
meta::function('hash', 'fast_hash(@_);');
meta::function('hook', <<'__d74d8e2b611342af6a0897e0bd62e6e6');
my ($hook, @args) = @_;
$transient{active_hooks}{$hook} = 1;
dangerous('', sub {&$_(@args)}) for grep /^hook::${hook}::/, sort keys %data;
@args;
__d74d8e2b611342af6a0897e0bd62e6e6
meta::function('hooks', 'join "\\n", sort keys %{$transient{active_hooks}};');
meta::function('identity', 'retrieve(\'data::permanent-identity\') || associate(\'data::permanent-identity\', fast_hash(rand() . name() . serialize()));');
meta::function('import', <<'__ac86cbe9c9fb12fc8cef2cc88e80c01e');
my $name = pop @_;
associate($name, @_ ? join('', map(file::read($_), @_)) : join('', ));
__ac86cbe9c9fb12fc8cef2cc88e80c01e
meta::function('import-bundle', <<'__4c7139ed5c9f65f38a33cf8f8a6cae27');
eval join '', ;
die $@ if $@;
__4c7139ed5c9f65f38a33cf8f8a6cae27
meta::function('initial-state', '$transient{initial};');
meta::function('is', <<'__5d4fddb72b0715694b83aa0c925a8d04');
my ($attribute, @criteria) = @_;
my ($options, @stuff) = separate_options(@criteria);
grep $_ eq $attribute, select_keys(%$options);
__5d4fddb72b0715694b83aa0c925a8d04
meta::function('load-state', <<'__ea18db867bd62a294e067f60e6975dcf');
around_hook('load-state', @_, sub {
my ($state_name) = @_;
my $state = retrieve("state::$state_name");
terminal::state('saving current state into _...');
&{'save-state'}('_');
delete $data{$_} for grep ! /^state::/, keys %data;
%externalized_functions = ();
terminal::state("restoring state $state_name...");
meta::eval_in($state, "state::$state_name");
terminal::error(hook('load-state-failed', $@)) if $@;
reload();
verify()});
__ea18db867bd62a294e067f60e6975dcf
meta::function('lock', 'hook(\'lock\', chmod_self(sub {$_[0] & 0555}));');
meta::function('ls', <<'__7585c2f886d5a8f0258b8749374da6bc');
my ($options, @criteria) = separate_options(@_);
my ($all, $shadows, $dereference, $sizes, $flags, $hashes) = @$options{qw(-a -s -d -z -l -h)};
$all ||= $dereference;
$sizes ||= $flags;
return table_display([grep ! exists $data{$externalized_functions{$_}}, sort keys %externalized_functions]) if $shadows;
my $criteria = join('|', @criteria);
my @definitions = select_keys('--criteria' => $criteria, %$options);
my %inverses = map {$externalized_functions{$_} => $_} keys %externalized_functions;
my @externals = map $inverses{$_}, grep length, @definitions;
my @internals = grep length $inverses{$_}, @definitions;
my @sizes = map sprintf('%6d %6d', length(serialize_single($_)), length(retrieve($_))), @{$all ? \@definitions : \@internals} if $sizes;
my @hashes = map fast_hash(retrieve($_)), @definitions if $hashes;
my %flag_hashes = map {$_ => {map {$_ => 1} select_keys("-$_" => 1)}} qw(m u i) if $flags;
my @flags = map {my $k = $_; join '', map($flag_hashes{$_}{$k} ? $_ : '-', sort keys %flag_hashes)} @definitions if $flags;
join "\n", map strip($_), split /\n/, table_display($all ? [@definitions] : [grep length, @externals], $dereference ? ([@externals]) : (),
$sizes ? ([@sizes]) : (), $hashes ? ([@hashes]) : (), $flags ? ([@flags]) : ());
__7585c2f886d5a8f0258b8749374da6bc
meta::function('ls-a', 'ls(\'-ad\', @_);');
meta::function('mv', <<'__52e95180e3c7019116bd798e0da0fdda');
my ($from, $to) = @_;
die "'$from' does not exist" unless exists $data{$from};
associate($to, retrieve($from));
rm($from);
__52e95180e3c7019116bd798e0da0fdda
meta::function('name', <<'__6848cbc257e4b6d7441b25acb04e23c9');
my $name = $0;
$name =~ s/^.*\///;
$name;
__6848cbc257e4b6d7441b25acb04e23c9
meta::function('note', <<'__bcbfeac6dd2112f47296265444570a6e');
# Creates a note with a given name, useful for jotting things down.
create("note::$_[0]");
__bcbfeac6dd2112f47296265444570a6e
meta::function('notes', 'ls(\'-a\', \'^note::\');');
meta::function('parents', 'join "\\n", grep s/^parent:://o, sort keys %data;');
meta::function('perl', <<'__986a274c013b77fe08d29726ce3799fe');
my $result = eval(join ' ', @_);
$@ ? terminal::error($@) : $result;
__986a274c013b77fe08d29726ce3799fe
meta::function('reload', 'around_hook(\'reload\', sub {execute($_) for grep ! /^bootstrap::/, keys %data});');
meta::function('rm', <<'__7cecfb1691a7bf86741f00058bcc54ca');
around_hook('rm', @_, sub {
exists $data{$_} or terminal::warning("$_ does not exist") for @_;
delete @data{@_}});
__7cecfb1691a7bf86741f00058bcc54ca
meta::function('save', 'around_hook(\'save\', sub {dangerous(\'\', sub {file::write($0, serialize()); $transient{initial} = state()}) if verify()});');
meta::function('save-state', <<'__863e4d9fa75ca198ef7a755248d1002a');
# Creates a named copy of the current state and stores it.
my ($state_name) = @_;
around_hook('save-state', $state_name, sub {
associate("state::$state_name", &{'current-state'}(), execute => 1)});
__863e4d9fa75ca198ef7a755248d1002a
meta::function('serialize', <<'__5148e8ca46eeb3e297f76d098e496bcf');
my ($options, @criteria) = separate_options(@_);
my $partial = $$options{'-p'};
my $criteria = join '|', @criteria;
my @attributes = map serialize_single($_), select_keys(%$options, '-m' => 1, '--criteria' => $criteria), select_keys(%$options, '-M' => 1, '--criteria' => $criteria);
my @final_array = @{$partial ? \@attributes : [retrieve('bootstrap::initialization'), @attributes, 'internal::main();', '', '__END__']};
join "\n", @final_array;
__5148e8ca46eeb3e297f76d098e496bcf
meta::function('serialize_single', <<'__ef0f63556d22816ed102d3bbe2172b28');
# Serializes a single attribute and optimizes for content.
my $name = $_[0] || $_;
my $contents = $data{$name};
my $meta_function = 'meta::' . namespace($name);
my $invocation = attribute($name);
my $escaped = $contents;
$escaped =~ s/\\/\\\\/go;
$escaped =~ s/'/\\'/go;
return "$meta_function('$invocation', '$escaped');" unless $escaped =~ /\v/;
my $delimiter = '__' . fast_hash($contents);
return "$meta_function('$invocation', <<'$delimiter');\n$contents\n$delimiter";
__ef0f63556d22816ed102d3bbe2172b28
meta::function('sh', 'system(@_);');
meta::function('shell', <<'__dc8238ad70b1e02eaf230c4f1bd21690');
terminal::cc(retrieve('data::current-continuation')) if length $data{'data::current-continuation'};
shell::repl();
__dc8238ad70b1e02eaf230c4f1bd21690
meta::function('size', 'length(serialize());');
meta::function('snapshot', <<'__d3d84a364524eeb8ee90623f545187e8');
my ($name) = @_;
file::write(my $finalname = temporary_name($name), serialize(), noclobber => 1);
chmod 0700, $finalname;
hook('snapshot', $finalname);
__d3d84a364524eeb8ee90623f545187e8
meta::function('state', <<'__119111f84c3e32a5536838ac84bc6f10');
my @keys = sort keys %data;
my $hash = fast_hash(fast_hash(scalar @keys) . join '|', @keys);
$hash = fast_hash("$data{$_}|$hash") for @keys;
$hash;
__119111f84c3e32a5536838ac84bc6f10
meta::function('touch', 'associate($_, \'\') for @_;');
meta::function('unlock', 'hook(\'unlock\', chmod_self(sub {$_[0] | 0200}));');
meta::function('update', '&{\'update-from\'}(@_, grep s/^parent:://o, sort keys %data);');
meta::function('update-from', <<'__8590779bfe8c02cbacd03ced167567d9');
# Upgrade all attributes that aren't customized. Customization is defined when the data type is created,
# and we determine it here by checking for $transient{inherit}{$type}.
# Note that this assumes you trust the remote script. If you don't, then you shouldn't update from it.
around_hook('before-update-from-invocation', separate_options(@_), sub {
my ($options, @targets) = @_;
@targets or return;
my $save_state = ! ($$options{'-n'} || $$options{'--no-save'});
my $no_parents = $$options{'-P'} || $$options{'--no-parent'} || $$options{'--no-parents'};
my $force = $$options{'-f'} || $$options{'--force'};
my $clobber_divergent = $$options{'-D'} || $$options{'--clobber-divergent'};
&{'save-state'}('before-update') if $save_state;
for my $target (@targets) {
dangerous("updating from $target", sub {
around_hook('update-from', $target, sub {
my $attributes = join '', qx($target ls -ahiu);
my %divergent;
die "skipping unreachable $target" unless $attributes;
for my $to_rm (split /\n/, retrieve("parent::$target")) {
my ($name, $hash) = split(/\s+/, $to_rm);
next unless exists $data{$name};
my $local_hash = fast_hash(retrieve($name));
terminal::debug("comparing $local_hash with $hash");
if ($hash eq $local_hash or ! defined $hash) {rm($name)}
else {terminal::info("preserving local version of divergent attribute $name");
$divergent{$name} = retrieve($name)}}
associate("parent::$target", $attributes) unless $no_parents;
dangerous('', sub {eval qx($target serialize -ipmu)});
dangerous('', sub {eval qx($target serialize -ipMu)});
map associate($_, $divergent{$_}), keys %divergent unless $clobber_divergent;
reload()})})}
if (verify()) {hook('update-from-succeeded', $options, @targets);
terminal::info("Successfully updated. Run 'load-state before-update' to undo this change.") if $save_state}
elsif ($force) {hook('update-from-failed', $options, @targets);
terminal::warning('Failed to verify: at this point your object will not save properly, though backup copies will be created.',
'Run "load-state before-update" to undo the update and return to a working state.') if $save_state}
else {hook('update-from-failed', $options, @targets);
terminal::error('Verification failed after the upgrade was complete.');
terminal::info("$0 has been reverted to its pre-upgrade state.", "If you want to upgrade and keep the failure state, then run 'update-from $target --force'.") if $save_state;
return &{'load-state'}('before-update') if $save_state}});
__8590779bfe8c02cbacd03ced167567d9
meta::function('usage', '"Usage: $0 action [arguments]\\nUnique actions (run \'$0 ls\' to see all actions):" . ls(\'-u\');');
meta::function('verify', <<'__d31b85fffd464ddf516d2afeb63dcbde');
file::write(my $other = $transient{temporary_filename} = temporary_name(), my $serialized_data = serialize());
chomp(my $observed = join '', qx|perl '$other' state|);
unlink $other if my $result = $observed eq (my $state = state());
terminal::error("Verification failed; expected $state but got $observed from $other") unless $result;
hook('after-verify', $result, observed => $observed, expected => $state);
$result;
__d31b85fffd464ddf516d2afeb63dcbde
meta::internal_function('around_hook', <<'__e1cd17b80d4e8165df9c94facd9f239b');
# around_hook('hookname', @args, sub {
# stuff;
# });
# Invokes 'before-hookname' on @args before the sub runs, invokes the
# sub on @args, then invokes 'after-hookname' on @args afterwards.
# The after-hook is not invoked if the sub calls 'die' or otherwise
# unwinds the stack.
my $hook = shift @_;
my $f = pop @_;
hook("before-$hook", @_);
my $result = &$f(@_);
hook("after-$hook", @_);
$result;
__e1cd17b80d4e8165df9c94facd9f239b
meta::internal_function('associate', <<'__fc4f785bcf3ffe3225a73a1fdd314703');
my ($name, $value, %options) = @_;
die "Namespace does not exist" unless exists $datatypes{namespace($name)};
$data{$name} = $value;
execute($name) if $options{'execute'};
$value;
__fc4f785bcf3ffe3225a73a1fdd314703
meta::internal_function('attribute', <<'__62efb9f22157835940af1d5feae98d98');
my ($name) = @_;
$name =~ s/^[^:]*:://;
$name;
__62efb9f22157835940af1d5feae98d98
meta::internal_function('chmod_self', <<'__b13487447c65f2dc790bd6b21dde89dd');
my ($mode_function) = @_;
my (undef, undef, $mode) = stat $0;
chmod &$mode_function($mode), $0;
__b13487447c65f2dc790bd6b21dde89dd
meta::internal_function('complete', <<'__f14ae2337c0653b6bb6fd02bb6493646');
my @functions = sort keys %externalized_functions;
my @attributes = sort keys %data;
sub match {
my ($text, @options) = @_;
my @matches = sort grep /^$text/, @options;
if (@matches == 0) {return undef;}
elsif (@matches == 1) {return $matches [0];}
elsif (@matches > 1) {return ((longest ($matches [0], $matches [@matches - 1])), @matches);}
}
sub longest {
my ($s1, $s2) = @_;
return substr ($s1, 0, length $1) if ($s1 ^ $s2) =~ /^(\0*)/;
return '';
}
# This is another way to implement autocompletion.
#
# my $attribs = $term->Attribs;
# $attribs->{completion_entry_function} = $attribs->{list_completion_function};
# $attribs->{completion_word} = [sort keys %data, sort keys %externalized_functions];
my ($text, $line) = @_;
if ($line =~ / /) {
# Start matching attribute names.
match ($text, @attributes);
} else {
# Start of line, so it's a function.
match ($text, @functions);
}
__f14ae2337c0653b6bb6fd02bb6493646
meta::internal_function('dangerous', <<'__4b8343178d6d4d1b760d61b1cfda008c');
# Wraps a computation that may produce an error.
my ($message, $computation) = @_;
terminal::info($message) if $message;
my @result = eval {&$computation()};
terminal::warning(translate_backtrace($@)), return undef if $@;
wantarray ? @result : $result[0];
__4b8343178d6d4d1b760d61b1cfda008c
meta::internal_function('debug_trace', <<'__77644ab45a770a6e172680f659911507');
terminal::debug(join ', ', @_);
wantarray ? @_ : $_[0];
__77644ab45a770a6e172680f659911507
meta::internal_function('execute', <<'__4b4efc33bc6767a7aade7f427eedf83f');
my ($name, %options) = @_;
my $namespace = namespace($name);
eval {&{"meta::$namespace"}(attribute($name), retrieve($name))};
warn $@ if $@ && $options{'carp'};
__4b4efc33bc6767a7aade7f427eedf83f
meta::internal_function('exported', <<'__27414e8f2ceeaef3555b9726e690eb0f');
# Allocates a temporary file containing the concatenation of attributes you specify,
# and returns the filename. The filename will be safe for deletion anytime.
my $filename = temporary_name();
file::write($filename, cat(@_));
$filename;
__27414e8f2ceeaef3555b9726e690eb0f
meta::internal_function('extension_for', <<'__65e48f50f20bc04aa561720b03bf494c');
my $extension = $transient{extension}{namespace($_[0])};
$extension = &$extension($_[0]) if ref $extension eq 'CODE';
$extension || '';
__65e48f50f20bc04aa561720b03bf494c
meta::internal_function('fast_hash', <<'__ac70f469e697725cfb87629833434ab1');
my ($data) = @_;
my $piece_size = length($data) >> 3;
my @pieces = (substr($data, $piece_size * 8) . length($data), map(substr($data, $piece_size * $_, $piece_size), 0 .. 7));
my @hashes = (fnv_hash($pieces[0]));
push @hashes, fnv_hash($pieces[$_ + 1] . $hashes[$_]) for 0 .. 7;
$hashes[$_] ^= $hashes[$_ + 4] >> 16 | ($hashes[$_ + 4] & 0xffff) << 16 for 0 .. 3;
$hashes[0] ^= $hashes[8];
sprintf '%08x' x 4, @hashes[0 .. 3];
__ac70f469e697725cfb87629833434ab1
meta::internal_function('file::read', <<'__186bbcef8f6f0dd8b72ba0fdeb1de040');
my $name = shift;
open my($handle), "<", $name;
my $result = join "", <$handle>;
close $handle;
$result;
__186bbcef8f6f0dd8b72ba0fdeb1de040
meta::internal_function('file::write', <<'__eb7b1efebe0db73378b0cce46681788d');
use File::Path 'mkpath';
use File::Basename 'dirname';
my ($name, $contents, %options) = @_;
die "Choosing not to overwrite file $name" if $options{noclobber} and -f $name;
mkpath(dirname($name)) if $options{mkpath};
open my($handle), $options{append} ? '>>' : '>', $name or die "Can't open $name for writing";
print $handle $contents;
close $handle;
__eb7b1efebe0db73378b0cce46681788d
meta::internal_function('fnv_hash', <<'__8d001a3a7988631bab21a41cee559758');
# A rough approximation to the Fowler-No Voll hash. It's been 32-bit vectorized
# for efficiency, which may compromise its effectiveness for short strings.
my ($data) = @_;
my ($fnv_prime, $fnv_offset) = (16777619, 2166136261);
my $hash = $fnv_offset;
my $modulus = 2 ** 32;
$hash = ($hash ^ ($_ & 0xffff) ^ ($_ >> 16)) * $fnv_prime % $modulus for unpack 'L*', $data . substr($data, -4) x 8;
$hash;
__8d001a3a7988631bab21a41cee559758
meta::internal_function('hypothetically', <<'__33ee2e1595d3877bd1d9accaa72305c8');
# Applies a temporary state and returns a serialized representation.
# The original state is restored after this, regardless of whether the
# temporary state was successful.
my %data_backup = %data;
my ($side_effect) = @_;
my $return_value = eval {&$side_effect()};
%data = %data_backup;
die $@ if $@;
$return_value;
__33ee2e1595d3877bd1d9accaa72305c8
meta::internal_function('internal::main', <<'__435a9e83ac803960745d9aa5aac6c75f');
disable();
$SIG{'INT'} = sub {snapshot(); exit 1};
$transient{initial} = state();
chomp(my $default_action = retrieve('data::default-action'));
my $function_name = shift(@ARGV) || $default_action || 'usage';
terminal::warning("unknown action: '$function_name'") and $function_name = 'usage' unless $externalized_functions{$function_name};
around_hook('main-function', $function_name, @ARGV, sub {
dangerous('', sub {
chomp(my $result = &$function_name(@ARGV));
print "$result\n" if $result})});
save() unless state() eq $transient{initial};
END {
enable();
}
__435a9e83ac803960745d9aa5aac6c75f
meta::internal_function('invoke_editor_on', <<'__1448132d5294a4b8390b4a684d8a78f9');
my ($data, %options) = @_;
my $editor = $options{editor} || $ENV{VISUAL} || $ENV{EDITOR} || die 'Either the $VISUAL or $EDITOR environment variable should be set to a valid editor';
my $options = $options{options} || $ENV{VISUAL_OPTS} || $ENV{EDITOR_OPTS} || '';
my $attribute = $options{attribute};
$attribute =~ s/\//-/g;
my $filename = temporary_name() . "-$attribute$options{extension}";
file::write($filename, $data);
system("$editor $options '$filename'");
my $result = file::read($filename);
unlink $filename;
$result;
__1448132d5294a4b8390b4a684d8a78f9
meta::internal_function('is_locked', '!((stat($0))[2] & 0222);');
meta::internal_function('namespace', <<'__93213d60cafb9627e0736b48cd1f0760');
my ($name) = @_;
$name =~ s/::.*$//;
$name;
__93213d60cafb9627e0736b48cd1f0760
meta::internal_function('retrieve', <<'__0b6f4342009684fdfa259f45ac75ae37');
my @results = map defined $data{$_} ? $data{$_} : retrieve_with_hooks($_), @_;
wantarray ? @results : $results[0];
__0b6f4342009684fdfa259f45ac75ae37
meta::internal_function('retrieve_with_hooks', <<'__5186a0343624789d08d1cc2084550f3d');
# Uses the hooks defined in $transient{retrievers}, and returns undef if none work.
my ($attribute) = @_;
my $result = undef;
defined($result = &$_($attribute)) and return $result for map $transient{retrievers}{$_}, sort keys %{$transient{retrievers}};
return undef;
__5186a0343624789d08d1cc2084550f3d
meta::internal_function('select_keys', <<'__8ee1d5fa37927c66d9eec4d0d8269493');
my %options = @_;
my %inherited = map {$_ => 1} split /\n/o, join "\n", retrieve(grep /^parent::/o, sort keys %data) if $options{'-u'} or $options{'-U'};
my $criteria = $options{'--criteria'} || $options{'--namespace'} && "^$options{'--namespace'}::" || '.';
grep /$criteria/ && (! $options{'-u'} || ! $inherited{$_}) && (! $options{'-U'} || $inherited{$_}) &&
(! $options{'-I'} || ! $transient{inherit}{namespace($_)}) && (! $options{'-i'} || $transient{inherit}{namespace($_)}) &&
(! $options{'-S'} || ! /^state::/o) && (! $options{'-M'} || ! /^meta::/o) && (! $options{'-m'} || /^meta::/o), sort keys %data;
__8ee1d5fa37927c66d9eec4d0d8269493
meta::internal_function('separate_options', <<'__d47e8ee23fe55e27bb523c9fcb2f5ca1');
# Things with one dash are short-form options, two dashes are long-form.
# Characters after short-form are combined; so -auv4 becomes -a -u -v -4.
# Also finds equivalences; so --foo=bar separates into $$options{'--foo'} eq 'bar'.
# Stops processing at the -- option, and removes it. Everything after that
# is considered to be an 'other' argument.
# The only form not supported by this function is the short-form with argument.
# To pass keyed arguments, you need to use long-form options.
my @parseable;
push @parseable, shift @_ until ! @_ or $_[0] eq '--';
my @singles = grep /^-[^-]/, @parseable;
my @longs = grep /^--/, @parseable;
my @others = grep ! /^-/, @parseable;
my @singles = map /-(.{2,})/ ? map("-$_", split(//, $1)) : $_, @singles;
my %options;
$options{$1} = $2 for grep /^([^=]+)=(.*)$/, @longs;
++$options{$_} for grep ! /=/, @singles, @longs;
({%options}, @others, @_);
__d47e8ee23fe55e27bb523c9fcb2f5ca1
meta::internal_function('strip', 'wantarray ? map {s/^\\s*|\\s*$//g; $_} @_ : $_[0] =~ /^\\s*(.*?)\\s*$/ && $1;');
meta::internal_function('table_display', <<'__8a6897e093f36bf05477a3889b84a61d');
# Displays an array of arrays as a table; that is, with alignment. Arrays are
# expected to be in column-major order.
sub maximum_length_in {
my $maximum = 0;
length > $maximum and $maximum = length for @_;
$maximum;
}
my @arrays = @_;
my @lengths = map maximum_length_in(@$_), @arrays;
my @row_major = map {my $i = $_; [map $$_[$i], @arrays]} 0 .. $#{$arrays[0]};
my $format = join ' ', map "%-${_}s", @lengths;
join "\n", map strip(sprintf($format, @$_)), @row_major;
__8a6897e093f36bf05477a3889b84a61d
meta::internal_function('temporary_name', <<'__0fb1402061581b69822f913631b4a9d9');
use File::Temp 'tempfile';
my (undef, $temporary_filename) = tempfile("$0." . 'X' x 4, OPEN => 0);
$temporary_filename;
__0fb1402061581b69822f913631b4a9d9
meta::internal_function('translate_backtrace', <<'__06fad3d85833a6484e426401b95e0206');
my ($trace) = @_;
$trace =~ s/\(eval (\d+)\)/$locations{$1 - 1}/g;
$trace;
__06fad3d85833a6484e426401b95e0206
meta::internal_function('with_exported', <<'__fc4f32c46d95c6deed0414364d1c7410');
# Like exported(), but removes the file after running some function.
# Usage is with_exported(@files, sub {...});
my $f = pop @_;
my $name = exported(@_);
my $result = eval {&$f($name)};
terminal::warning("$@ when running with_exported()") if $@;
unlink $name;
$result;
__fc4f32c46d95c6deed0414364d1c7410
meta::library('shell', <<'__528f486cc4d9eb390e4c350b8727c751');
# Functions for shell parsing and execution.
package shell;
use Term::ReadLine;
sub tokenize {grep length, split /\s+|("[^"\\]*(?:\\.)?")/o, join ' ', @_};
sub parse {
my ($fn, @args) = @_;
s/^"(.*)"$/\1/o, s/\\\\"/"/go for @args;
{function => $fn, args => [@args]}}
sub execute {
my %command = %{$_[0]};
die "undefined command: $command{function}" unless exists $externalized_functions{$command{function}};
&{"::$command{function}"}(@{$command{args}})}
sub run {execute(parse(tokenize(@_)))}
sub prompt {
my %options = @_;
my $name = $options{name} // ::name();
my $state = $options{state} // ::state();
my $other = $state ne $transient{initial} ? 33 : 30;
my $locked = ::is_locked() ? "\033[1;31mlocked\033[0;0m" : '';
my $cc = length ::retrieve('data::current-continuation') ? "\033[1;36mcc\033[0;0m" : '';
"\033[1;32m$name\033[1;${other}m" . substr($state, 0, 4) . "\033[0;0m$cc$locked\033[1;34m$options{stuff}\033[0;0m "}
sub repl {
my %options = @_;
my $term = new Term::ReadLine "$0 shell";
$term->ornaments(0);
my $attribs = $term->Attribs;
$attribs->{completion_entry_function} = $attribs->{list_completion_function};
my $autocomplete = $options{autocomplete} || sub {[sort keys %data, sort keys %externalized_functions]};
my $prompt = $options{prompt} || \&prompt;
my $parse = $options{parse} || sub {parse(tokenize(@_))};
my $command = $options{command} || sub {my ($command) = @_; ::around_hook('shell-command', $command, sub {print ::dangerous('', sub {execute($command)}), "\n"})};
&$command(&$parse($_)) while ($attribs->{completion_word} = &$autocomplete(), defined($_ = $term->readline(&$prompt())))}
__528f486cc4d9eb390e4c350b8727c751
meta::library('terminal', <<'__c52308d05ebb4ff61c5fc36e6d9c7a8a');
# Functions for nice-looking terminal output.
package terminal;
my $process = ::name();
sub message {print STDERR "[$_[0]] $_[1]\n"}
sub color {
my ($name, $color) = @_;
*{"terminal::$name"} = sub {chomp($_), print STDERR "\033[1;30m$process(\033[1;${color}m$name\033[1;30m)\033[0;0m $_\n" for map join('', $_), @_}}
my %preloaded = (info => 32, progress => 32, state => 34, debug => 34, warning => 33, error => 31);
color $_, $preloaded{$_} for keys %preloaded;
__c52308d05ebb4ff61c5fc36e6d9c7a8a
meta::message_color('cc', '36');
meta::message_color('state', 'purple');
meta::message_color('states', 'yellow');
meta::message_color('watch', 'blue');
meta::note('queue', <<'__5fd27e7684a58beb914b27cd8518abbd');
Queue.
This stores todo items in whatever format you care to use.
__5fd27e7684a58beb914b27cd8518abbd
meta::parent('/home/spencertipping/bin/notes', <<'__885245820bcb60424dfcbd480e5b966e');
function::note
function::notes
meta::type::note
parent::object
__885245820bcb60424dfcbd480e5b966e
meta::parent('object', <<'__c5a03c5766c0b29491bd506ab06ef998');
bootstrap::html aa347c4339e71e7acc9ea4fc3d593347
bootstrap::initialization 8774229a1a0ce7fd056d81ba0b077f79
bootstrap::perldoc c63395cbc6f7160b603befbb2d9b6700
function::alias 28744564997657da45ab16cd5b441104
function::cat a19fdfda461f9a0aa01978dff2e2c2f7
function::cc c4d52b1d8f52a480f07b81b93c3aac7b
function::ccc 2351344fc688518c75aa4ab3acec1c4a
function::child f69646398c3123d3d939a7f2b3156606
function::clone 54e00ff2103e54423d4c9febb97ce063
function::cp e5fee448a74ecbf4ae215e6b43dfc048
function::create 090c342a2dc304b39c643d53350474a0
function::current-state d83ae43551c0f58d1d0ce576402a315a
function::disable 49db26fcd680ca34c1f52629a7375d2f
function::edit 36bd3f2c1165d0c041b0fa9a96a85378
function::enable 37af2d2e603e2455be227ae3c5a42c3a
function::export 388e0cc60507443cb1c0cc3e2658cfef
function::extern 6a9fa8c2a8a4eaae9fe8d38e402145e4
function::grep f2f28b81f4bcf4cf9fbe9e27bc9447b0
function::hash 7a903f90f2c8ed27af7e030f407d9f7b
function::hook d74d8e2b611342af6a0897e0bd62e6e6
function::hooks 230122bdc8929884e45b2f78a7743e2e
function::identity 37106ce13a0200af001d361ce7e81e57
function::import ac86cbe9c9fb12fc8cef2cc88e80c01e
function::initial-state e21ba3519838b221a7b2d4e8a7544e7f
function::is 5d4fddb72b0715694b83aa0c925a8d04
function::load-state ea18db867bd62a294e067f60e6975dcf
function::lock 9bf21fee2f0f809131d43553bde82fa5
function::ls 7585c2f886d5a8f0258b8749374da6bc
function::mv 52e95180e3c7019116bd798e0da0fdda
function::name 6848cbc257e4b6d7441b25acb04e23c9
function::parents f94c3a5addbc92fe7f90d198fa701484
function::perl 986a274c013b77fe08d29726ce3799fe
function::reload c57ff432c3ffd91a5506cd3eb8bf50c9
function::rm 7cecfb1691a7bf86741f00058bcc54ca
function::save 181da4858ac39c157dbf38e6bac7a0d2
function::save-state 863e4d9fa75ca198ef7a755248d1002a
function::serialize 5148e8ca46eeb3e297f76d098e496bcf
function::serialize_single ef0f63556d22816ed102d3bbe2172b28
function::sh 9647fa9227bef6c139a79d0dd4acc8b4
function::shell dc8238ad70b1e02eaf230c4f1bd21690
function::size ed32c644d604fdc61dda48bd3fbe5559
function::snapshot d3d84a364524eeb8ee90623f545187e8
function::state 119111f84c3e32a5536838ac84bc6f10
function::touch 819878bc64df094d3323c7050f2c3e97
function::unlock 8fc9bd69f3466f0b54ee2c6965f68cea
function::update 4de1a6a4085836590a3b1ef997f9d5ea
function::update-from 8590779bfe8c02cbacd03ced167567d9
function::usage b36ead828ad566c8e3919f3b40fb99e6
function::verify d31b85fffd464ddf516d2afeb63dcbde
internal_function::around_hook e1cd17b80d4e8165df9c94facd9f239b
internal_function::associate fc4f785bcf3ffe3225a73a1fdd314703
internal_function::attribute 62efb9f22157835940af1d5feae98d98
internal_function::chmod_self b13487447c65f2dc790bd6b21dde89dd
internal_function::dangerous 4b8343178d6d4d1b760d61b1cfda008c
internal_function::debug_trace 77644ab45a770a6e172680f659911507
internal_function::execute 4b4efc33bc6767a7aade7f427eedf83f
internal_function::exported 27414e8f2ceeaef3555b9726e690eb0f
internal_function::extension_for 65e48f50f20bc04aa561720b03bf494c
internal_function::fast_hash ac70f469e697725cfb87629833434ab1
internal_function::file::read 186bbcef8f6f0dd8b72ba0fdeb1de040
internal_function::file::write eb7b1efebe0db73378b0cce46681788d
internal_function::fnv_hash 8d001a3a7988631bab21a41cee559758
internal_function::hypothetically 33ee2e1595d3877bd1d9accaa72305c8
internal_function::internal::main 435a9e83ac803960745d9aa5aac6c75f
internal_function::invoke_editor_on 1448132d5294a4b8390b4a684d8a78f9
internal_function::is_locked 42c3c89625863a31105d1df49a2a762f
internal_function::namespace 93213d60cafb9627e0736b48cd1f0760
internal_function::retrieve 0b6f4342009684fdfa259f45ac75ae37
internal_function::retrieve_with_hooks 5186a0343624789d08d1cc2084550f3d
internal_function::select_keys 8ee1d5fa37927c66d9eec4d0d8269493
internal_function::separate_options d47e8ee23fe55e27bb523c9fcb2f5ca1
internal_function::strip 4af6a470effeed94c0dd9800d01f7d66
internal_function::table_display 8a6897e093f36bf05477a3889b84a61d
internal_function::temporary_name 0fb1402061581b69822f913631b4a9d9
internal_function::translate_backtrace 06fad3d85833a6484e426401b95e0206
internal_function::with_exported fc4f32c46d95c6deed0414364d1c7410
library::shell 528f486cc4d9eb390e4c350b8727c751
library::terminal c52308d05ebb4ff61c5fc36e6d9c7a8a
message_color::cc 6249446f73b3c5af2404c322c150e57b
message_color::state 14e993fdf2c62df353613c243dc9053b
message_color::states 152a940086f7cee6110528a09af7dd78
meta::configure 25976e07665878d3fae18f050160343f
meta::externalize 9141b4e8752515391385516ae94b23b5
meta::functor::editable e3d2ede6edf65ffe2123584b2bd5dab7
meta::type::alias 28fe15dd61f4902ed5180d8604d15d97
meta::type::bootstrap 297d03fb32df03b46ea418469fc4e49e
meta::type::data 58d8027f20099b28a159eaac67314051
meta::type::function d93b3cc15693707dac518e3d6b1f5648
meta::type::hook f55a3f728ddfb90204dff3fe5d86845c
meta::type::inc c95915391b969734305f2f492d5ca8e3
meta::type::internal_function 34abb44c67c7e282569e28ef6f4d62ab
meta::type::library b6dd78120e6d787acdb5c1629f7f1896
meta::type::message_color 794bf137c425293738f07636bcfb5c55
meta::type::meta 640f25635ce2365b0648962918cf9932
meta::type::parent 607e9931309b1b595424bedcee5dfa45
meta::type::retriever 6e847a9d205e4a5589765a3366cdd115
meta::type::state c1f29670be26f1df6100ffe4334e1202
retriever::file b8e7aefc98b8341260d91f21dc61d749
retriever::id a791a5735e9b4f2cb8b99fd39dc17bc3
__c5a03c5766c0b29491bd506ab06ef998
meta::retriever('file', '-f $_[0] ? file::read($_[0]) : undef;');
meta::retriever('id', '$_[0] =~ /^id::/ ? substr($_[0], 4) : undef;');
__29a
internal::main();
__END__