/[Sack]/trunk/bin/storableedit.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/bin/storableedit.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 37 - (hide annotations)
Wed Sep 23 23:10:50 2009 UTC (14 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 13980 byte(s)
added unmungling to storableedit.pl
1 dpavlin 32 #!/usr/bin/perl
2     use strict;
3     use Storable;
4     my $VERSION = 1.5;
5    
6     #=Description
7     # Simple editor for files saved with storable.
8    
9     # Read file
10     my $filename=shift;
11     my $data;
12     if (! $filename) {
13     print 'This is storableedit.pl, version ', $VERSION, "\n";
14     print "\n";
15     print 'Usage: storableedit.pl FILE', "\n";
16     print ' where FILE is a Perl Storable file', "\n";
17     print "\n";
18     print 'For more information, check out', "\n";
19     print ' perldoc storableedit.pl', "\n";
20     exit;
21     } elsif (-f $filename) {
22     StorableEdit->done('Opening file: ', $filename);
23     $data=retrieve($filename);
24     } else {
25     StorableEdit->done('Creating new file: ', $filename);
26     $data={};
27     }
28    
29     # Edit
30     my $e=StorableEdit->new($data);
31     my $dosave=$e->edit();
32    
33     # Save
34     if ($dosave) {
35     store $e->data(), $filename;
36     StorableEdit->done('File saved: ', $filename);
37     }
38    
39     exit;
40    
41     package StorableEdit;
42     use Storable;
43     use Term::ReadLine;
44    
45 dpavlin 37
46     use lib 'lib';
47     use Sack::Digest;
48     our $digest_opened = 0;
49     sub unshard {
50     my ( $o, $v ) = @_;
51     return $v unless $o->{path}->[0] =~ m{#};
52     $digest_opened ||= Sack::Digest->open(0);
53     # warn "### ",$o->{path}->[0], " $v\n";
54     Sack::Digest->from_int($v);
55     }
56    
57 dpavlin 32 sub new {
58     my $o=shift;
59     my $class=(ref($o) || $o);
60     my $newo=bless {}, $class;
61     $newo->init(@_);
62     return $newo;
63     }
64    
65     # Initializing.
66     sub init {
67     my $o=shift;
68     my $data=shift;
69    
70     $o->data($data);
71     $o->{'changed'}=0;
72     $o->{'readline'}=Term::ReadLine->new('StorableEdit');
73     return;
74     }
75    
76     # Returns the data.
77     sub data {
78     my $o=shift;
79     if (my $set=shift) {
80     $o->{'data'}=$set; # Data
81     $o->{'pathobjects'}=[]; # Path objects
82     $o->{'path'}=[]; # Path to cur
83     $o->{'cur'}=$set; # Currently selected branch
84     }
85     return $o->{'data'};
86     }
87    
88     # Sets the changed flag.
89     sub setchanged {
90     my $o=shift;
91     $o->{'changed'}=1;
92     return;
93     }
94    
95     # Returns the changed flag.
96     sub changed {
97     my $o=shift;
98     return $o->{'changed'};
99     }
100    
101     # Lets the user edit the structure.
102     sub edit {
103     my $o=shift;
104    
105     # Show contents
106     $o->show($o->{'data'}, 50);
107    
108     # Command loop
109     while (1) {
110     my $cmd=$o->input();
111    
112     if (($cmd eq 'quit') || ($cmd eq 'q')) {
113     if ($o->changed()) {
114     $o->error('Changes were made. Use "q!" to quit without saving or "x" to save and quit.');
115     } else {
116     return 0;
117     }
118     } elsif (($cmd eq 'quit!') || ($cmd eq 'q!')) {
119     return 0;
120     } elsif (($cmd eq 'exit') || ($cmd eq 'x')) {
121     return 1;
122     } elsif ($cmd eq 'help') {
123     print 'Commands:', "\n";
124     print ' x, exit: save and quit', "\n";
125     print ' q, quit: quit without saving', "\n";
126     print ' l, ls: list items in current object/reference', "\n";
127     print ' cd ITEM: dive into item (must be reference)', "\n";
128     print ' cd ..: go up', "\n";
129     print ' cd ...: go 2 steps up', "\n";
130     print ' other: evaled as perl code', "\n";
131     print ' $cur: variable of current object/reference', "\n";
132     print ' $base: variable of base object/reference', "\n";
133     print '', "\n";
134     print 'Examples:', "\n";
135     print ' $cur->{\'test\'}=\'hello world\'', "\n";
136     print ' $cur->{\'fruits\'}=[\'apple\', \'banana\']', "\n";
137     print ' $cur->{\'available\'}={\'apple\' => 1, \'banana\' => 5}', "\n";
138     print ' cd available', "\n";
139     print ' l', "\n";
140     print ' bless $cur, \'MyObject\'', "\n";
141     print ' $cur->{\'imported\'}=retrieve(\'importfile.store\')', "\n";
142     print ' store($cur, \'exportfile.store\')', "\n";
143     print ' $data=$cur', "\n";
144     print '', "\n";
145     print 'For a more complete help, see', "\n";
146     print ' perldoc storableedit.pl', "\n";
147     } elsif ($cmd eq 'l') {
148     $o->show($o->{'cur'}, 50);
149     } elsif ($cmd eq 'ls') {
150     $o->show($o->{'cur'});
151     } elsif ($cmd =~ /^cd\s+(.*?)$/) {
152     my $cd=$1;
153     $o->cd($cd);
154     } elsif ($cmd eq 'cd') {
155     } elsif ($cmd eq '..') {
156     $o->cd('..')
157     } elsif ($cmd eq '...') {
158     $o->cd('...')
159     } elsif ($cmd) {
160     $o->setchanged();
161     my $cur=$o->{'cur'};
162     my $data=$o->{'data'};
163    
164     undef $@;
165     undef $!;
166     eval $cmd;
167    
168     if ($@) {
169     $o->error('Eval error: ', $@);
170     } elsif ($!) {
171     $o->error('Eval error: ', $!);
172     } elsif ($data!=$o->{'data'}) {
173     if (ref $data) {
174     $o->data($data);
175     $o->done('New base object set!');
176     } else {
177     $o->error('The base object must be a reference (HASH, ARRAY or REF).');
178     }
179     } elsif ($cur!=$o->{'cur'}) {
180     $o->error('You cannot change the current branch this way. Use "cd" instead.');
181     }
182     }
183     }
184    
185     return;
186     }
187    
188     sub cd { # cd($cdpath)
189     my $o=shift;
190     my $cdpath=shift;
191    
192     my $pathinfo={
193     'node' => $o->{'cur'},
194     'path' => [@{$o->{'path'}}],
195     'pathobjects' => [@{$o->{'pathobjects'}}],
196     };
197    
198     $pathinfo=$o->cdpath($pathinfo, $cdpath);
199     return if (! ref $pathinfo);
200    
201     $o->{'cur'}=$pathinfo->{'node'};
202     $o->{'path'}=$pathinfo->{'path'};
203     $o->{'pathobjects'}=$pathinfo->{'pathobjects'};
204     return;
205     }
206    
207     sub cdpath { # $node = cdpath($pathinfo, $cdpath)
208     my $o=shift;
209     my $pathinfo=shift;
210     my $cdpath=shift;
211    
212     my @path=split(/\//, $cdpath);
213     foreach my $label (@path) {
214     $pathinfo=$o->cdstep($pathinfo, $label);
215     return if (! ref $pathinfo);
216     }
217    
218     return $pathinfo;
219     }
220    
221     # Change directory.
222     sub cdstep { # $node = cdstep($node, $cd)
223     my $o=shift;
224     my $pathinfo=shift;
225     my $cd=shift;
226    
227     my $node=$pathinfo->{'node'};
228     my $path=$pathinfo->{'path'};
229     my $pathobjects=$pathinfo->{'pathobjects'};
230    
231     my $type=$o->type($node);
232     if ($cd eq '..') {
233     if (scalar(@$path)>0) {
234     pop @$path;
235     $pathinfo->{'node'}=pop @$pathobjects;
236     return $pathinfo;
237     }
238     } elsif ($cd eq '...') {
239     $pathinfo=$o->cdstep($pathinfo, '..');
240     $pathinfo=$o->cdstep($pathinfo, '..');
241     return $pathinfo;
242     } elsif ($type eq 'ARRAY') {
243     my $newcur=$node->[$cd];
244     if (ref $newcur) {
245     push @$path, $cd;
246     push @$pathobjects, $node;
247     $pathinfo->{'node'}=$newcur;
248     return $pathinfo;
249     } else {
250     $o->error('Cannot change into non-reference (', $cd, ').');
251     }
252     } elsif ($type eq 'HASH') {
253     my $newcur=$node->{$cd};
254     if (ref $newcur) {
255     push @$path, $cd;
256     push @$pathobjects, $node;
257     $pathinfo->{'node'}=$newcur;
258     return $pathinfo;
259     } else {
260     $o->error('Cannot change into non-reference (', $cd, ').');
261     }
262     } elsif ($type eq 'REF') {
263     my $newcur=$$node;
264     if (ref $newcur) {
265     push @$path, 'ref';
266     push @$pathobjects, $node;
267     $pathinfo->{'node'}=$newcur;
268     return $pathinfo;
269     } else {
270     $o->error('Cannot change into non-reference (', $cd, ').');
271     }
272     } else {
273     $o->error('Unexpected error.');
274     }
275    
276     return;
277     }
278    
279     # Lets the user give an input.
280     sub input {
281     my $o=shift;
282    
283     my @cmd;
284     my $line=$o->inputline(1);
285     while ($line =~ /^\s*(.*)\s*\\$/) {
286     push @cmd, $1;
287     $line=$o->inputline(0);
288     }
289     $line=$1 if ($line =~ /^\s*(.*)\s*$/);
290     push @cmd, $line;
291     return join("\n", @cmd);
292     }
293    
294    
295     # Lets the user give an input.
296     sub inputline { # $cmd = inputline($showpath)
297     my $o=shift;
298     my $showpath=shift;
299     my $path=$o->{'path'};
300     my $cur=$o->{'cur'};
301     my $readline=$o->{'readline'};
302    
303     # Path
304     print "\n" if ($showpath);
305     my $prompt=chr(033).'[0;33m';
306     $prompt.='/'.join('/', @$path) if ($showpath);
307     $prompt.=' ('.ref($cur).')' if ($showpath);
308     $prompt.='> '.chr(033).'[0m';
309    
310     # Get user input
311     my $cmd=$readline->readline($prompt);
312     return if (! defined $cmd);
313     while (chomp $cmd) {}
314     $readline->addhistory($cmd);
315     return $cmd;
316     }
317    
318     # Displays contents.
319     sub show {
320     my $o=shift;
321     my $data=shift;
322     my $textlimit=shift;
323    
324     # Data
325     my $type=$o->type($data);
326     if ($type eq 'ARRAY') {
327     return $o->show_array($data, $textlimit);
328     } elsif ($type eq 'HASH') {
329     return $o->show_hash($data, $textlimit);
330     } elsif ($type eq 'REF') {
331     return $o->show_ref($data, $textlimit);
332     }
333    
334     $o->error('Unexpected error.');
335     return;
336     }
337    
338     # Displays the contents of an array.
339     sub show_array {
340     my $o=shift;
341     my $data=shift;
342     my $textlimit=shift;
343    
344     my $i=0;
345     foreach my $item (@$data) {
346     my $tt=scalar($item);
347     $tt=substr($tt, 0, $textlimit).'...' if (($textlimit) && (length($tt)>$textlimit));
348     print ' ', $i, ' => ', $tt, "\n";
349     } continue {
350     $i++;
351     }
352    
353     $o->info(' (empty array)') if ($i==0);
354     return;
355     }
356    
357     # Displays the contents of a hash.
358     sub show_hash {
359     my $o=shift;
360     my $data=shift;
361     my $textlimit=shift;
362    
363     my $i=0;
364     foreach my $ky (sort keys %$data) {
365     my $tt=scalar($data->{$ky});
366     $tt=substr($tt, 0, $textlimit).'...' if (($textlimit) && (length($tt)>$textlimit));
367 dpavlin 37 print ' ', $o->unshard($ky), ' => ', $tt, "\n";
368 dpavlin 32 } continue {
369     $i++;
370     }
371    
372     $o->info(' (empty hash)') if ($i==0);
373     return;
374     }
375    
376     # Displays the contents of a reference.
377     sub show_ref {
378     my $o=shift;
379     my $data=shift;
380     my $textlimit=shift;
381    
382     my $tt=scalar($$data);
383     $tt=substr($tt, 0, $textlimit).'...' if (($textlimit) && (length($tt)>$textlimit));
384     print ' ref => ', $tt, "\n";
385     return;
386     }
387    
388     # Returns the reference type.
389     sub type {
390     my $o=shift;
391     my $obj=shift;
392    
393     my $type;
394     if (ref $obj) {
395     $type=scalar($obj);
396     $type=(($type=~/=(.*)\(/) ? $1 : ref $obj);
397     }
398    
399     return $type;
400     }
401    
402     # Prints out a colored line of text.
403     sub colorline {
404     my $o=shift;
405     my $color=shift;
406     print chr(033), '[', $color, 'm', @_, chr(033), '[0m', "\n";
407     return;
408     }
409    
410     # Displays an error message.
411     sub error {
412     my $o=shift;
413     $o->colorline('31', @_);
414     return;
415     }
416    
417     # Displays a done message.
418     sub done {
419     my $o=shift;
420     $o->colorline('32', @_);
421     return;
422     }
423    
424     # Displays an info message.
425     sub info {
426     my $o=shift;
427     $o->colorline('34;1', @_);
428     return;
429     }
430    
431     =head1 NAME
432    
433     storableedit.pl - Edit Perl Storable Files
434    
435     =head1 SYNOPSIS
436    
437     storableedit.pl PERL_STORABLE_FILE
438    
439     =head1 DESCRIPTION
440    
441     B<storableedit.pl> is a simple command line editor for Perl Storable files. Its interface is similar to a shell.
442    
443     When the program is started, it reads the whole file and displays the first level of the data structure. One can then dive into the structure with the B<cd> command and display values with the B<ls> or B<l> commands.
444    
445     The data can be modified with usual Perl expressions, where
446     I<$cur> is a reference to the current node
447     I<$data> is a reference to the root node of the data
448    
449     To save the data back to file and exit, type B<x> or B<exit>. To exit without saving, type B<q> or B<quit>. If you have modified the data, you can force discarding the changes with B<q!> or B<quit!>.
450    
451     =head1 BUILT-IN COMMANDS
452    
453     =over 4
454    
455     =item cd ELEMENT
456    
457     Moves into ELEMENT. If the current node is a hashref, ELEMENT must be a key. The new current node will be $cur->{'ELEMENT'}. If the current node is an arrayref, ELEMENT must be an index and $cur->[ELEMENT] becomes the new current node. If the current node is a reference to a reference, type B<cd ref>. Note that you can only move into new references (hashref, arrayref or ref, but not scalars).
458    
459     To move up to the parent, type B<cd ..> (or just B<..>). To move up two steps, type B<cd ...> (or just B<...>).
460    
461     =item ..
462    
463     Shortcut for B<cd ..>
464    
465     =item ...
466    
467     Shortcut for B<cd ...>
468    
469     =item ls
470    
471     Displays the contents of the current node. For hashrefs, all key/value pairs are listed. For arrayrefs, all elements are listed.
472    
473     Note that ls doesn't take any arguments, i.e. you can't use regexes to display only parts of the content.
474    
475     =item l
476    
477     Similar as B<ls>, but cuts all values that are longer than 50.
478    
479     =item x, exit
480    
481     Saves the (modified) data structure and quits.
482    
483     =item q, quit
484    
485     Quits without saving. If the data structure has been modified, a warning is shown.
486    
487     =item q!, quit!
488    
489     Forces quiting without saving.
490    
491     =back
492    
493     =head1 PERL EXPRESSIONS
494    
495     If a command is not recognized as a built-in command, the whole line is passed to the Perl eval() function. Two variables are provided to access the data structure:
496     I<$cur> is a reference to the current node
497     I<$data> is a reference to the root node of the data
498    
499     Note that this script does not check what you are doing. It does NOT ask to confirm when you delete values. But keep in mind that you can always quit with B<q!> to discard all changes.
500    
501     In case of syntax or runtime errors, the Perl error messages are shown.
502    
503     Simple examples:
504    
505     $cur->{'foo'} = 'bar'; # if $cur is a hashref
506     $cur->[0] = 'bar'; # if $cur is an arrayref
507     $cur->[1] = {'a' => 'b'}; # creates new hashref
508     $cur->[2] = ['a', 'b']; # creates new arrayref
509    
510     Fancy examples:
511    
512     push @$cur, 'bar';
513     # adds a new value to the current arrayref
514    
515     push @$data, pop @$cur;
516     # more array ops
517    
518     $cur->{'list'} = split(/,/, $cur->{'string'};
519     # splitting a string
520    
521     $cur->{'list'} = [sort values %{$cur->{'hashref'}}];
522     # sorted values of a hashref
523    
524     print join("\n", grep {/abc/} keys %$cur)
525     # grep, join ... it's Perl!
526    
527     $data = $cur;
528     # sets the current node as new root
529     # note that you might loose data ...
530    
531     $cur->{'imported'}=retrieve('otherfile.store');
532     # imports from another Storable file
533    
534     store($cur, 'otherfile.store');
535     # exports to another Storable file
536    
537     Perl-OO examples:
538    
539     use My::Module;
540     # load My::Module
541    
542     $cur->{'obj'} = My::Module->new();
543     # create a new My::Module object
544    
545     $cur->{'obj'}->my_method();
546     # call a method
547    
548     bless $cur, 'My::Module';
549     # blessing manually
550    
551     To enter multiline Perl expressions, put a backslash (\) at the end of each line. Example:
552    
553     open(my $fh, '<', 'myfile.txt');\
554     $cur->{'firstline'}=<$fh>;\
555     close $fh;
556    
557     =head1 CAVEATS
558    
559     =over 4
560    
561     =item Cyclic Structures
562    
563     B<storableedit.pl> also handles cyclic structures.
564    
565     =item File Handles
566    
567     File handles are not stored by the Storable module (for obvious reasons).
568    
569     =item Objects
570    
571     When loading storable files with (blessed) objects, the corresponding modules are not automatically loaded. If you need to invoke methods, type B<use My::Module;> or B<require "/path/to/module.pm";> to load the necessary module(s).
572    
573     =back
574    
575     =head1 AUTHORS
576    
577     Thomas Lochmatter <thl@lothosoft.ch>.
578    
579     =head1 README
580    
581     A simple command line editor for Perl Storable files. The interface is similar to a shell. It allows to easily walk through the data structure of the file and to list and change values.
582    
583     =begin comment
584    
585     =pod PREREQUISITES
586    
587     strict
588     Storable
589     Term::ReadLine
590    
591     =pod OSNAMES
592    
593     any
594    
595     =pod SCRIPT CATEGORIES
596    
597     Unix/System_administration
598    
599     =end comment
600    
601     =cut

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26