if (word(2 $loadinfo()) != [pf]) { load -pf $word(1 $loadinfo()); return; }; unload data_struct; package data_struct; # # Struct/Assign manipulation functions. # # The distinction between the two is that the assign functions operate on # the entire alias space whereas the struct functions operate only on sub # structures. The assign functions are typically slower per call, but since # they do all the work in one call, they can be faster than the recursive # struct functions. # # For each assign.* alias, a corresponding alias.*, but some of them have # implicit bugs from the fact that arg lists can't yet be retrieved for stored # aliases. # # # Assign functions. # # Make two aliases for every alias. One for assign handling, one for alias # handling, then make two more for forward and reverse modes. # stack push alias alias.ttt; stack push alias alias.tt; alias alias.ttt (args) { alias $args; @ sar(gr/assign/alias/args); alias $args; }; alias alias.tt (args) { alias.ttt $args; @ sar(gr/assign./assign.r/args); @ sar(gr/pmatch/rpmatch/args); @ sar(gr/;@ :list = revw($list);/;/args); alias.ttt $args; }; # # Check the consistency of the internal structures. The only # reason to use these is if an epic bug is suspected. # alias.ttt assign.check { @ :oxd = xdebug(dword); xdebug dword; @ :omr = aliasctl(maxret 0); @ :last = []; @ :list = aliasctl(assign pmatch "\\[$*\\]"); fe ($list) foo { if (uniq($last $foo)!=sort($last $foo)) { echo assign consistancy failure: $last >= $foo; @ last = foo; }; }; @ aliasctl(maxret $omr); echo Checked $#list assigns matching $*; xdebug $oxd; }; # # Faster version. Tells you whether there's an error, not where it is. # alias.ttt assign.qcheck { @ :oxd = xdebug(dword); xdebug dword; @ :omr = aliasctl(maxret 0); @ :list = aliasctl(assign pmatch "\\[$*\\]"); @ :status = uniq($list) == sort($list) ? [passed] : [failed]; @ aliasctl(maxret $omr); echo Checked $#list assigns matching $*: $status; xdebug $oxd; }; # # Dump all assigns with name matching the given masks. # alias.tt assign.dump { @ :oxd = xdebug(dword); xdebug dword; @ :list = aliasctl(assign pmatch "\\[$*\\]"); fe ($list) e { echo [$#aliasctl(assign get $e)] $e$chr(9)$aliasctl(assign get $e); }; @ list = #list; xdebug $oxd; if (functioncall()) { return $list; } else { echo Dumped $list matching $*; }; }; # # Dump all assigns with name matching the first arg and # contents matching the rest. # alias.tt assign.grep (args) { #@ :oxd = xdebug(dword); #xdebug dword; #@ :list = aliasctl(assign pmatch "\\[$shift(args)\\]"); @ :list = aliasctl(assign pmatch $shift(args)); #xdebug $oxd; fe list foo { if (aliasctl(assign get $foo) !~ args) { @ foo = []; } elsif (!functioncall()) { echo [$aliasctl(assign getpackage $foo)] $foo$chr(9)$aliasctl(assign get $foo); }; }; if (functioncall()) { return $list; } else { echo Dumped $#list matching $*; }; }; # # Delete and reassign all matching vars. Theoretically, this is a no-op, # however, alias.pack will destroy the arg lists of the alias. # alias.tt assign.pack { @ :oxd = xdebug(dword); xdebug dword; do { @ :list = aliasctl(assign $start pmatch "\\[$*\\]"); fe ($list) foo { @ :baz = aliasctl(assign getpackage $foo); @ aliasctl(assign set $foo $aliasctl(assign get $foo)); @ aliasctl(assign setpackage $foo $baz); }; @ list = #list; if (functioncall()) { return $list; } elsif (isdisplaying()) { echo Packed $list matching $*; }; } while (list && (:start += aliasctl(maxret))); xdebug $oxd; }; # # Delete all matching vars. # alias.tt assign.purge { if (functioncall()) { @ :list = aliasctl(assign pmatch "\\[$*\\]"); @ :list = revw($list); fe ($list) bar {^assign -$bar}; return $#list; } else { @ :oxd = xdebug(dword); xdebug dword; do { @ :list = assign.purge($*); if (isdisplaying()) { echo Purged $list matching $*; }; } while (list && list == aliasctl(maxret)); xdebug $oxd; }; }; # # Write matching assigns to a file which can then be /load'ed. Any arg lists # in the original definition won't be saved by alias.save. # alias.tt assign.save { @ :pkg = rand(0); @ :fh = open($0 W); @ :start = []; @ :oxd = xdebug(dword); xdebug dword; do { @ :list = aliasctl(assign $start pmatch "\\[$1-\\]"); fe list foo { if (pkg != aliasctl(assign getpackage $foo)) { @ write($fh PACKAGE ${pkg=aliasctl(assign getpackage $foo)}); }; @ write($fh assign $foo $sar(g/\{/\\\{/$sar(g/\}/\\\}/$aliasctl(assign get $foo)))); @ foo = 0; }; if (functioncall()) { break; } elsif (isdisplaying()) { echo Wrote $#list matching $1-; }; @ :start += aliasctl(maxret); } while (#list && start); @ close($fh); xdebug $oxd; return $#list; }; # # As for .save, but write a $decode() encoded file which won't be # damaged by certain variable contents. # alias.tt assign.esave { @ :pkg = rand(0); @ :fh = open($0 W); @ :start = []; @ :oxd = xdebug(dword); xdebug dword; do { @ :list = aliasctl(assign $start pmatch "\\[$1-\\]"); fe list foo { if (pkg != aliasctl(assign getpackage $foo)) { @ write($fh PACKAGE ${pkg=aliasctl(assign getpackage $foo)}); }; @ write($fh @aliasctl\(assign set $foo \$decode\($encode($aliasctl(assign get $foo))\)\)); @ foo = 0; }; if (functioncall()) { break; } elsif (isdisplaying()) { echo Wrote $#list matching $1-; }; @ :start += aliasctl(maxret); } while (#list && start); @ close($fh); xdebug $oxd; return $#list; }; # # Save the data, then delete it. Repeat until no more data exists. # The reason the procedure is repeated is because of the aforementioned # potential bug that .check checks for. # alias.tt assign.flush { do { @ :bar = assign.save($*); @ :bar = assign.purge($1-); echo Flushed $bar matching $1-; } while (foo != (:foo = bar) || (foo && foo == aliasctl(maxret))); }; # # As above but use .esave. # alias.tt assign.eflush { do { @ :bar = assign.esave($*); @ :bar = assign.purge($1-); echo Flushed $bar matching $1-; } while (foo != (:foo = bar) || (foo && foo == aliasctl(maxret))); }; # # End of /assign.* functions. # stack pop alias alias.tt; stack pop alias alias.ttt; # # struct functions. # # # Recursively erase a structure. # alias struct.purge { fe ($*) foo { ^assign -$foo; }; return ${struct.purgesub($*)+#}; }; # # Continued. # The third sub-loop does what the first does and should never be entered. # It's there for company. # alias struct.purgesub { fe ($*) foo { @ :bar = aliasctl(assign match ${foo}.); @ :bar = revw($bar); @ :hit += #bar; fe ($bar) baz { ^assign -$baz; }; foreach $foo bar { @ hit += struct.purgesub(${foo}.${bar}); }; foreach $foo bar { ^assign -${foo}.${bar}; }; }; return ${0+hit}; }; # # Save a structure, like array.save. # alias struct.savefn { @ :fd = open($0 w); @ :hit = struct.savefd($fd $1-); @ close($fd); return $hit; }; # # Continued. Save to an FD. # alias struct.savefd { @:fd=[$0]; fe ($1-) foo { if (strlen($($foo))) { @ write($fd assign $foo $sar(g/\{/\\\{/$sar(g/\}/\\\}/$aliasctl(assign get $foo)))); @ :hit = 1; } else { @ :hit = 0; }; foreach $foo bar { @ hit += struct.savefd($fd ${foo}.${bar}); }; }; return $hit; }; # # Some basic /assign handling functions. # # /assign.uniq won't work without the functions and data_array scripts. You need to load those manually. # alias assign.insert (var,val) {assign $var $uniq($val $($var));}; alias assign.add (var,val) {assign $var $uniq($($var) $val);}; alias assign.addn (var,val) {assign $var $revw($uniq($revw($($var) $val)));}; alias assign.ifnul {if ([]==[$($0)]){assign $*};}; alias assign.filter { fe ($uniq($aliasctl(assign pmatch "\\[$split(, $0)\\]"))) foo { @ :bar = filter("\\[$1-\\]" $($foo)); unless (bar == [$($foo)]) { assign ${#bar?[]:[-]}$foo $bar; }; }; }; alias assign.pattern $sar(g/filter/pattern/$aliasctl(alias get assign.filter)); alias assign.uniq (args) { bless; @ :num = isnumber(b10 $args) ? shift(args) : 1; @ delarray(assign.uniq); #fe ($args) var { fe ($uniq($aliasctl(assign pmatch "\\[$args\\]"))) var { @ :vars = replace(\$varx x $jot(1 $num)); fe ($($var)) $replace(varx x $jot(1 $num)) { eval setuniqitem assign.uniq $vars; }; assign $var $getandmitems(assign.uniq *); }; };