#!/usr/bin/perl
#
# Dump/Restore memory content of Elecraft K3
# © 2011-04-25 - Pierfrancesco Caci, ik5pvx
# 
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# changelog:
# 20110428 - first basic working read only version; added quick memories
# 20110502 - changed name to k3mem; command line support (implemented:
#            help, man, verbose, debug, device, speed, all normal memories,
#            all quick memories, ); check that the radio is really a K3
# 20110503 - command line support (added: a single memory, a list of
#            memories, a group of quick memories by band), properly 
#            recall the content of VFO B for normal memories
# 20110504 - command line support (added: filename); save in csv format to 
#            file instead of Data::Dumper; read back the savefile
# 20110505 - finished read back from savefile; quick memories can actually
#            store VFO B as well; merge data from radio with that on file;
#            command line support (added: restore);
# 20110507 - rudimentary restore, no way to write the label for now;
#
# TODO:
# - command line options
#   * switches for: a range of memories
#   * read only vfo A or A+B
# - ask if overwrite file content is ok
# - save to file and read it back
# - basic editing of a channel
# - verify if we need to read other data
# - finish man page/pod
# 
#
# credits:
# Sample use of Device::SerialPort blatantly copied from
#     http://ubuntuforums.org/archive/index.php/t-432277.html
# Sequence of commands to read the memory label courtesy of
#     Mike Markowski, ab3ap

use warnings;
use diagnostics;
use strict;

use Data::Dumper;
$Data::Dumper::Indent = 3;

use Getopt::Long;
use Pod::Usage;
#use Carp;

use Time::HiRes qw(usleep);
use Device::SerialPort;

# safe defaults, can be changed by command line options
my $device="/dev/ttyS0";
my $speed=38400;
my $savefile="$ENV{'HOME'}/k3memdump";
# end configurable part

# timings. critical, don't reduce them too much
my $minsleep=50000; # microseconds
my $midsleep=5*$minsleep;
my $longsleep=12*$minsleep;

# band numbers as per K3 Programmer's Reference rev C14
my %bands = (
	     0  => '160m',
	     1  => '80m',
	     2  => '60m',
	     3  => '40m',
	     4  => '30m',
	     5  => '20m',
	     6  => '17m',
	     7  => '15m',
	     8  => '12m',
	     9  => '10m',
	     10 => '6m',
	     # 11 => 'res11', # uncomment when no longer reserved
	     # 12 => 'res12',
	     # 13 => 'res13',
	     # 14 => 'res14',
	     # 15 => 'res15',
	     # 16 => 'xv1', # uncomment if you have transverters
	     # 17 => 'xv2',
	     # 18 => 'xv3',
	     # 19 => 'xv4',
	     # 20 => 'xv5',
	     # 21 => 'xv6',
	     # 22 => 'xv7',
	     # 23 => 'xv8',
	     # 24 => 'xv9',
	    );


my %bandsbyname;
foreach (keys %bands) {
  $bandsbyname{$bands{$_}}=$_;
}



my $verbose=0;
my $debug=0;
my $man=0;
my $help=0;
my $all='';
my $quick='';
my @mems=();
my @qmb=();
my $bandhelp=0;
my $restore=0;

Getopt::Long::Configure ("bundling");

GetOptions('help|h|?'     => \$help,  # help options
	   'man'          => \$man,
	   'verbose|v+'   => \$verbose,
	   'debug|D'      => \$debug,
	   'device|d=s'   => \$device, # serial port options
	   'speed|s=i'    => \$speed,
	   'all|a'        => \$all,
	   'quickall|q'   => \$quick,
	   'memories|m=s' => \@mems,
	   'bands|b=s'    => \@qmb,
	   'listbands|l'  => \$bandhelp,
	   'file|f=s'     => \$savefile,
	   'restore'      => \$restore,

	  ) or pod2usage(0);
pod2usage(-exitstatus => 1, -verbose=>0,
	  -msg =>
	  "Supported band names (edit %bands in source file to change):\n" .
	  join(', ',(keys %bandsbyname)) . "\n"
	 ) if $bandhelp;
pod2usage(1) if $help;
pod2usage(-exitstatus => 0, -verbose => 2) if $man;

@mems=split(/,/,join(',',@mems));
@qmb=split(/,/,join(',',@qmb));

print "Options:\nv:$verbose D:$debug\n" .
  "d:$device s:$speed\nf:$savefile\n" .
  "a:$all q:$quick b:@qmb\n" .
  "m:@mems\n" if $debug;

# K3 only supports these 4 speeds
if ($speed != 4800 && $speed != 9600 && 
    $speed != 19200 && $speed != 38400) {
  print STDERR "Invalid serial port speed.\n" . 
    "Supported values are 4800, 9600, 19200, 38400\n";
  exit 1;
}


my %saveA;
my %saveB;

my %memories;
my %savedmemories;
my $autoinfo;
#my $memscroll;
my $havefile=0;

# check if the savefile exists and is readable
if (-e $savefile  ) {
  open (FILE,"<$savefile") || die "Can't open file $savefile for read:\n$!";
  print "Reading saved memory contents from $savefile\n" if $verbose;
  while (<FILE>) {
    next if /^#/;
    my ($mem,$label,$fa,$md,$bw,$is,$ra,$pa,$nb,$nl,$xf,$an,
	$fb,$mdb,$bwb,$rab,$pab,$nbb,$nlb,$xfb) = split /,/;
    $mem = sprintf "%03d", $mem;

    $savedmemories{$mem}{'displayB'}=$label if ($label);
    $savedmemories{$mem}{'vfoA'}{'freq'}=$fa if ($fa);
    $savedmemories{$mem}{'vfoA'}{'md'}=$md if ($md);
    $savedmemories{$mem}{'vfoA'}{'bw'}=$bw if ($bw);
    $savedmemories{$mem}{'vfoA'}{'is'}=$is if ($is);
    $savedmemories{$mem}{'vfoA'}{'ra'}=$ra if ($ra);
    $savedmemories{$mem}{'vfoA'}{'pa'}=$pa if ($pa);
    $savedmemories{$mem}{'vfoA'}{'nb'}=$nb if ($nb);
    $savedmemories{$mem}{'vfoA'}{'nl'}=$nl if ($nl);
    $savedmemories{$mem}{'vfoA'}{'xf'}=$xf if ($xf);
    $savedmemories{$mem}{'vfoA'}{'an'}=$an if ($an);
    $savedmemories{$mem}{'vfoB'}{'freq'}=$fb if ($fb);
    $savedmemories{$mem}{'vfoB'}{'md'}=$mdb if ($mdb);
    $savedmemories{$mem}{'vfoB'}{'bw'}=$bwb if ($bwb);
#    $savedmemories{$mem}{'vfoB'}{'is'}=$is if ($is);
    $savedmemories{$mem}{'vfoB'}{'ra'}=$rab if ($rab);
    $savedmemories{$mem}{'vfoB'}{'pa'}=$pab if ($pab);
    $savedmemories{$mem}{'vfoB'}{'nb'}=$nbb if ($nbb);
    $savedmemories{$mem}{'vfoB'}{'nl'}=$nlb if ($nlb);
    $savedmemories{$mem}{'vfoB'}{'xf'}=$xfb if ($xfb);
#    $savedmemories{$mem}{'vfoB'}{'an'}=$an if ($an);


  }
  close FILE;
  print Dumper(\%savedmemories) if $debug;
#  sleep 10 if $debug;
} 

# open the serial device
my $port = Device::SerialPort->new($device) || 
  die "Can't open $device: $!\n";

# serial port settings
$port->baudrate($speed);
$port->parity("none");
$port->handshake("none");
$port->databits(8);
$port->stopbits(1);
$port->read_char_time(0);
$port->read_const_time(1);


#check if this is indeed a K3
if ( sendcommand("K3;") !~ /^K3[01];$/ ) {
  print "It doesn't look like there is an Elecraft K3 connected to $device\n" .
    "Please check if the radio is on, the serial port is connected, and\n" .
      "the serial port is the right one.\n";
  exit 1;
}

# save autoinfo status, then disable it
$autoinfo = sendcommand("AI;");
sendcommand("AI0;");

# save memory scroll to vfo status
#$memscroll = sendcommand("mn102;swt11;swt11;db;");
#if ($memscroll =~ /NOR/) {
#  sendcommand("swt11;");
#}
#sendcommand("mn255;");

# save current VFO status so that we can restore at the end.
print "Saving current content of VFO A & B...\n" if $verbose;
getvfo('A',\%saveA);
getvfo('B',\%saveB);

print "VFO A: ", values(%saveA) , "\n" if $verbose > 1;
print "VFO B: ", values(%saveB) , "\n" if $verbose > 1;

## gratuitous change to check that save/restore works
#my %testA;
#$testA{'freq'}='FA00051080150;';
#$testA{'bw'}='BW0220;';
#setvfo('A',\%testA);
#sleep 2;

# build the list of memory channels to operate on.
my @list;
if ($all) {                        # --all
  for (my $i=0;$i<100;$i++) {
    push @list, sprintf "%03d", $i;
  }
} else {                           # --memories
  foreach (@mems) {
    next unless /^\d{1,3}$/;
    next if $_ > 199;              # 100+4*(max value of bn=24)+3
    push @list, sprintf "%03d", $_;
  }
}
if ($quick) {                      # --quickall
  quickmemlist(\@list);
} else {                           # --bands
  foreach (@qmb) {
    next unless defined $bandsbyname{$_};
    m1m4($bandsbyname{$_},\@list);
  }
}

print "List: @list \n" if $debug;


if ($restore) { # push data from file to radio
  print "Writing memories to the radio...\n";
  foreach (sort @list) {
    setmem($_,\%{$savedmemories{$_}});
  }
} else {  # read data from radio to file

  print "Reading memories from the radio...\n";
  foreach (sort @list) {
    getmem($_,\%{$memories{$_}});
    #  if ($memories{$_}) {
    
    if ($savedmemories{$_}) {
      print "Replacing saved memory $_ with data from the radio\n"
	if $verbose >1;
    }
    $savedmemories{$_} = $memories{$_};
    print Dumper(\$savedmemories{$_}) if $debug;
    #  }
  }
}

# restore VFO status to what we had before
print "Restoring previous content of VFO A & B...\n" if $verbose;
setvfo('A',\%saveA);
setvfo('B',\%saveB);

# restore autoinfo
sendcommand($autoinfo);

# restore memory scroll to vfo status
#if ($memscroll =~ /NOR/) {
#  sendcommand("mn102;swt11;mn255;");
#}

$port->close();

#print Dumper("VFO A:",%saveA,"VFO B:",%saveB);

if (!$restore) {

  open (FILE, ">$savefile") || die "Can't open file $savefile for write:\n$!\n";
  print Dumper(\%memories) if $debug;
  print FILE "#mem,label,fa,md,bw,is,ra,pa,nb,nl,xf,an,";
  print FILE "fb,md\$,bw\$,ra\$,pa\$,nb\$,nl\$,xf\$\n";
  foreach (sort keys %savedmemories) {
    print FILE "$_," . 
      (defined $savedmemories{$_}{'displayB'} ?
       "$savedmemories{$_}{'displayB'}," : ",") .
	 (defined $savedmemories{$_}{'vfoA'}{'freq'} ?
	  "$savedmemories{$_}{'vfoA'}{'freq'}," : ",") .
	    (defined $savedmemories{$_}{'vfoA'}{'md'} ?
	     "$savedmemories{$_}{'vfoA'}{'md'}," : ",") .
	       (defined $savedmemories{$_}{'vfoA'}{'bw'} ?
		"$savedmemories{$_}{'vfoA'}{'bw'}," : ",") .
		  (defined $savedmemories{$_}{'vfoA'}{'is'} ?
		   "$savedmemories{$_}{'vfoA'}{'is'}," : ",") .
		     (defined $savedmemories{$_}{'vfoA'}{'ra'} ?
		      "$savedmemories{$_}{'vfoA'}{'ra'}," : ",") .
			(defined $savedmemories{$_}{'vfoA'}{'pa'} ?
			 "$savedmemories{$_}{'vfoA'}{'pa'}," : ",") .
			   (defined $savedmemories{$_}{'vfoA'}{'nb'} ?
			    "$savedmemories{$_}{'vfoA'}{'nb'}," : ",") .
			      (defined $savedmemories{$_}{'vfoA'}{'nl'} ?
			       "$savedmemories{$_}{'vfoA'}{'nl'}," : ",") .
				 (defined $savedmemories{$_}{'vfoA'}{'xf'} ?
				  "$savedmemories{$_}{'vfoA'}{'xf'}," : ",") .
				    (defined $savedmemories{$_}{'vfoA'}{'an'} ?
				     "$savedmemories{$_}{'vfoA'}{'an'}," : ",");
    print FILE (defined $savedmemories{$_}{'vfoB'}{'freq'} ?
		"$savedmemories{$_}{'vfoB'}{'freq'}," : ",") .
		  (defined $savedmemories{$_}{'vfoB'}{'md'} ?
		   "$savedmemories{$_}{'vfoB'}{'md'}," : ",") .
		     (defined $savedmemories{$_}{'vfoB'}{'bw'} ?
		      "$savedmemories{$_}{'vfoB'}{'bw'}," : ",") .
			(defined $savedmemories{$_}{'vfoB'}{'is'} ?
			 #	     "$savedmemories{$_}{'vfoB'}{'is'}," : ",") .
			 #	       (defined $savedmemories{$_}{'vfoB'}{'ra'} ?
			 "$savedmemories{$_}{'vfoB'}{'ra'}," : ",") .
			   (defined $savedmemories{$_}{'vfoB'}{'pa'} ?
			    "$savedmemories{$_}{'vfoB'}{'pa'}," : ",") .
			      (defined $savedmemories{$_}{'vfoB'}{'nb'} ?
			       "$savedmemories{$_}{'vfoB'}{'nb'}," : ",") .
				 (defined $savedmemories{$_}{'vfoB'}{'nl'} ?
				  "$savedmemories{$_}{'vfoB'}{'nl'}," : ",") .
				    (defined $savedmemories{$_}{'vfoB'}{'xf'} ?
				     "$savedmemories{$_}{'vfoB'}{'xf'}," : ",") . "\n";
    
  }
  close FILE;
}
print "73 TU dit dit\n";

exit 0;

#########################################################
# read from the serial port
sub getoutput {
  my $maxCnt = shift;
  my $notFndCnt = 0;

  # clear the read buffer variables
  my $readChars = 0;
  my $readBytes = "";
  my $readBuffer = "";


  while ($notFndCnt < $maxCnt) {
    # Read serial port data
    ($readChars, $readBytes) = $port->read(1);

    $readBuffer .= $readBytes;

    last if $readBytes eq ';';

    if ($readChars == 0) {
      $notFndCnt++;
      usleep $minsleep;
    } else {
      $notFndCnt = 0;
    }
  }

  return $readBuffer;
}

###############################################################
# get all info about the vfo and store it in a hash
sub getvfo {
  my ($vfo,$vref) = @_;
  #  print Dumper ($vfo, $vref, ref $vref);

  my $command = "F$vfo;"; # get the frequency
  ${$vref}{'freq'} = sendcommand($command);

  $vfo = ($vfo eq 'A'? '' : '$');

  # BW - bandwidth
  ${$vref}{'bw'} = sendcommand("BW$vfo;");

  if (! $vfo) {
    # IS - if shift  (only vfoA)
    ${$vref}{'is'} = sendcommand("IS;");
    # AN - antenna (only vfoA)
  ${$vref}{'an'} = sendcommand("AN;");
  
  }

  # MD - mode
  ${$vref}{'md'} = sendcommand("MD$vfo;");

  # NB - noise blanker
  ${$vref}{'nb'} = sendcommand("NB$vfo;");

  # NL - nb level
  ${$vref}{'nl'} = sendcommand("NL$vfo;");

  # PA - preamp
  ${$vref}{'pa'} = sendcommand("PA$vfo;");

  # RA - attenuator
  ${$vref}{'ra'} = sendcommand("RA$vfo;");

  # XF - filter
  ${$vref}{'xf'} = sendcommand("XF$vfo;");
  
}

##########################################################
# send a command to the radio, save the result
sub sendcommand {
  my $command = shift;
  my $l = length ($command);
  my $c = $port->write($command) || die "write failed: $!\n";
  die "write incomplete: $!\n" if $c != $l;
  usleep $minsleep; 
  return getoutput(5);
}

##########################################################
# set vfo according to parameters in a hash
sub setvfo {
  my ($vfo,$vref) = @_;

  # if there's a frequency to change, do it first.
  if (defined ${$vref}{'freq'}) {
    my $f = sendcommand(${$vref}{'freq'});
    warn "Frequency change ${$vref}{'freq'} failed! Return value: $f\n" 
      unless ($f eq "" || $f eq ${$vref}{'freq'});
    usleep $longsleep; # allow for band change to settle
  }

  foreach (keys %{$vref}) {
    next if $_ eq 'freq';
    my $r = sendcommand(${$vref}{$_});
    warn "Command ${$vref}{$_} failed! Return value: $r\n" 
      unless ($r eq "" || $r eq ${$vref}{$_});
    usleep $minsleep;
  }


}

##########################################################
# read a memory channel
sub getmem {
   my ($chan,$memref) = @_;
#   print Dumper ($chan, $memref, ref ($memref));

   #1) MCnnn;   to go to memory nnn
   # if CONFIG:MEM 0-9 M->V is set to NOR, the band change happens here
   # otherwise it happens at step 5 below.
   # if the memory slot is empty "NOT SET" will flash very briefly on
   # display B
   sendcommand("MC$chan;");
   usleep $longsleep; # allow for band change to settle
   sendcommand("SWT13;"); # make sure VFO B is on same band as VFO A
   usleep $minsleep;
   sendcommand("MC$chan;"); # recall memory again so as to properly get VFO B
   usleep $minsleep;
   
   if ($chan < 100) { # do this only for regular memories
   
     #2) SWT23;   to tap the M>V button to enter M>V mode
     sendcommand("SWT23;");
     usleep $midsleep; 

     #3) DS;      read display - "----" means location is empty and move to next
     my $displayA = sendcommand("DS;");
     usleep $midsleep;

     if ($displayA !~ /^DS@@>>>>@@/) {
       $memref->{'displayA'} = $displayA;
       getoutput(5);	# flush the serial port before reading further

       #4) FB;    read vfo B display which in M>V mode shows the text label :-)
       my $displayB = sendcommand("DB;");
       $memref->{'displayB'} = $displayB;
       usleep $midsleep;

       #5) SWT23;   to exit M>V mode
       sendcommand("SWT23;");
       usleep $longsleep;	# allow for band change to settle

       #6) FA;      FB and so on to read the rest of the info.
       getvfo('A',\%{$memref->{'vfoA'}});
       getvfo('B',\%{$memref->{'vfoB'}});

       print "Mem $chan: " . 
	 (defined $memref->{'displayB'} ? $memref->{'displayB'} : "") . "\n";
       print "\tVFO A: ", values(%{$memref->{'vfoA'}}) , "\n" .
	     "\tVFO B: ", values(%{$memref->{'vfoB'}}) , "\n" if $verbose > 1;

     } else {
       print "Skipping empty mem $chan\n" if $verbose;
       #5) SWT23;   to exit M>V mode
       sendcommand("SWT23;");
       usleep $midsleep;
     }
   } else { # quick memories can't have a label, "NOT SET" appears in displayB
     my $displayB = sendcommand("DB;");
     if ($displayB !~ /NOT SET/) {
       
       getvfo('A',\%{$memref->{'vfoA'}});
       getvfo('B',\%{$memref->{'vfoB'}});
       print "Quick Mem $chan: " . quickmem($chan) . "\n";
       print "\tVFO A: ", values(%{$memref->{'vfoA'}}) , "\n" .
	 "\tVFO B: ", values(%{$memref->{'vfoB'}}) , "\n" if $verbose > 1;
     } else {
       print "Skipping empty quick mem $chan\n" if $verbose;
     }

   }
#   sleep 1;
   getoutput(5); #flush the serial port
}

##########################################################
# write a memory channel
sub setmem {
   my ($chan,$memref) = @_;
   print Dumper ($chan, $memref, ref ($memref));
   
   #1) MCnnn;   to go to memory nnn
   # if CONFIG:MEM 0-9 M->V is set to NOR, the band change happens here
   # otherwise it happens at step 5 below.
   # if the memory slot is empty "NOT SET" will flash very briefly on
   # display B
   sendcommand("MC$chan;");
   usleep $longsleep; # allow for band change to settle


   # set vfo A
   # set vfo B
   setvfo('A',\%{$memref->{'vfoA'}});
   if (\%{$memref->{'vfoB'}}) {
     
     setvfo('B',\%{$memref->{'vfoB'}});
   }
   
   #2) SWT15;   to tap the V>M button to enter V>M mode
   sendcommand("SWT15;");
   usleep $midsleep; 


   #set the label


   #5) SWT15;   to exit V>M mode
   sendcommand("SWT15;");
   usleep $longsleep;	# allow for band change to settle

   #   sleep 1;
   getoutput(5); #flush the serial port
}

##########################################################
# build the full list of quick memories
sub quickmemlist {
  my $listref = shift;
  foreach (sort {$a<=>$b} keys %bands) {
    m1m4($_,$listref);
  }
}
##########################################################
# the 4 quick memories associated with a band
sub m1m4{
  my ($b,$lr)=@_;
  my $q0 = 100 + $b*4;
  push @$lr, sprintf "%03d", $q0;
  push @$lr, sprintf "%03d", $q0 + 1;
  push @$lr, sprintf "%03d", $q0 + 2;
  push @$lr, sprintf "%03d", $q0 + 3;
}

##########################################################
# pretty print the band/M-key given a memory number
sub quickmem {
  my $mem = shift;
  $mem -= 100;
  my $b = int ($mem / 4);
  my $m = $mem % 4 + 1;
  
  return "M$m on $bands{$b}";
}

__END__

=head1 k3mem

Read, save, edit and restore Elecraft K3 memories.

=head1 SYNOPSIS

k3mem [options]

 Options:
   --help      | -h       brief help message
   --man                  full documentation
   --verbose   | -v       increase verbosity
   --debug     | -D       print debugging aids
   --device    | -d       serial port device
   --speed     | -s       serial port speed
   --all       | -a       all the normal memories (00-99)
   --quickall  | -q       all the quick memories 
   --memories  | -m       comma separated list of memories
   --bands     | -b       comma separated list of bands
   --listbands | -l       list known band names
   --file      | -f       save file
   --restore              write memory content to the radio

=head1 DESCRIPTION

B<k3mem> is a program to read memories from an Elecraft K3, save them to a
CSV file and load them back to the radio. It supports both standard memories
(00 to 99) and quick per band memories (M1-M4 on each band, including 
transverter bands).

=head1 OPTIONS

=over 8

=item B<--help|-h>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=item B<--verbose|-v>

Increase the verbosity of the program. Add more -v to see more output.

=item B<--debug|-D>

Print some debugging aids.

=item B<--device|-d>

Serial port device, e.g. /dev/ttyUSB0. Defaults to /dev/ttyS0.

=item B<--speed|-s>

Serial port speed. One of 4800, 9600, 19200, 38400 b/s. Defaults to 38400.

=item B<--all|-a>

Read all the normal memory slots (from 00 to 99) from the radio.
Overrides --memories.

=item B<--quickall|-q>

Read all the quick memory slots (M1-M4 for each band) from the radio.
Hint: you can edit the source file and comment out the transverter bands
that you are not using. 
Overrides --bands.

=item B<--memories|-m>

A comma separated list of memories to read from the radio. Can be the normal 
memories (00 to 99) or the quick memories (100 to 199).

=item B<--bands|-b>

A comma separated list of bands for which  to read the quick memories 
from the radio. 

=item B<--listbands|-l>

Show the list of supported band names.

=item B<--file|-f>

The name of the file use to save the memories. Defaults to ~/k3memdump.

=item B<--restore>

Write the selected memories to the radio, if they are present in the file.
Please note that there is no public API to write the label, so nothing 
will be written.

=back

=head1 FILES

~/k3memdump (configurable from command line).

=head1 AUTHOR

Pierfrancesco Caci, ik5pvx (pf@tippete.net).

=head1 BUGS

Plenty...
The serial port routine is timing critical and sometimes reads garbage.
If you're rewriting an existing memory, the existing label is overwritten 
with garbage.

=head1 COPYRIGHT

Copyright © 2011 Pierfrancesco Caci.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

=cut


