#!/usr/bin/perl -w

######################################################################
###
###   IMPORTANT!
###   Please read the warranty and legal notice 
###   at the end of this file!
###
######################################################################

require 5.000;
use lib '/usr/local/bin',"$ENV{HOME}/bin",'/usr/stud/loescher/bin';
use lib 'd:/bin','c:/mydos','c:/bin','/usr/sl';
use slutil;
use mo;
use leosub;
use English;
use FileHandle;
use File::Copy;

######################################################################
### Voreinstellungen
######################################################################

$version     = '0.76';
$appname     = 'slBackup';

$Logfile     = "/tmp/\L$appname\E.log";
$backuphome  = "$ENV{HOME}/\L$appname\E";

&Hilfe if ($#ARGV<0);

%config   = ();
@ToBackup = ();

ReadConfigFile();

foreach (keys %config)
{
  print "DEBUG: $_ = $config{$_}\n";
}
print "ToBackup: ",join("\n",@ToBackup),"\n";

# Fehler abfangen
unless (-e $config{tape})
{
  unless ($config{tape} =~ /.+:.+/)
  {
    die "Das angegebene Bandlaufwerk '$config{tape} existiert nicht!\n";
  }
}
unless (-e $config{mo})
{
  die "Das angegebene MO-Laufwerk '$config{mo} existiert nicht!\n";
}

unless (-d $backuphome)
{
  print "Lege Verzeichnis '$backuphome' an...\n";
  mkdir($backuphome, 0755) || die "Kann Verzeichnis '$backuphome' nicht anlegen!\n";
}

if (-e $Logfile)
{
  rename $Logfile,"$Logfile.old";
}

unlink "/tmp/\L$appname\E.pfade" if -e "/tmp/\L$appname\E.pfade";

# Kommando zum Bandwechseln
if ($config{change_tape} ne '')
{
  if ($config{change_tape} =~ /internal/i)
  {
    $changeTape = CreateChangeBackupTape();
  }
  else
  {
    $changeTape = $config{change_tape};
  }
}

# Kommando zum MO-Wechseln
if ($config{change_mo} ne '')
{
  if ($config{change_mo} =~ /internal/i)
  {
    $changeMO = CreateChangeBackupMO();
  }
  else
  {
    $changeMO = $config{change_mo};
  }
}

# Kommando zum Wechseln des inkrementellen "Mediums"
$changeIncr = $config{change_incr} || 'true';

######################################################################
### Hauptprogramm
######################################################################

### AFIO

# Auspacken
# -k : Fehler überspringen
# -n : neuere Files nicht ueberschreiben
# -x : Retain file ownership and setuid/setgid permissions

# Inhalt anzeigen
# -t

# Auspacken
# -i
# -s 0 : multi-volume-prompting
# Also: 
# cd ziel ; afio -vinkxZ -s 0 -H ChangeBackupMO testfile.afio

# Verify
# -r

# Auf eine normal ext2-formatierte MO passen genau 220354560 Bytes
# Wenn man mit mke2fs -v -i 262144 ... formtiert, dann 227380224

# Zeitmessung auf MO:
# Daten: 134 MB
# -Z:   8.5 min, 52 MB   
# -G 1: 7   min, 55 MB      
# -G 9: 13  min, 51 MB   
# ohne -Z: 10 min !!! und 141 MB !!!


# *********************************************************************

printumlaute Kopf();

$tape_backup = $FALSE;
$mo_backup   = $FALSE;
$full        = $FALSE;
$incremental = $FALSE;
$rmlabel     = $FALSE;
$mo2tape     = $FALSE;
$afio        = $FALSE;

while (defined $ARGV[0])
{
 SWITCH:
  {
    if ($ARGV[0] eq '-tape') { $tape_backup = $TRUE; shift @ARGV; last SWITCH;}
    if ($ARGV[0] eq '-mo'  ) { $mo_backup   = $TRUE; shift @ARGV; last SWITCH;}
    if ($ARGV[0] eq '-full') { $full        = $TRUE; shift @ARGV; last SWITCH;}
    if ($ARGV[0] eq '-incr') { $incremental = $TRUE; shift @ARGV; last SWITCH;}
    if ($ARGV[0] eq '-afio') { $afio        = $TRUE; shift @ARGV; last SWITCH;}
    if ($ARGV[0] eq '-rmlabel') { $rmlabel  = $TRUE; shift @ARGV; last SWITCH;}
    if ($ARGV[0] eq '-mo2tape') { $mo2tape  = $TRUE; shift @ARGV; last SWITCH;}
    # Sonst:
    die "Option '$ARGV[0]' gibt es nicht!\n";
  }
}

# Backup von MOs auf Band
if ($mo2tape)
{
  while( MOBackup() ) {};
  Label();
  exit;
}

unless ($incremental)
{
  unless ($tape_backup || $mo_backup)
  {
    die "Sie müssen einen der Schalter '-tape' oder '-mo' angeben!\n";
  }
}

unless ($full || $incremental)
{
  die "Sie müssen einen der Schalter '-full' oder '-incr' angeben!\n";
}

if ($full && $incremental)
{
  die "Sie dürfen nur einen der beiden Schalter '-full' oder '-incr' angeben!\n";
}

if ($mo_backup && $mo2tape)
{
  die "Eine Sicherung von MO auf MO ist nicht möglich!\n";
}

Full_Tape()   if ($full && $tape_backup);

Full_MO()     if ($full && $mo_backup);

if ($incremental)
{
  if ($tape_backup || $mo_backup || $afio || $rmlabel || $mo2tape)
  {
    warn "Alle anderen Schalter außer '-incr' werden ignoriert.\n";
  }
  Incremental();
}

Label();
exit;


######################################################################
### Unterprogramme
######################################################################


sub Incremental
{
  my $incrlist;
  my $incrlistbak;

  print "Inkrementelle Sicherung nach $config{incr_dev}...\n";

  if ($config{pre_incr} ne '')
  {
    system($config{pre_incr});
  }

  unless (-d $config{incr_dev})
  {
    print "Das Verzeichnis '$config{incr_dev}' existiert nicht!\n";
    print "Soll es angelegt werden? (J/N) ";
    my $input = readkey(); print "\n";
    die "Abbruch.\n" unless $input =~ /^[jy]$/i;
    mkdir ($config{incr_dev}, 0755) || die "Kann Verzeichnis '$config{incr_dev}' nicht anlegen!\n";
  }

  print "LOG-File kann mit 'tail -f $Logfile' betrachtet werden.\n";
  my $datum = longdate();
  foreach $pfad (@ToBackup)
  {
    print "Backup von $pfad ...\n";
    WriteLogFile($pfad);

#    if ($afio)
#    {
#      system ("find $pfad/ -xdev | afio -ovBZ -H $changeTape -s 0 -M 10m ".
#	      "-G 1 $config{tape} >> $Logfile");
#    }

    $pfadabc = $pfad;
    $pfadabc =~ s/\W/_/g;
    $incrlist = "$backuphome${slash}incrlist.$pfadabc";
    $incrlistbak = $incrlist.'.bak';
    if (-e $incrlist)
    {
      unlink $incrlistbak if -e $incrlistbak;
      copy($incrlist, $incrlistbak) ||
      warn "Fehler beim Kopieren von '$incrlist' nach '$incrlistbak'\n";
    }
    $pfadabc .= '.'.$datum;
    system("tar -F $changeIncr --totals -C $pfad -g $incrlist ".
	   "-cvlpRMSf $config{incr_dev}$slash$pfadabc.tar . >> $Logfile");
  };
  WriteLogFile("ENDE");

  if ($config{post_incr} ne '')
  {
    system($config{pre_incr});
  }
}


sub Full_MO
{
  my $incrlist;
  my $incrlistbak;

  print "Komplettes Backup auf MO...\n";

  print "Bitte erste MO einlegen! (Dann Return drücken)\n";
  $dummy = ""; 
  while ( $dummy ne "\n" ) { read STDIN, $dummy, 1; };
  
  if ($config{pre_mo} ne '')
  {
    if ($config{pre_mo} =~ /internal/i)
    {
      # Erste MO mounten
      print "Mounte MO ...\n";
      mountmo() || die "Fehler beim Mounten der MO!\n";
    }
    else
    {
      system($config{pre_mo});
    }
  }
  
  print "LOG-File kann mit 'tail -f $Logfile' betrachtet werden.\n";
  foreach $pfad (@ToBackup)
  {
    print "Backup von $pfad ...\n";
    system("echo '$pfad' >> /tmp/\L$appname\E.pfade");
    WriteLogFile($pfad);

    my $filename = $pfad; # "/usr/local/bin"
    $filename =~ s:/:_:g; # "_usr_local_bin"

    if ($afio)
    {
      system ("find $pfad/ -xdev | afio -ovBZ -H $changeMO -s 0 -M 10m ".
	      "-G 1 $config{mo}/$filename.afio >> $Logfile");
    }
    else
    {
      my $pfadabc = $pfad;
      $pfadabc =~ s/\W/_/g;
      $incrlist = "$backuphome${slash}incrlist.$pfadabc";
      $incrlistbak = $incrlist.'.bak';
      if (-e $incrlist)
      {
	unlink $incrlistbak if -e $incrlistbak;
	rename $incrlist, $incrlistbak;
      }
      system("tar -F $changeMO --totals -C $pfad -g $incrlist ".
	     "-cvlpRMSf $config{mo}/$filename.tar . >> $Logfile");
    }

  };
  WriteLogFile("ENDE");

  print "Schreibe LOG-File auf MO...\n";
  system ("cp $Logfile $config{mo}/");

  if ($config{post_mo} ne '')
  {
    if ($config{post_mo} =~ /internal/i)
    {
      # Letzte MO auswerfen
      print "Unmounten der MO ...\n";
      umountmo() || die "### Fehler beim Unmounten der MO!\n";
    }
    else
    {
      system($config{post_mo});
    }
  }
}


sub CreateChangeBackupTape
{
  # Es wird ein Programm in /tmp/ erstellt, das von tar zum Bandwechsel
  # verwendet wird.
  # Parameter: -
  # Return:    Name des erzeugten Programms
  #
  my $file = "/tmp/ChangeBackupTape.$$";
  my $fh = FileHandle->new();
  open($fh, ">$file");
  print $fh <<'PROG';
#!/usr/bin/perl -w
$|=1; # Ausgaben nicht puffern
PROG

  print $fh "\$tape = '$config{tape}';
system(\"echo '---' >> /tmp/\L$appname\E.pfade\");\n";

  print $fh <<'PROG';
print "Rückspulen und Auswerfen des Bandes...\n";
system("mt -f $tape rewoffl");
$fehler = ($?>>8);
if ( $fehler != 0 ) { print "### Fehler beim Rückspulen: $fehler \n"; }
print "Bitte nächstes Band einlegen! (Dann Return drücken)\n";
$dummy = ""; 
while ( $dummy ne "\n" ) { read STDIN, $dummy, 1; };
PROG
  close $fh;
  chmod 0755, $file;
  return $file;
}


sub CreateChangeBackupMO
{
  # Es wird ein Programm in /tmp/ erstellt, das von tar zum MOwechsel
  # verwendet wird.
  # Parameter: -
  # Return:    Name des erzeugten Programms
  #
  my $file = "/tmp/ChangeBackupMO.$$";
  my $fh = FileHandle->new();
  open($fh, ">$file");
  print $fh <<'PROG';
#!/usr/bin/perl -w
require 5.000;
use lib '/usr/local/bin',"$ENV{HOME}/bin",'/usr/stud/loescher/bin';
use lib 'd:/bin','c:/mydos','c:/bin';
use mo;
$|=1; # Ausgaben nicht puffern
PROG

  print $fh "system(\"echo '---' >> /tmp/\L$appname\E.pfade\");\n";

  print $fh <<'PROG';
warn "Unmounten der MO ...\n";
umountmo || do
{
  warn "### Fehler beim Unmounten der MO!\n";
  exit(1);
};
warn "Bitte nächste MO einlegen! (Dann Return drücken)\n";
if (-t STDIN)
{
  $dummy = ""; 
  while ( $dummy ne "\n" ) { read STDIN, $dummy, 1; };
}
else
{
  warn "Uhh, ich bin mit keinem Terminal verdbunden!\n";
  warn "Ich versuche ein XTerm zur Tastatureingabe zu öffnen...\n";
  system('xterm -e bash -c "echo Weiter... ; read"');
  if ($?>>8)
  {
    warn "Kann kein XTerm öffnen. Ich hole mir die .Xauthority...\n";
    system ('/bin/cp /home/loescher/.Xauthority /root/');
    system('xterm -e bash -c "echo Weiter... ; read"');
  }
}
warn "Mounte die MO ...\n";
mountmo || do
{
  warn "### Fehler beim Mounten der MO!\n";
  exit(1);
};
warn "Backup geht weiter...\n";
exit(0);
PROG
  close $fh;
  chmod 0755, $file;
  return $file;
}


sub Full_Tape
{
  my $position;
  my $incrlist;
  my $incrlistbak;

  print "Komplettes Backup auf Band...\n";

  if ($config{pre_tape} ne '')
  {
    system($config{pre_tape});
  }

  print "LOG-File kann mit 'tail -f $Logfile' betrachtet werden.\n";
  foreach $pfad (@ToBackup)
  {
    print "Backup von $pfad ...\n";
    chomp($position = `mt -f $config{tape} tell`);
    system("echo '$position: $pfad' >> /tmp/\L$appname\E.pfade");
    WriteLogFile($pfad);

    if ($afio)
    {
      system ("find $pfad/ -xdev | afio -ovBZ -H $changeTape -s 0 -M 10m ".
	      "-G 1 $config{tape} >> $Logfile");
    }
    else
    {
      $pfadabc = $pfad;
      $pfadabc =~ s/\W/_/g;
      $incrlist = "$backuphome${slash}incrlist.$pfadabc";
      $incrlistbak = $incrlist.'.bak';
      if (-e $incrlist)
      {
	unlink $incrlistbak if -e $incrlistbak;
	rename $incrlist, $incrlistbak;
      }
      system("tar -F $changeTape --totals -C $pfad -g $incrlist ".
	     "-cvlpRMSf $config{tape} . >> $Logfile");
    }

    system("mt -f $config{tape} tell >> $Logfile");
  };
  WriteLogFile("ENDE");

  print "Schreibe LOG-File auf Band...\n";
  system("tar -F $changeTape --totals -cvlpRMSf $config{tape} $Logfile");

  if ($config{post_tape} ne '')
  {
    system($config{pre_tape});
  }
};


sub ReadConfigFile
{
  # Es wird die Konfigurationsdatei eingelesen und im globalen Hash %config
  # gespeichert und die Backupverzeichnisse werden im globalen @ToBackup
  # gespeichert.
  # Parameter: -
  # Return:    -
  #
  my $appname = lc $appname;
  my $file = "./${appname}rc";
  $file = "$ENV{HOME}/.${appname}rc" unless -r $file;
  $file = "/etc/${appname}rc" unless -r $file;
  die "Kann weder './${appname}rc' noch '$ENV{HOME}/.${appname}rc' noch ".
  "'/etc/${appname}rc' lesen!\nHilfe druch Aufruf ohne ".
  "Parameter.\n" unless -r $file;

  my @options = qw ( 
		    tape
		    pre_tape
		    post_tape
		    change_tape
		    mo
		    pre_mo
		    post_mo
		    change_mo
		    incr_dev
		    pre_incr
		    post_incr
		    change_incr
		    label
		   );

  my $parse_code;
  foreach (@options)
  {
    # Vorbelegung
    $config{$_} = '';
    # Pro Option gibt es eine if-Zeile, die die Option erkennt:
    $parse_code .= "\$config{$_} = \$1 if /^$_\\s*=\\s*(.+)/io;\n";
  }

  my $fh = FileHandle->new();
  open($fh, $file);
  while(<$fh>)
  {
    next if /^\#/; # Kommentare überspringen
    # Staffel von if-Statements:
    eval $parse_code;
    last if /^DIRECTORIES/io;
    next;
  }
  
#  # Nicht-optionale Variablen müssen belegt sein
#  foreach (qw (tape mo))
#  {
#    die "Kein '$_' in '$file' definiert!\n" if $config{$_} eq '';
#  }

  # Es muß entweder ein Tape oder MO definiert sein.
  if ( ($config{tape} eq '') && ($config{mo} eq '') )
  {
    die "Sie müssen mindestens TAPE oder MO in '$file' definieren!\n";
  }

  # Verzeichnis zum Backup einlesen
  while(<$fh>)
  {
    next if /^\#/;   # Kommentare überspringen
    next if /^\s*$/; # Leerzeilen überspringen
    chomp;
    push @ToBackup, $_;
  }
  
  close $fh;
}


sub MOBackup
{
  my $position;
  my $inclist;
  my $incrlistbak;

  my $pfad = $config{mo};
  print "MO-Backup ($pfad) ...\n";
  print "Bitte gewünschte MO einlegen! (Abbruch mit 'q'.)\n";
  my $input = readkey;
  return $FALSE if $input =~ /q/;

  mountmo() || die "Fehler beim Mounten!\n";
  my $name = ReadLabelFile($pfad.$slash.'label.dat') || 'MO-NoLabel';
  print "LOG-File kann mit 'tail -f $Logfile' betrachtet werden.\n";
  print "Backup von $pfad ...\n";
  chomp($position = `mt -f $config{tape} tell`);
  system("echo '$position: $pfad : $name' >> /tmp/\L$appname\E.pfade");
  WriteLogFile("$pfad : $name");

  $incrlist = "$backuphome${slash}incrlist.$name";
  $incrlistbak = $incrlist.'.bak';
  if (-e $incrlist)
  {
    unlink $incrlistbak if -e $incrlistbak;
    rename $incrlist, $incrlistbak;
  }

  if ($afio)
  {
    system ("find $pfad/ -xdev | afio -ovBZ -H $changeTape -s 0 -M 10m ".
	    "-G 1 $config{tape} >> $Logfile");
  }
  else
  {
    system("tar -F $changeTape --totals -C $pfad -g $incrlist ".
	   "-cvlpRMSf $config{tape} . >> $Logfile");
  }
  system("mt -f $config{tape} tell >> $Logfile");
  WriteLogFile("ENDE");
  umountmo() || die "Fehler beim Un-Mounten!\n";
};


sub Label
{
  # Erstellt einen Zettel für Bänder

  my $i = 0;
  my @AusdruckListe = ();
  my @pfadergebnis = `cat /tmp/\L$appname\E.pfade`;
  foreach (@pfadergebnis)
  {
    next if /^\s*$/;
    chomp;
    $AusdruckListe[$i] .= "$_\n" unless /^---/;
    if (/^---/)
    {
      $AusdruckListe[$i] =~ s/, $//;
      $i++;
    }
  }
  $AusdruckListe[$i] =~ s/, $//;
  
  $i = 1;
  my $total = $#AusdruckListe+1;
  my $text = '';
  foreach(@AusdruckListe)
  {
    $text .= "Band $i/$total  ".date."\n";
    $text .= $_;
    $i++;
  }

  my $fh = new FileHandle;
  # Soll das Label auf einen Pipe geschrieben werden?
  if ($config{label} =~ /^\|/)
  {
    $text .= chr(12); # Formfeed
    print "Label wird auf '$config{label}' geleitet...\n";
    open($fh, $config{label});
  }
  else # In ein File schreiben
  {
    unlink $config{label} if $rmlabel; # Altes Label-File löschen.
    print "Label wird nach '$config{label}' geschrieben...\n";
    open($fh, ">>$config{label}");
  }
  print $fh $text;
  close $fh;
}


sub WriteLogFile
{
  my $pfad = shift;
  open (LOGFILE, ">>$Logfile") || die "Kann Logfile nicht öffnen!\n";
  print LOGFILE "### ", scalar localtime,"\n";
  print LOGFILE "### $pfad\n";
  close LOGFILE;
}


sub Kopf
{
  my $head = "$appname $version   -   von Stephan Löscher";
  return "\n$head\n" . '~' x length($head) . "\n";
}


sub Hilfe
{
  printumlautepaged
  Kopf().
"Syntax: \L$appname\E OPTIONEN

Optionen:
-tape    : Backup auf Band
-mo      : Backup auf MO
-full    : Komplett-Backup
-incr    : Inkrementelles Backup
-rmlabel : Altes Label-File, falls vorhanden löschen
-mo2tape : Backup eines MOs auf Band
-afio    : Sicherung mit AFIO und Kompression statt TAR.
           Mit AFIO sind keine inkrementellen Backups möglich.

Es muß entweder '-tape' oder '-mo' und entweder '-full' oder '-incr'
angegeben werden.
Wenn '-tape' zusammen mit '-mo2tape' angegeben wird, dann wird nach dem Backup
auf Band nach MOs gefragt, die auf das gleiche Band gesichert werden können.

Beispiel: \L$appname\E -full -tape

Die Konfigurationsdatei ./\L$appname\Erc, ~/.\L$appname\Erc oder /etc/\L$appname\Erc
hat diesen Aufbau:

TAPE        = Band-Gerät, auf das gesichert werden soll, z.B. /dev/tape
PRE_TAPE    = Kommando, das vor dem ersten Band-Zugriff gestartet wird
POST_TAPE   = Kommando, das nach dem letzten Band gestartet wird
CHANGE_TAPE = Kommando, das zum Bandwechsel gestartet wird oder 'internal'
MO          = Mountpoint des MOs
PRE_MO      = Kommando, das vor dem ersten MO-Zugriff gestartet wird
              oder 'internal'
POST_MO     = Kommando, das nach dem letzten MO gestartet wird oder 'internal'
CHANGE_MO   = Kommando, das zum MO-Wechsel gestartet wird oder 'internal'
INCR_DEV    = Mountpoint für inkrementelle Backups
PRE_INCR    = Kommando, das vor dem inkrementellen Backup gestartet wird
POST_INCR   = Kommando, das nach dem inkrementellen Backup gestartet wird
CHANGE_INCR = Kommando, das gestartet wird, wenn das Backupmedium für
              inkrementelle Backups voll ist
LABEL       = Filename oder Pipe für das Label
DIRECTORIES
Pro Zeile ein Verzeichnis

Beispiel:

TAPE        = /dev/tape
PRE_TAPE    =
POST_TAPE   =
CHANGE_TAPE = internal
MO          = /m
PRE_MO      = internal
POST_MO     = internal
CHANGE_MO   = internal
INCR_DEV    = /xtemp
PRE_INCR    =
POST_INCR   =
CHANGE_INCR =
LABEL       = /tmp/label.txt
DIRECTORIES
/
/home
/usr/local
/usr/src
/var/spool/news

";
exit;
}


######################################################################
#
# Warranty and legal notice
# ~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Copyright (c) 1997 by Stephan Löscher  -  all rights reserved
# My Address: Stephan Löscher, Dr.Troll-str. 3, 82194 Gröbenzell, Germany
# Email: loescher@gmx.de
# WWW: http://www.loescher-online.de/
#
# This program is freeware.
# It is NOT Public-Domain-Software!
# The author (Stephan Löscher) does NOT give up his copyright, but he 
# reserves his copyright. Usage and copying is free of charge for private
# use, but NOT for commercial use!
# 
# You may and should copy this program free of charge, use it,
# give it to your friends, upload it to a BBS or something similar, under
# the following conditions:
# * Don't charge any money for it. If you upload it to a BBS, make sure that
#    it can be downloaded free (without paying for downloading it, except
#    for usage fees that have to be paid anyway). Small copying fees (up to
#    5 DM or 3 $US) may be charged.
#  * Only distribute the whole original package, with all the files included.
#  * This program may not be part of any commercial product or service without
#    the written permission by the author.
#  * If you want to include this program on a CD-ROM and/or book, please send
#    me a free copy of the CD/book (this is not a must, but I would appreciate
#    it very much).
# 
# Distribution of the program is explicitly desired, provided that the above
# conditions are accepted.
# 
# YOU ARE USING THIS PROGRAM AT YOUR OWN RISK! THE AUTHOR (STEPHAN LÖSCHER)
# IS NOT LIABLE FOR ANY DAMAGE OR DATA-LOSS CAUSED BY THE USE OF THIS PROGRAM
# OR BY THE INABILITY TO USE THIS PROGRAM. IF YOU ARE NOT SURE ABOUT THIS, OR
# IF YOU DON'T ACCEPT THIS, THEN DO NOT USE THIS PROGRAM!
# BECAUSE OF THE VARIOUS HARDWARE AND SOFTWARE ENVIRONMENTS INTO WHICH THIS
# PROGRAM MAY BE PUT, NO WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE IS
# OFFERED.
# GOOD DATA PROCESSING PROCEDURE DICTATES THAT ANY PROGRAM BE THOROUGHLY
# TESTED WITH NON-CRITICAL DATA BEFORE RELYING ON IT.
# 
# No part of the documentation may be reproduced, transmitted, transcribed,
# stored in any retrieval system, or translated into any other language in
# whole or in part, in any form or by any means, whether it be electronic,
# mechanical, magnetic, optical, manual or otherwise, without prior written
# consent of the author, Stephan Löscher.
# 
# You may not make any changes or modifications to this software or this
# manual. You may not decompile, disassemble, or otherwise reverse-engineer
# the software in any way.
# If you got the source, then you are permitted to modify it if you
# contact me and tell me your enhancements.
# You also may include the source as a whole or parts of it into other
# programs, as long as you don't make profit directly out of selling
# the result. If you re-use code of this program then do not remove my name!
# If you include this source-code in your projects, mark it clearly as such
# "... derived from code XXX by Stephan Löscher".
# But don't distribute modified code!
# 
# If you believe your copy of this software has been tampered or altered in
# anyway, shape or form, please contact me immediately! Do not hesitate a
# moment to inform me. Remember, this software should be available to all, in
# the original form, so please do not accept modified or damaged versions of
# my software.
# 
# The author reserves his right for taking legal steps if the copyright or the
# license agreement is violated.
# 
# All product names mentioned in this software are trademarks or registered
# trademarks of their respective owners.
# 
# If you have any questions, ideas, suggestions for improvements or if you find
# bugs (I don't hope so.) then feel free to contact me. (Email is appreciated.)
# 
# I'm not a native english speaker. If you are one and discover some strange
# sounding parts in this documentation or in the program, please, feel free
# to point it out to me and give me suggestions for alteration!
# 
# If the program works for you, and you want to honour my efforts, you are
# invited to donate as much as you want... :)
#
# In any case, if you don't like the restrictions in this license, contact
# me, and we can work something out.
#
######################################################################
