# Copyrights 2003,2004,2007 by Mark Overmeer . # For other contributors see Changes. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 0.99. package User::Identity::Archive::Plain; use vars '$VERSION'; $VERSION = '0.91'; use base 'User::Identity::Archive'; use strict; use warnings; use Carp; my %abbreviations = ( user => 'User::Identity' , email => 'Mail::Identity' , location => 'User::Identity::Location' , system => 'User::Identity::System' , list => 'User::Identity::Collection::Emails' ); sub init($) { my ($self, $args) = @_; $self->SUPER::init($args) or return; # Define the keywords. my %only; if(my $only = delete $args->{only}) { my @only = ref $only ? @$only : $only; $only{$_}++ for @only; } while( my($k,$v) = each %abbreviations) { $self->abbreviation($k, $v) unless keys %only && !$only{$k}; } if(my $abbrevs = delete $args->{abbreviations}) { $abbrevs = { @$abbrevs } if ref $abbrevs eq 'ARRAY'; while( my($k,$v) = each %$abbrevs) { $self->abbreviation($k, $v) unless keys %only && !$only{$k}; } } foreach (keys %only) { warn "Option 'only' specifies undefined abbreviation '$_'\n" unless defined $self->abbreviation($_); } $self->{UIAP_items} = {}; $self->{UIAP_tabstop} = delete $args->{tabstop} || 8; $self; } sub from($@) { my ($self, $in, %args) = @_; my $verbose = $args{verbose} || 0; my ($source, @lines); if(ref $in) { ($source, @lines) = ref $in eq 'ARRAY' ? ('array', @$in) : ref $in eq 'GLOB' ? ('GLOB', <$in>) : $in->isa('IO::Handle') ? (ref $in, $in->getlines) : confess "Cannot read from a ", ref $in, "\n"; } elsif(open IN, "<", $in) { $source = "file $in"; @lines = ; } else { warn "Cannot read archive from file $in: $!\n"; return $self; } print "reading data from $source\n" if $verbose; return $self unless @lines; my $tabstop = $args{tabstop} || $self->defaultTabStop; $self->_set_lines($source, \@lines, $tabstop); while(my $starter = $self->_get_line) { $self->_accept_line; #warn "$verbose: $starter\n"; my $indent = $self->_indentation($starter); print " adding $starter" if $verbose > 1; my $item = $self->_collectItem($starter, $indent); $self->add($item->type => $item) if defined $item; } $self; } sub _set_lines($$$) { my ($self, $source, $lines, $tab) = @_; $self->{UIAP_lines} = $lines; $self->{UIAP_source} = $source; $self->{UIAP_curtab} = $tab; $self->{UIAP_linenr} = 0; $self; } sub _get_line() { my $self = shift; my ($lines, $linenr, $line) = @$self{ qw/UIAP_lines UIAP_linenr UIAP_line/}; # Accept old read line, if it was not accepted. return $line if defined $line; # Need to read a new line; $line = ''; while($linenr < @$lines) { my $reading = $lines->[$linenr]; $linenr++, next if $reading =~ m/^\s*\#/; # skip comments $linenr++, next unless $reading =~ m/\S/; # skip blanks $line .= $reading; if($line =~ s/\\\s*$//) { $linenr++; next; } if($line =~ m/^\s*tabstop\s*\=\s*(\d+)/ ) { $self->{UIAP_curtab} = $1; $line = ''; next; } last; } return () unless length $line || $linenr < @$lines; $self->{UIAP_linenr} = $linenr; $self->{UIAP_line} = $line; $line; } sub _accept_line() { my $self = shift; delete $self->{UIAP_line}; $self->{UIAP_linenr}++; } sub _location() { @{ (shift) }{ qw/UIAP_source UIAP_linenr/ } } sub _indentation($) { my ($self, $line) = @_; return -1 unless defined $line; my ($indent) = $line =~ m/^(\s*)/; return length($indent) unless index($indent, "\t") >= 0; my $column = 0; my $tab = $self->{UIAP_curtab}; my @chars = split //, $indent; while(my $char = shift @chars) { $column++, next if $char eq ' '; $column = (int($column/$tab+0.0001)+1)*$tab; } $column; } sub _collectItem($$) { my ($self, $starter, $indent) = @_; my ($type, $name) = $starter =~ m/(\w+)\s*(.*?)\s*$/; my $class = $abbreviations{$type}; my $skip = ! defined $class; #warn "Skipping type $type\n" if $skip; my (@fields, @items); while(1) { my $line = $self->_get_line; my $this_indent = $self->_indentation($line); last if $this_indent <= $indent; $self->_accept_line; $line =~ s/[\r\n]+$//; #warn "Skipping line $line\n" if $skip; next if $skip; my $next_line = $self->_get_line; my $next_indent = $self->_indentation($next_line); if($this_indent < $next_indent) { # start a collectable item #warn "Accepting item $line, $this_indent\n"; my $item = $self->_collectItem($line, $this_indent); push @items, $item if defined $item; #warn "Item ready $line\n"; } elsif( $this_indent==$next_indent && $line =~ m/^\s*(\w*)\s*(\w+)\s*\=\s*(.*)/ ) { # Lookup! my ($group, $name, $lookup) = ($1,$2,$3); #warn "Lookup ($group, $name, $lookup)"; my $item; # not implemented yet push @items, $item if defined $item; } else { # defined a field #warn "Accepting field $line\n"; my ($group, $name) = $line =~ m/(\w+)\s*(.*)/; $name =~ s/\s*$//; push @fields, $group => $name; next; } } return () unless @fields || @items; #warn "$class NAME=$name"; my $warn = 0; my $warn_sub = $SIG{__WARN__}; $SIG{__WARN__} = sub {$warn++; $warn_sub ? $warn_sub->(@_) : print STDERR @_}; my $item = $class->new(name => $name, @fields); $SIG{__WARN__} = $warn_sub; if($warn) { my ($source, $linenr) = $self->_location; $linenr -= 1; warn " found in $source around line $linenr\n"; } #warn $_->type foreach @items; $item->add($_->type => $_) foreach @items; $item; } sub defaultTabStop(;$) { my $self = shift; @_ ? ($self->{UIAP_tabstop} = shift) : $self->{UIAP_tabstop}; } sub abbreviation($;$) { my ($self, $name) = (shift, shift); return $self->{UIAP_abbrev}{$name} unless @_; my $class = shift; return delete $self->{UIAP_abbrev}{$name} unless defined $class; eval "require $class"; die "Class $class is not usable, because of errors:\n$@" if $@; $self->{UIAP_abbrev}{$name} = $class; } sub abbreviations() { sort keys %{shift->{UIAP_abbrev}} } 1;