OSSP CVS Repository

ossp - Difference in ossp-pkg/cvsfusion/RCS.pm versions 1.7 and 1.8
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [History

ossp-pkg/cvsfusion/RCS.pm 1.7 -> 1.8

--- RCS.pm       2004/04/23 17:58:17     1.7
+++ RCS.pm       2004/04/23 21:38:48     1.8
@@ -28,36 +28,84 @@
 ##  RCS.pm: RCS file handling
 ##
 
-package RCS;
-
 require 5;
+use strict;
+use warnings;
+
+##  _________________________________________________________________________
+##
+##                       Class "RCS::Global" (SHARED)
+##  _________________________________________________________________________
+##
+
+package RCS::Global;
+
+#   check whether an entry name is valid
+sub valid_entry_name ($$) {
+    my ($obj, $name) = @_;
+
+    my $valid = 0;
+    if (defined($obj->{$name}) and $name !~ m|^-|) {
+        $valid = 1;
+    }
+    return $valid;
+}
+
+#   check whether an entry value is valid
+sub valid_entry_value ($$$) {
+    my ($obj, $name, $value) = @_;
+
+    my $type   = $obj->{$name}->{-type};
+    my $syntax = $obj->{$name}->{-syntax};
+
+    my $valid = 0;
+    if ($type eq '$' and not ref($value) and $value =~ m|${syntax}|s) {
+        $valid = 1;
+    }
+    elsif ($type eq '@' and ref($value) eq 'ARRAY') {
+        $valid = 1;
+        foreach my $v (@{$value}) {
+            if ($v !~ m|${syntax}|s) {
+                $valid = 0;
+                last;
+            }
+        }
+    }
+    elsif ($type eq '%' and ref($value) eq 'HASH') {
+        $valid = 1;
+        foreach my $k (keys(%{$value})) {
+            if ($k."::".$value->{$k} !~ m|${syntax}|s) {
+                $valid = 0;
+                last;
+            }
+        }
+    }
+    return $valid;
+}
+
+##  _________________________________________________________________________
+##
+##                        Class "RCS::Object" (ABSTRACT)
+##  _________________________________________________________________________
+##
+
+package RCS::Object;
+
 require Exporter;
 use Carp;
-use IO::File;
 
-@ISA       = qw(Exporter);
-@EXPORT_OK = qw(new destroy DESTROY dump tool load save parse format revapply trunk2branch);
+our @ISA       = qw(Exporter);
+our @EXPORT_OK = qw(new destroy DESTROY dump);
 
 #   create new object
 sub new ($;$) {
-    my ($proto, $file) = @_;
+    my ($proto) = @_;
 
     #   create new object
     my $class = ref($proto) || $proto;
     my $self = {};
     bless ($self, $class);
 
-    #   initialize object
-    $self->{'tool'} = {
-        'rcs'  => 'rcs',
-        'co'   => 'co',
-        'diff' => 'diff',
-    };
-    $self->{'rcs'} = {};
-
-    #   optionally load file into object
-    $rcs->load($file) if (defined($file));
-
     #   return new object
     return $self;
 }
@@ -78,7 +126,7 @@
 #   dump object internals (debugging only)
 sub dump ($;$) {
     my ($self, $name) = @_;
-    $name ||= "rcs";
+    $name ||= "obj";
     eval {
         use Data::Dumper;
     };
@@ -92,17 +140,129 @@
     return $dump;
 }
 
-#   get and/or set paths to external tools
-sub tool ($;$) {
-    my ($self, $tool, $path) = @_;
-    my $old = $self->{'tool'}->{$tool};
-    if (not defined($old)) {
-        croak "tool \"$tool\" not known";
+##  _________________________________________________________________________
+##
+##                            Class "RCS::Revision"
+##  _________________________________________________________________________
+##
+
+package RCS::Revision;
+
+require 5;
+require Exporter;
+use Carp;
+
+our @ISA       = qw(Exporter RCS::Object);
+our @EXPORT_OK = qw(new destroy DESTROY dump revision set get);
+
+#   create new object
+sub new ($;$) {
+    my ($proto, $rev) = @_;
+
+    #   create new object
+    my $self = {
+        -rev       => undef,
+        -order     => [],
+        'date'     => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'author'   => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'state'    => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'branches' => { -type => '@', -syntax => qr/.*/, -value => undef },
+        'next'     => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'log'      => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'text'     => { -type => '$', -syntax => qr/.*/, -value => undef },
+    };
+    my $class = ref($proto) || $proto;
+    bless ($self, $class);
+
+    #   optionally set revision
+    $self->revision($rev) if (defined($rev));
+
+    #   return new object
+    return $self;
+}
+
+#   get and/or set revision number
+sub revision ($;$) {
+    my ($self, $rev) = @_;
+    my $old_rev = $self->{-rev};
+    if (defined($rev)) {
+        $self->{-rev} = $rev;
     }
-    if (defined($path)) {
-        $self->{'tool'}->{$tool} = $path;
+    return $old_rev;
+}
+
+#   set entry into object
+sub set ($$$) {
+    my ($self, $name, $value) = @_;
+
+    if (not RCS::Global::valid_entry_name($self, $name)) {
+        croak "invalid entry \"$name\"";
+    }
+    if (defined($value)) {
+        if (not RCS::Global::valid_entry_value($self, $name, $value)) {
+            croak "invalid value \"$value\" for entry \"$name\"";
+        }
+    }
+    my $old_value = $self->{$name}->{-value};
+    $self->{$name}->{-value} = $value;
+    $self->{-order} = [ grep { $_ ne $name } @{$self->{-order}} ];
+    push(@{$self->{-order}}, $name);
+    return $old_value;
+}
+
+#   get entry from object
+sub get ($;$) {
+    my ($self, $name) = @_;
+
+    if (not defined($name)) {
+        return @{$self->{-order}};
+    }
+    if (not RCS::Global::valid_entry_name($self, $name)) {
+        croak "invalid entry \"$name\"";
     }
-    return $old;
+    return $self->{$name}->{-value};
+}
+
+##  _________________________________________________________________________
+##
+##                                 Class "RCS"
+##  _________________________________________________________________________
+##
+
+package RCS;
+
+require Exporter;
+use Carp;
+use IO::File;
+
+our @ISA       = qw(Exporter RCS::Object);
+our @EXPORT_OK = qw(new destroy DESTROY dump load save parse format insert remove lookup set get);
+
+#   create new object
+sub new ($;$) {
+    my ($proto, $file) = @_;
+
+    #   create new object
+    my $self = {
+        -order     => [],
+        'head'     => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'access'   => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'symbols'  => { -type => '@', -syntax => qr/.*/, -value => undef },
+        'locks'    => { -type => '%', -syntax => qr/.*/, -value => undef },
+        'strict'   => { -type => '%', -syntax => qr/.*/, -value => undef },
+        'comment'  => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'expand'   => { -type => '$', -syntax => qr/.*/, -value => undef },
+        'desc'     => { -type => '$', -syntax => qr/.*/, -value => undef },
+        -revision  => { -count => 0 },
+    };
+    my $class = ref($proto) || $proto;
+    bless ($self, $class);
+
+    #   optionally load file into object
+    $self->load($file) if (defined($file));
+
+    #   return new object
+    return $self;
 }
 
 #   INTERNAL: quote a RCS string
@@ -139,27 +299,45 @@
 }
 
 #   INTERNAL: structured revision to sequential number mapping
-sub _rev_rev2num ($$) {
+sub rev2num ($$) {
     my ($self, $rev) = @_;
-    foreach my $num (keys(%{$self->{'rcs'}->{'rev'}})) {
-        next if ($num eq '-count');
-        return $num if ($self->{'rcs'}->{'rev'}->{$num} eq $rev);
+    foreach my $num (keys(%{$self->{'-revision'}})) {
+        next if ($num =~ m|^-|);
+        return $num if ($self->{'-revision'}->{$num}->revision() eq $rev);
     }
-    my $num = sprintf("REV-NUM(%d)", $self->{'rcs'}->{'rev'}->{-count}++);
-    $self->{'rcs'}->{'rev'}->{$num} = $rev;
+    my $num = sprintf("REV-NUM(%d)", $self->{'-revision'}->{-count}++);
+    $self->{'-revision'}->{$num} = new RCS::Revision $rev;
     return $num;
 }
 
 #   INTERNAL: sequential number to structured revision mapping
-sub _rev_num2rev ($$) {
+sub num2rev ($$) {
     my ($self, $num) = @_;
-    return $self->{'rcs'}->{'rev'}->{$num};
+    return $self->{'-revision'}->{$num}->revision();
+}
+
+#   INTERNAL: object to sequential number mapping
+sub obj2num ($$) {
+    my ($self, $obj) = @_;
+    foreach my $num (keys(%{$self->{'-revision'}})) {
+        next if ($num =~ m|^-|);
+        return $num if ($self->{'-revision'}->{$num} eq $obj);
+    }
+    my $num = sprintf("REV-NUM(%d)", $self->{'-revision'}->{-count}++);
+    $self->{'-revision'}->{$num} = $obj;
+    return $num;
+}
+
+#   INTERNAL: sequential number to object mapping
+sub num2obj ($$) {
+    my ($self, $num) = @_;
+    return $self->{'-revision'}->{$num};
 }
 
 #   INTERNAL: check whether argument is a valid sequential number
-sub _rev_isnum ($$) {
+sub isrevnum ($$) {
     my ($self, $num) = @_;
-    return (($num =~ m|^REV-NUM\(\d+\)$| and defined($self->{'rcs'}->{'rev'}->{$num})) ? 1 : 0);
+    return (($num =~ m|^REV-NUM\(\d+\)$| and defined($self->{'-revision'}->{$num})) ? 1 : 0);
 }
 
 #   parse a RCS file content into object
@@ -167,12 +345,11 @@
 sub parse ($$) {
     my ($self, $rcs) = @_;
 
-    #   clear RCS structure
-    $self->{'rcs'} = {
-        'header'    => { -order => [] },
-        'delta'     => { -order => [] },
-        'rev'       => { -count => 0  },
-    };
+    #   clear entries of object
+    foreach my $entry (keys(%{$self})) {
+        next if ($entry =~ m|^-|);
+        $self->{$entry}->{-value} = undef;
+    }
 
     #   pre-generate reusable regular expressions
     my $re_num     = qr/[\d.]+/;
@@ -188,63 +365,61 @@
     while (1) {
         $rcs =~ s|^\s*||s;
         if ($rcs =~ s/^head\s+($re_rev)\s*;//s) {
-            $self->{'rcs'}->{'header'}->{'head'} = $self->_rev_rev2num($1);
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, 'head');
+            $self->{'head'}->{-value} = $self->rev2num($1);
+            push(@{$self->{-order}}, 'head');
         }
         elsif ($rcs =~ s/^branch\s+($re_rev)\s*;//s) {
-            $self->{'rcs'}->{'header'}->{'branch'} = $self->_rev_rev2num($1);
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, 'branch');
+            $self->{'branch'}->{-value} = $self->rev2num($1);
+            push(@{$self->{-order}}, 'branch');
         }
         elsif ($rcs =~ s/^access((?:\s+$re_id)*)\s*;//s) {
-            $self->{'rcs'}->{'header'}->{'access'} = 
-                (defined($1) ? [ split(/\s+/, $1) ] : []);
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, 'access');
+            $self->{'access'}->{-value} = (defined($1) ? [ split(/\s+/, $1) ] : []);
+            push(@{$self->{-order}}, 'access');
         }
         elsif ($rcs =~ s/^symbols((?:\s+$re_sym:$re_rev)*)\s*;//s) {
             my $symbols = { -order => [] };
             if (defined($1)) {
                 map { if (m/^(.+):(.+)$/s) {
-                $symbols->{$1} = $self->_rev_rev2num($2);
+                $symbols->{$1} = $self->rev2num($2);
                 push(@{$symbols->{-order}}, $1);
                 } } split(/\s+/, $1);
             } 
-            $self->{'rcs'}->{'header'}->{'symbols'} = $symbols;
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, 'symbols');
+            $self->{'symbols'}->{-value} = $symbols;
+            push(@{$self->{-order}}, 'symbols');
         }
         elsif ($rcs =~ s/^locks((?:\s+$re_id:$re_rev)*)\s*;//s) {
             my $locks = { -order => [] };
             if (defined($1)) {
                 map { if (m/^(.+):(.+)$/s) {
-                    $locks->{$1} = $self->_rev_rev2num($2);
+                    $locks->{$1} = $self->rev2num($2);
                     push(@{$locks->{-order}}, $1);
                 } } split(/\s+/, $1);
             }
-            $self->{'rcs'}->{'header'}->{'locks'} = $locks;
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, 'locks');
+            $self->{'locks'}->{-value} = $locks;
+            push(@{$self->{-order}}, 'locks');
         }
         elsif ($rcs =~ s/^strict\s*;//s) {
-            $self->{'rcs'}->{'header'}->{'strict'} = "";
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, 'strict');
+            $self->{'strict'}->{-value} = "";
+            push(@{$self->{-order}}, 'strict');
         }
         #elsif ($rcs =~ s/^comment\s+\@($re_str)\@\s*;//s) { # would maximally span 32K
         elsif ($rcs =~ s/^comment\s+\@//s) {
             my $str = '';
-            1 while ($rcs =~ s/^((?:\@\@|[^@])+)/$str .= $1, ''/se);
+            1 while ($rcs =~ s/^((?:\@\@|[^@]){1,32000})/$str .= $1, ''/se);
             $rcs =~ s/\@\s*;//s;
-            $self->{'rcs'}->{'header'}->{'comment'} = &_string_unquote($str);
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, 'comment');
+            $self->{'comment'}->{-value} = &_string_unquote($str);
+            push(@{$self->{-order}}, 'comment');
         }
         #elsif ($rcs =~ s/^expand\s+\@($re_str)\@\s*;//s) { # would maximally span 32K
         elsif ($rcs =~ s/^expand\s+\@//s) {
             my $str = '';
-            1 while ($rcs =~ s/^((?:\@\@|[^@])+)/$str .= $1, ''/se);
+            1 while ($rcs =~ s/^((?:\@\@|[^@]){1,32000})/$str .= $1, ''/se);
             $rcs =~ s/\@\s*;//s;
-            $self->{'rcs'}->{'header'}->{'expand'} = &_string_unquote($str);
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, 'expand');
+            $self->{'expand'}->{-value} = &_string_unquote($str);
+            push(@{$self->{-order}}, 'expand');
         }
         elsif ($rcs =~ s/^([a-z]+)(?:\s*([^;]*));//s) {
-            $self->{'rcs'}->{'header'}->{$1} = $2;
-            push(@{$self->{'rcs'}->{'header'}->{-order}}, $1);
+            #   currently intentionally just ignored, because does not occur at all
         }
         else {
             last;
@@ -255,33 +430,31 @@
     while (1) {
         $rcs =~ s|^\s*||s;
         if ($rcs =~ s/^($re_rev)//s) {
-            my $rev = $self->_rev_rev2num($1);
-            $self->{'rcs'}->{'delta'}->{$rev} = {};
-            push(@{$self->{'rcs'}->{'delta'}->{-order}}, $rev);
+            my $num = $self->rev2num($1);
+            my $rev = $self->num2obj($num);
             while (1) {
                 $rcs =~ s|^\s*||s;
                 if ($rcs =~ s/^date\s+($re_date)\s*;//s) {
-                    $self->{'rcs'}->{'delta'}->{$rev}->{'date'} = $1;
+                    $rev->set('date', $1);
                 }
                 elsif ($rcs =~ s/^author\s+($re_id)\s*;//s) {
-                    $self->{'rcs'}->{'delta'}->{$rev}->{'author'} = $1;
+                    $rev->set('author', $1);
                 }
                 elsif ($rcs =~ s/^state(?:\s*($re_id))?\s*;//s) {
-                    $self->{'rcs'}->{'delta'}->{$rev}->{'state'} = $1;
+                    $rev->set('state', $1);
                 }
                 elsif ($rcs =~ s/^branches(?:\s+((?:\s*$re_rev)*))?\s*;//s) {
-                    $self->{'rcs'}->{'delta'}->{$rev}->{'branches'} =
-                        (defined($1) and $1 ne '' ? [ map { $self->_rev_rev2num($_) } split(/\s+/, $1) ] : []);
+                    $rev->set('branches',
+                        defined($1) ? [ map { $self->rev2num($_) } split(/\s+/, $1) ] : []);
                 }
                 elsif ($rcs =~ s/^next(?:\s*($re_rev))?\s*;//s) {
-                    $self->{'rcs'}->{'delta'}->{$rev}->{'next'} =
-                        (defined($1) and $1 ne '' ? $self->_rev_rev2num($1) : '');
+                    $rev->set('next', (defined($1) and $1 ne '' ? $self->rev2num($1) : ''));
                 }
                 elsif ($rcs =~ m/^desc\s+/s) {
                     last;
                 }
                 elsif ($rcs =~ s/^([a-z]+)(?:\s*([^;]*));//s) {
-                    $self->{'rcs'}->{'delta'}->{$rev}->{$1} = $2;
+                    #   currently intentionally just ignored, because does not occur at all
                 }
                 else {
                     last;
@@ -298,36 +471,34 @@
     #if ($rcs =~ s/^desc\s+\@($re_str)\@\s*//s) { # would maximally span 32K
     if ($rcs =~ s/^desc\s+\@//s) {
         my $str = '';
-        1 while ($rcs =~ s/^((?:\@\@|[^@])+)/$str .= $1, ''/se);
+        1 while ($rcs =~ s/^((?:\@\@|[^@]){1,32000})/$str .= $1, ''/se);
         $rcs =~ s/\@\s*//s;
-        $self->{'rcs'}->{'header'}->{'desc'} = &_string_unquote($str);
-        push(@{$self->{'rcs'}->{'header'}->{-order}}, 'desc');
+        $self->{'desc'}->{-value} = &_string_unquote($str);
+        push(@{$self->{-order}}, 'desc');
     }
 
     #  parse deltatext section(s)
     while (1) {
         $rcs =~ s|^\s*||s;
         if ($rcs =~ s/^($re_rev)//s) {
-            my $rev = $self->_rev_rev2num($1);
-            if (not defined($self->{'rcs'}->{'delta'}->{$rev})) {
-                croak "deltatext section for unknown revision \"".$self->_rev_num2rev($rev)."\" found";
-            }
+            my $num = $self->rev2num($1);
+            my $rev = $self->num2obj($num);
             my $textseen = 0;
             while (1) {
                 $rcs =~ s|^\s*||s;
                 #if ($rcs =~ s/^log\s+\@($re_str)\@\s*//s) { # would maximally span 32K
                 if ($rcs =~ s/^log\s+\@//s) {
                     my $str = '';
-                    1 while ($rcs =~ s/^((?:\@\@|[^@])+)/$str .= $1, ''/se);
+                    1 while ($rcs =~ s/^((?:\@\@|[^@]){1,32000})/$str .= $1, ''/se);
                     $rcs =~ s/\@\s*//s;
-                    $self->{'rcs'}->{'delta'}->{$rev}->{'log'} = &_string_unquote($str);
+                    $rev->set('log', &_string_unquote($str));
                 }
                 #elsif ($rcs =~ s/^text\s+\@($re_str)\@\s*//s) { # would maximally span 32K
                 elsif ($rcs =~ s/^text\s+\@//s) {
                     my $str = '';
-                    1 while ($rcs =~ s/^((?:\@\@|[^@])+)/$str .= $1, ''/se);
+                    1 while ($rcs =~ s/^((?:\@\@|[^@]){1,32000})/$str .= $1, ''/se);
                     $rcs =~ s/\@\s*//s;
-                    $self->{'rcs'}->{'delta'}->{$rev}->{'text'} = &_string_unquote($str);
+                    $rev->set('text', &_string_unquote($str));
                     $textseen = 1;
                 }
                 #elsif ($textseen == 0 and $rcs =~ s/^([a-z]+)(?:\s+\@($re_str)\@\s*)?//s) { # would maximally span 32K
@@ -335,10 +506,10 @@
                     my ($keyword, $with_str) = ($1, $2);
                     my $str = '';
                     if ($with_str) { 
-                        1 while ($rcs =~ s/^((?:\@\@|[^@])+)/$str .= $1, ''/se);
+                        1 while ($rcs =~ s/^((?:\@\@|[^@]){1,32000})/$str .= $1, ''/se);
                         $rcs =~ s/\@\s*//s;
                     }
-                    $self->{'rcs'}->{'delta'}->{$rev}->{$keyword} = $str;
+                    #   currently intentionally just ignored, because does not occur at all
                 }
                 else {
                     last;
@@ -358,12 +529,14 @@
 sub _revlist ($$) {
     my ($self, $branchfirst) = @_;
     my @revs = ();
-    &nextrev($self, \@revs, $self->{'rcs'}->{'header'}->{'head'}, $branchfirst);
+    if (defined($self->{'head'}->{-value})) {
+        &nextrev($self, \@revs, $self->{'head'}->{-value}, $branchfirst);
+    }
     sub nextrev ($$$$) {
         my ($self, $revs, $rev, $branchfirst) = @_;
         push(@{$revs}, $rev);
-        my $next     = $self->{'rcs'}->{'delta'}->{$rev}->{'next'};
-        my $branches = $self->{'rcs'}->{'delta'}->{$rev}->{'branches'};
+        my $next     = $self->num2obj($rev)->get('next');
+        my $branches = $self->num2obj($rev)->get('branches');
         if ($branchfirst) {
             foreach my $branch (@{$branches}) {
                 &nextrev($self, $revs, $branch, $branchfirst); 
@@ -387,7 +560,7 @@
     my $rcs = '';
     if (ref($val) eq 'ARRAY' and @{$val} > 0) {
         foreach my $v (@{$val}) {
-            $v = $self->_rev_num2rev($v) if ($self->_rev_isnum($v));
+            $v = $self->num2rev($v) if ($self->isrevnum($v));
             $rcs .= "\n\t$v";
         }
     }
@@ -395,16 +568,16 @@
         if (defined($val->{-order})) {
             foreach my $k (@{$val->{-order}}) {
                 my $v = $val->{$k};
-                $v = $self->_rev_num2rev($v) if ($self->_rev_isnum($v));
-                $k = $self->_rev_num2rev($k) if ($self->_rev_isnum($k));
+                $v = $self->num2rev($v) if ($self->isrevnum($v));
+                $k = $self->num2rev($k) if ($self->isrevnum($k));
                 $rcs .= sprintf("\n\t%s:%s", $k, $v);
             }
         }
         else {
             foreach my $k (keys(%{$val})) {
                 my $v = $val->{$k};
-                $v = $self->_rev_num2rev($v) if ($self->_rev_isnum($v));
-                $k = $self->_rev_num2rev($k) if ($self->_rev_isnum($k));
+                $v = $self->num2rev($v) if ($self->isrevnum($v));
+                $k = $self->num2rev($k) if ($self->isrevnum($k));
                 $rcs .= sprintf("\n\t%s:%s", $k, $v);
             }
         }
@@ -414,7 +587,7 @@
             $rcs .= "\t" . &_string_quote($val);
         }
         else {
-            $val = $self->_rev_num2rev($val) if ($self->_rev_isnum($val));
+            $val = $self->num2rev($val) if ($self->isrevnum($val));
             $rcs .= "\t$val";
         }
     }
@@ -449,13 +622,11 @@
 
     #   generate header section
     foreach my $header (
-        @kw_header,
-        ( grep { not grep(/^\Q$_\E$/, @kw_header) }
-          @{$self->{'rcs'}->{'header'}->{-order}}  )
+        @kw_header, (grep { not grep(/^\Q$_\E$/, @kw_header) } @{$self->{-order}})
     ) {
         my $tag = '';
         $tag = $1 if ($header =~ s/([@\-])$//s);
-        my $val = $self->{'rcs'}->{'header'}->{$header};
+        my $val = $self->{$header}->{-value};
         if (defined($val)) {
             $rcs .= $header . $self->_genvalue($val, $tag). ";";
             $rcs .= ($tag eq '-' ? " " : "\n");
@@ -466,54 +637,127 @@
     #   generate delta section(s)
     my @revlist = $self->_revlist(0);
     foreach my $rev (@revlist) {
-        my $delta = $self->{'rcs'}->{'delta'}->{$rev};
+        my $obj = $self->num2obj($rev);
         $rcs .= "\n";
-        $rcs .= $self->_rev_num2rev($rev)."\n";
-        $rcs .= "date\t" . $delta->{'date'} . ";\t";
-        $rcs .= "author " . $delta->{'author'} . ";\t";
-        $rcs .= "state " . $delta->{'state'} . ";\n";
+        $rcs .= $obj->revision()."\n";
+        $rcs .= "date\t" . $obj->get('date') . ";\t";
+        $rcs .= "author " . $obj->get('author') . ";\t";
+        $rcs .= "state " . $obj->get('state') . ";\n";
         $rcs .= "branches";
-        if (@{$delta->{'branches'}} > 0) {
-            foreach my $v (@{$delta->{'branches'}}) {
-                $rcs .= "\n\t". $self->_rev_num2rev($v);
+        my $branches = $obj->get('branches');
+        if (@{$branches} > 0) {
+            foreach my $v (@{$branches}) {
+                $rcs .= "\n\t". $self->num2rev($v);
             }
         }
         $rcs .= ";\n";
-        $rcs .= "next\t" . (defined($delta->{'next'}) ? $self->_rev_num2rev($delta->{'next'}) : "") . ";\n";
+        my $next = $obj->get('next');
+        $rcs .= "next\t" . (defined($next) and $next ne '' ? $self->num2rev($next) : "") . ";\n";
     }
 
     #   generate description section
-    my $desc = $self->{'rcs'}->{'header'}->{'desc'};
+    my $desc = $self->{'desc'}->{-value};
     $rcs .= "\n\ndesc\n" . &_string_quote($desc) . "\n";
 
     #   generate deltatext section(s)
     @revlist = $self->_revlist(1);
     foreach my $rev (@revlist) {
-        my $delta = $self->{'rcs'}->{'delta'}->{$rev};
+        my $obj = $self->num2obj($rev);
         $rcs .= "\n";
         $rcs .= "\n";
-        $rcs .= $self->_rev_num2rev($rev)."\n";
-        $rcs .= "log\n" . &_string_quote($delta->{'log'}) . "\n";
-        $rcs .= "text\n" . &_string_quote($delta->{'text'}) . "\n";
+        $rcs .= $obj->revision()."\n";
+        my $log  = $obj->get('log')  || '';
+        my $text = $obj->get('text') || '';
+        $rcs .= "log\n" . &_string_quote($log) . "\n";
+        $rcs .= "text\n" . &_string_quote($text) . "\n";
     }
 
     return $rcs;
 }
 
-#   apply a translation function onto all RCS revisions
-sub revapply ($$) {
-    my ($self, $sub) = @_;
-    foreach my $num (keys(%{$self->{'rcs'}->{'rev'}})) {
-        $self->{'rcs'}->{'rev'}->{$num} =
-            &$sub($self->{'rcs'}->{'rev'}->{$num});
+#   insert a revision object
+sub insert ($$) {
+    my ($self, $obj) = @_;
+    $self->obj2num($obj);
+}
+
+#   remove a revision object
+sub remove ($$) {
+    my ($self, $obj) = @_;
+    my $num = $self->obj2num($obj);
+    delete $self->{-revision}->{$num};
+}
+
+#   lookup a revision object
+sub lookup ($;$) {
+    my ($self, $id) = @_;
+
+    if (not defined($id)) {
+        return values(%{$self->{-revision}});
+    }
+    else {
+        if ($id =~ m|^\d+(\.\d+)*$|) {
+            #   lookup by revision number
+            my $num = $self->rev2num($id);
+            my $obj = $self->num2obj($num);
+            return $obj;
+        }
+        else {
+            #   lookup by symbolic tag
+            foreach my $symbol (keys(%{$self->{'symbols'}->{-value}})) {
+                if ($symbol eq $id) {
+                    my $num = $self->{'symbols'}->{-value}->{$symbol};
+                    my $obj = $self->num2obj($num);
+                    return $obj;
+                }
+            }
+        }
     }
-    return;
+    return undef;
+}
+
+#   set entry into object
+sub set ($$$) {
+    my ($self, $name, $value) = @_;
+
+    if (not RCS::Global::valid_entry_name($self, $name)) {
+        croak "invalid entry \"$name\"";
+    }
+    if (defined($value)) {
+        if (not RCS::Global::valid_entry_value($self, $name, $value)) {
+            croak "invalid value \"$value\" for entry \"$name\"";
+        }
+    }
+    my $old_value = $self->{$name}->{-value};
+    $self->{$name}->{-value} = $value;
+    $self->{-order} = [ grep { $_ ne $name } @{$self->{-order}} ];
+    push(@{$self->{-order}}, $name);
+    return $old_value;
+}
+
+#   get entry from object
+sub get ($;$) {
+    my ($self, $name) = @_;
+
+    if (not defined($name)) {
+        return @{$self->{-order}};
+    }
+    if (not RCS::Global::valid_entry_name($self, $name)) {
+        croak "invalid entry \"$name\"";
+    }
+    return $self->{$name}->{-value};
 }
 
 1;
 
 __END__
 
+##  _________________________________________________________________________
+##
+##                                Manual Page
+##  _________________________________________________________________________
+##
+
 =pod
 
 =head1 NAME
@@ -533,38 +777,90 @@
 
 =item C<my $rcs = >B<new>C< RCS>[C< $filename>]C<;>
 
-This creates a new RCS object and (for convinience reasons) optionally
-loads an RCS file via C<$rcs-E<gt>>B<load>C<($filename)> into it.
-
 =item C<$rcs-E<gt>>B<destroy>C<;>
 
-=item C<undef $rcs;>
+Method B<new> creates a new RCS object and (for convinience reasons)
+optionally loads an RCS file via C<$rcs-E<gt>>B<load>C<($filename)>
+into it. Method B<destroy> destroys the RCS object and frees all its
+resources.
 
-This destroys the RCS object.
-
-=item [C<my $old_path = >]C<$rcs-E<gt>>B<tool>C<(">I<tool>C<", $path);>
+=item C<$rcs-E<gt>>B<load>C<($filename);>
 
-=item C<my $path = $rcs-E<gt>>B<tool>C<(">I<tool>C<");>
+=item C<$rcs-E<gt>>B<save>C<($filename);>
 
-This sets and/or gets the path to the external command I<tool>. Used
-I<tool>s are C<rcs>, C<co> and C<diff> from GNU RCS and GNU DiffUtils.
+Method B<load> loads the RCS file under C<$filename> into RCS object C<$rcs>.
+Methid B<save> saves the RCS file content from RCS object C<$rcs> under C<$filename>.
 
 =item C<$rcs-E<gt>>B<parse>C<($filename, $rcstext);>
 
-This parses the RCS file content in C<$rcstext> and
-and stores the result in RCS object C<$rcs>.
-
 =item C<my $rcstext = $rcs-E<gt>>B<format>C<($filename);>
 
-This formats and returns the RCS file content in C<$rcs>.
+Method B<parse> parses the RCS file content in C<$rcstext> and and
+stores the result in RCS object C<$rcs>. Method B<format> formats and
+returns the RCS file content in C<$rcs>.
 
-=item C<$rcs-E<gt>>B<load>C<($filename);>
+=item C<my $value = $rcs-E<gt>>B<get>C<($name);>
 
-This loads the RCS file under C<$filename> into RCS object C<$rcs>.
+=item C<$rcs-E<gt>>B<set>C<($name, $value);>
 
-=item C<$rcs-E<gt>>B<save>C<($filename);>
+Methods B<get> and B<set> get and/or set the value of the RCS entry
+identified by C<$name>. Known entries are:
+
+ Name    Type            Example
+ head    scalar          '1.42'
+ branch  scalar          '1.7'
+ access  array reference [ 'foo', 'bar' ]
+ symbols hash reference  { 'FOO' => '1.1', 'BAR' => '1.2' }
+ locks   hash reference  { 'foo' => '1.3', 'bar' => '1.4' }
+ strict  scalar          1
+ comment scalar          'foo bar'
+ expand  scalar          'b'
+ desc    scalar          'foo bar'
+
+=item C<$rcs-E<gt>>B<insert>C<($rev);>
+
+=item C<$rcs-E<gt>>B<remove>C<($rev);>
+
+=item C<my @revlist = $rcs-E<gt>>B<lookup>C<();>
+
+=item C<my $rev = $rcs-E<gt>>B<lookup>C<($num);>
+
+=item C<my $rev = $rcs-E<gt>>B<lookup>C<($tag);>
+
+Methods B<insert> and B<remove> insert and remove a RCS::Revision object
+C<$rev> to/from the RCS object C<$rcs>. Method B<lookup> lookups the
+RCS file content revision(s) and returns either all existing revision
+objects or a particular revision looked up by revision number or
+revision tag. The result objects are of type RCS::Revision.
+
+=item C<my $rev = >B<new>C< RCS::Revision>[ C<$rev>]C<;>
+
+=item C<$rev-E<gt>>B<destroy>C<;>
+
+Method B<new> creates a new RCS::Revision object. Method B<destroy>
+destroys the RCS object and frees all its resources.
+
+=item [C<my $rev = >]C<$rev-E<gt>>B<revision>C<(>[C<$rev>]C<);>
+
+Method B<revision> gets and/or sets the revision number of the object.
+
+=item C<my @names = $rev-E<gt>>B<get>C<();>
+
+=item C<my $value = $rev-E<gt>>B<get>C<($name);>
+
+=item C<$rev-E<gt>>B<set>C<($name, $value);>
+
+Methods B<get> and B<set> get and/or set the value of the RCS::Revision
+entry identified by C<$name>. Known entries are:
 
-This saves the RCS file content from RCS object C<$rcs> under C<$filename>.
+ Name     Type            Example
+ date     scalar          2004.04.24.10.20.30
+ author   scalar          foo
+ state    scalar          Exp;
+ branches array reference [ '1.1.1', '1.3.2', '1.4.2' ]
+ next     scalar          '1.2'
+ log      scalar          'foo bar'
+ text     scalar          "a0 1\nfoo bar\n"
 
 =back
 

CVSTrac 2.0.1