#!/usr/bin/perl -w
# $Id$
# This script (sophomorix-check) is maintained by Rüdiger Beck
# It is Free Software (License GPLv3)
# If you find errors, contact the author
# jeffbeck@web.de  or  jeffbeck@gmx.de


# ===========================================================================
# Bibliotheken
# ===========================================================================
use strict;
use Getopt::Long;
Getopt::Long::Configure ("bundling");
use String::Approx 'amatch';
use String::Approx 'adist';
use Sophomorix::SophomorixConfig;
use Sophomorix::SophomorixBase;
use Time::Local;
use Time::localtime;
use Term::ANSIColor qw(:constants); # farbiger Text RED, BLUE, ...
use Date::Calc qw(check_date);

use DBI;
use Net::LDAP;
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Useqq = 1;
$Data::Dumper::Terse = 1; 
use Text::Iconv;


my @arguments = @ARGV;

# Scriptname ermitteln
my @list = split(/\//,$0);
my $scriptname = pop @list;

# nach jedem Printbefehl wieder auf Standardfarbe zurücksetzen
$Term::ANSIColor::AUTORESET = 1;
use Sophomorix::SophomorixPgLdap qw(show_modulename
                                    get_sys_users
                                    date_pg2perl
                                    update_user_db_entry
                                    user_reaktivieren
                                    user_deaktivieren
                                    check_connections
                                   );

# Es wird eine Datei sophomorix.ok (schueler+lehrer) erzeugt 
# Welche Dateien mit Userdaten sollen verarbeitet werden
my  @schueler_to_check_dateien=(
    # 0: ergebnis/schueler.txt.tmp
    "$DevelConf::schueler_datei",
    # 1: konfiguration/extraschueler.txt
    "${DevelConf::config_pfad}/extraschueler.txt",
    # 2:konfiguration/extrakurse.txt
    "${DevelConf::ergebnis_pfad}/extrakurse.students",
    # 3:lehrer.txt
    "$DevelConf::lehrer_datei",
   );


my %firstnames_data=();
my %firstnames_errors=();
my @encodings_to_check=("utf8","ISO_8859-1");
my %encoding_check_results=();
my @firstnames_not_found=();
my @firstnames_found=();
my @firstnames_found_errors=();

# ?????????????????? hash nicht richtig definiert
my %filename_map = ("$DevelConf::schueler_datei", "schueler.txt",
        "${DevelConf::config_pfad}/extraschueler.txt","extraschueler.txt",
        "$DevelConf::lehrer_datei","lehrer.txt",
        "${DevelConf::ergebnis_pfad}/extrakurse.students","extrakurse.students"
		    );


# =========================================================================
# lehrer.txt ordnen
# =========================================================================
&check_datei_touch("${DevelConf::users_pfad}/lehrer.txt");
&lehrer_ordnen();

# ===========================================================================
# Optionen verarbeiten
# ==========================================================================
$Conf::log_level=1;
my $help=0;
my $file_to_check="";
my $old_info_dir="";
my $no_kclass=0;
my $use_uid=0;
my $use_gid=0;
my $cron=0;
my $lock=0;
my $unlock=0;
my $filter_only=0;
my $analyze_encoding="";
my $no_auto_unids=0;
my $no_auto_teach_in=0;
my $encoding_students="";
my $encoding_students_extra="";
my $encoding_courses_extra="";
my $encoding_teachers="";

# Parsen der Optionen
my $testopt=GetOptions(
           "verbose|v+" => \$Conf::log_level,
           "help|h" => \$help,
           "get-info-from-old-files=s" => \$old_info_dir,
           "use-uid" => \$use_uid,
           "use-gid" => \$use_gid,
           "cron" => \$cron,
           "lock" => \$lock,
           "unlock" => \$unlock,
           "filter-only" => \$filter_only,
           "analyze-encoding=s" => \$analyze_encoding,
           "no-kclass" => \$no_kclass,
           "no-auto-unids" => \$no_auto_unids,
           "no-auto-teach-in|no-sync" => \$no_auto_teach_in,
           "file=s" => \$file_to_check,
           "encoding-students=s" => \$encoding_students,
           "encoding-students-extra=s" => \$encoding_students_extra,
           "encoding-courses-extra=s" => \$encoding_courses_extra,
           "encoding-teachers=s" => \$encoding_teachers,
          );

# Prüfen, ob Optionen erkannt wurden
&check_options($testopt);

# --help
if ($help==1) {
   # Befehlbeschreibung
   print "\n$scriptname examines and synchronises the following files\n",
         "with the system:\n";

   foreach my $datei (@schueler_to_check_dateien){
       print "   $datei\n";
   }
   print"\nWhere \n";
   print"   schueler.txt.tmp    is copied from   schueler.txt   \n";
   print"   lehrer.txt.tmp      is copied from   lehrer.txt \n";
   print"   extrakurse.students is copied from   extrakurse.txt \n";

   print"\nIf you want to filter schueler.txt before processing see the\n";
   print"explanation in /etc/sophomorix/user/sophomorix.conf\n";

   print('
Options
  -h  / --help
  -v  / --verbose
  -vv / --verbose --verbose
  --no-auto-teach-in / --no-sync
  --no-auto-unids
  --get-info-from-old-files directory
  --use-uid
  --use-gid
  --file filename
  --filter-only
  --analyze-encoding /path/to/schueler.txt
  --encoding-students ascii|8859-1|8859-15|win1252|utf8
  --encoding-students-extra ascii|8859-1|8859-15|win1252|utf8
  --encoding-courses-extra ascii|8859-1|8859-15|win1252|utf8
  --encoding-teachers ascii|8859-1|8859-15|win1252|utf8
  --lock / --unlock

Please see the sophomorix-check(8) man pages for full documentation
');
   print "\n";
   exit;
}


# ===========================================================================
# Heutiges Datum ermitteln
# ===========================================================================

# in Unix-Zeit
my $epoche_jetzt=0;

# Variablen für momentanes Datum und Uhrzeit
my $sec=0;
my $min=0;
my $hours=0;
my $tag=0;
my $monat=0;
my $jahr=0;
my $heute="";

## Epochenzeit in Sekunden seit 1.1.1970. 
$epoche_jetzt=time;

my $jetzt=localtime($epoche_jetzt);
$sec=$jetzt->sec;
$min=$jetzt->min;
$hours=$jetzt->hour;
$tag=$jetzt->mday;
$monat=$jetzt->mon+1;
$jahr=$jetzt->year+1900;

$heute=`date +%d.%m.%Y`;
chomp($heute);

&titel("Date: $heute     Time: $hours Hours $min Minutes $sec Seconds");


# --unlock
if ($unlock==1) {
    &unlock_sophomorix();
    exit;
}


# --lock
if ($lock==1) {
    &lock_sophomorix("lock",0,@arguments);
    exit;
}


# --cron
if ($cron==1){
    # cleanup 
    my $epoche_delete=$epoche_jetzt-86400*$DevelConf::log_pfad_delete_days;
    print "Cleaning up $DevelConf::log_pfad\n";
    print "   Epoc time now: $epoche_jetzt\n";
    print "   Delete before: $epoche_delete\n";

    opendir USERLOG, $DevelConf::log_pfad;
    foreach my $datei (readdir USERLOG){
        if ($datei eq "." or $datei eq ".."){
            next;
        }
        print "Checking $datei:\n";
        my ($date,$string)=split(/_/,$datei);
        my ($jahr,$monat,$tag)=split(/-/,$date);
        if (not defined $jahr or 
            not defined $monat or 
            not defined $tag) {
            print "   * Skipping $datei\n";
            next;
        }
        if (check_date($jahr,$monat,$tag)){
            print "   * $date is a correct date\n"; 
        } else {
            print "   * date is NOT a correct date\n"; 
            next;
        }

        my $datum_epoche=timelocal(0, 0, 0, $tag , ($monat-1), $jahr);
        print "   * $datum_epoche\n";
        if ($datum_epoche < $epoche_delete){
            print "   * DELETE $datei\n";
            my $abs_path=$DevelConf::log_pfad."/".$datei;
            system ("rm $abs_path");
        } else {
            print "   * KEEP $datei\n";
        }
    }
    close(USERLOG);

    # cleaning up print-data
    print "Cleaning up $DevelConf::druck_pfad\n";
    # removing /var/lib/sophomorix/print-data/$HOME directory
    my $path=$DevelConf::druck_pfad."/\\\$HOME";
    system("rm -rf $path");
    opendir PRINTDATA, $DevelConf::druck_pfad;
    foreach my $datei (readdir PRINTDATA){
        if ($datei eq "." or $datei eq ".."){
            next;
        }
        my $abs_path=$DevelConf::druck_pfad."/".$datei;
        print "* Removing $abs_path\n";
        system("rm '$abs_path'");
    }
    close(PRINTDATA);
    # continue with script
}



# --encoding-*-*
my %supported_encodings = qw(
    ascii    supported
    8859-1   supported
    8859-15  supported
    win1252  supported
    utf8     supported
);
if ($encoding_students ne ""){
    if (not exists $supported_encodings{$encoding_students}){
        print "\nERROR: Encoding $encoding_students is not supported!\n\n";
        exit;
    }
}
if ($encoding_students_extra ne ""){
    if (not exists $supported_encodings{$encoding_students_extra}){
        print "\nERROR: Encoding $encoding_students_extra ",
              "is not supported!\n\n";
        exit;
    }
}
if ($encoding_courses_extra ne ""){
    if (not exists $supported_encodings{$encoding_courses_extra}){
        print "\nERROR: Encoding $encoding_courses_extra ",
              "is not supported!\n\n";
        exit;
    }
}
if ($encoding_teachers ne ""){
    if (not exists $supported_encodings{$encoding_teachers}){
        print "\nERROR: Encoding $encoding_teachers is not supported!\n\n";
        exit;
    }
}


&log_script_start(@arguments);

if ($file_to_check ne "") {
    @schueler_to_check_dateien=("$file_to_check","","","","","","");
}

&check_connections();

# Die Einzelnen Dateien werden unter folgenden Variable benutzt
my $schuelerdatei="";

# Tolerieren und Deaktivieren
my $epoche_duldung_bis_schueler=0;
my $epoche_duldung_bis_lehrer=0;

my $epoche_deaktivierung_bis_schueler=0;
my $epoche_deaktivierung_bis_lehrer=0;

my $epoche_deaktivierung_ab=0;
my $epoche_loeschen_ab=0;


# Erlaubte Werte für das Geburtsdatum in folgenden Variablen ablegen
my %convert_days=();
my %convert_month=();
my %geburts_jahreszahl_erlaubt=();
my $geburts_jahreszahl_stop=0; 
# Laufvariable für Geburtsjahreszahl
my $geburts_jahreszahl_lauf=0;

# Die eingelesenen Felder
my $feld1="";
my $feld2="";
my $feld3="";
my $feld4="";
my $feld5="";

my $okfeld1="";
my $okfeld2="";
my $okfeld3="";
my $okfeld4="";

# Informationen aus sperrklassen.txt in eine Hash-Tabelle ablegen
my %sperrklassen=();

# Informationen aus entfernen.txt in eine Hash-Tabelle ablegen
my %entfern_klassen=();

my %sophomorix_status=();
my %user_duldungsbeginn=();
my %user_deaktivierungsbeginn=();

# Fehlersuche 
my $k="";
my $v="";
my @liste=();
# Zeile zerlegen
# Ganze Zeile
my $zeile_orig="";
# Die 4 Felder (+ 2 Extrafelder)
my $klasse="";
my $nachname="";
my $vorname="";
my $geburt="";
my $unid="";
my $field_five="";
my $usertoken="";
my $wunsch_login="---";
my $wunsch_passwort="---";
# Die 3 Unterfelder des Datums
my $geburts_tag="";
my $geburts_monat="";
my $geburts_jahr="";

# Zähler
my $zeilen_anzahl=0;
my $zeilen_anzahl_datei=0;
my $leere_zeilen_anzahl=0;
my $schueler_ok_anzahl=0;

# Schalter für fehlerhafte Daten
# Korrupter Datensatz
my $datensatz_korrupt=0;
my $datum_korrupt=0;
# Leere Felder
my $feld_klasse_leer=0;
my $feld_nachname_leer=0;
my $feld_vorname_leer=0;
my $feld_geburt_leer=0;
my $feld_geburt_chars_error=0;
# Unerlaubte Zeichen
my $klasse_unerlaubtes_zeichen=0;
my $nachname_unerlaubtes_zeichen=0;
my $vorname_unerlaubtes_zeichen=0;
my $datum_unrealistisch=0;
my $date_nonexistent=0;
my $schueler_sternchenklasse=0;

# Zähler für fehlerhafte Daten
my $strichpunkt_anzahl=0;
# my $punkte_anzahl=0;
my $datensatz_korrupt_anzahl=0;
my $datum_korrupt_anzahl=0;
my $feld_klasse_leer_anzahl=0;
my $feld_nachname_leer_anzahl=0;
my $feld_vorname_leer_anzahl=0;
my $feld_geburt_leer_anzahl=0;
my $feld_geburt_chars_error_anzahl=0;
my $klasse_unerlaubtes_zeichen_anzahl=0;
my $nachname_unerlaubtes_zeichen_anzahl=0;
my $vorname_unerlaubtes_zeichen_anzahl=0;
my $datum_unrealistisch_anzahl=0;
my $date_nonexistent_count=0;
my $schueler_gesperrt_anzahl=0;
my $schueler_entfernt_anzahl=0;
# 0 unbedingt lassen bei Sternchenklassen (SPLAN):
my $schueler_sternchenklasse_anzahl=0;

my $auto_teach_in_count=0;

my $move=0;

# Alles OK
#my $identifier_to_check="";
my $datensatz_ok=0;
my $datum_ok="";

# Zähler für korrekte Daten
my $weiterverarbeitet_anzahl=0;
my $ungesperrt_anzahl=0;

# Hash-Tabelle für Klasse-Schüleranzahl
my %klasse_schueleranzahl=();
my $klass="";
my $sch_zahl="";

my @auto_class_rename=();

my $name="";
my $passwort="";
my $loginname_passwd="";
my $passwort_passwd="";
my $uid_passwd="";
my $gid_passwd="";
my $quota_passwd="";
my $name_passwd="";
my $gcos_passwd="";
my $vorname_passwd="";
my $nachname_passwd="";
my $home_passwd="";
my $shell_passwd="";

#my $identifier_protokoll="";
#my $identifier_passwd="";
my $identifier_ok="";
my $identifier_schueler_ok="";

my %lehrer_im_system_loginname;

my %schueler_im_system_hash=();
my %schueler_im_system_loginname=();
my %schueler_im_system_protokoll_linie=();
my %schueler_ok_hash=();
my %unid_ok_file=();
my %schueler_ok_unid=();
my %schueler_ok_usertoken=();
my %unid_ok_system=();
my %schueler_ok_wunsch_login_hash=();
my %wunsch_login_seen=();
my %wunsch_login_forbidden=();
my %schueler_ok_wunsch_passwort_hash=();
my %schueler_im_system_exit_admin_class=();
my %schueler_im_system_account_type=();
my %identifier_sys_usertoken = ();
my %identifier_sc_tol = ();
 
my %identifier_origline=();

my $schueler_weg_anzahl=0;
my $schueler_hinzu_anzahl=0;
my $schueler_zuversetzen_anzahl=0;
my $schueler_duldungs_beginn_anzahl=0;

# Subklassen
my %schueler_subklassen=();

# Listen fuer report.admin
my @admin_list_toleration;

my @admin_list_corrupt;
my @admin_list_deactivation;
my @admin_list_kill;
my @admin_list_attic;
my @admin_list_add;
my @admin_list_move;


# hashes from old data
my %old_id_login=();
my %old_id_password=();
my %old_id_id=();
my %old_gname_gid=();
my %new_id_old_id=();


# Allgemein
my $key="";
my $value="";

# ===========================================================================
# Ende der Variablendeklaration
# ===========================================================================


# ===========================================================================
# Programmbeginn
# ===========================================================================

# ===========================================================================
# Existenz wichtiger Dateien prüfen
# ===========================================================================
&check_datei_touch("${DevelConf::users_pfad}/schueler.txt");
&check_config_template("entfernen.txt");
&check_config_template("sperrklassen.txt");
&check_config_template("extrakurse.txt");
&check_config_template("extraschueler.txt");
&check_config_template("class.map");

# ===========================================================================
# Verzeichnisse und Dateien anlegen
# Alle vorher erzeugten Ergebnis-dateien löschen
# ===========================================================================

&check_verzeichnis_mkdir("${DevelConf::ergebnis_pfad}");

# remove check-result
system("rm -r ${DevelConf::ergebnis_pfad}");
mkdir("${DevelConf::ergebnis_pfad}",0700) 
       || die "Kann Verzeichnis ${DevelConf::ergebnis_pfad} nicht anlegen: $!"; 

# ===========================================================================
# TolerationDate und DeactivationDate ermitteln
# ===========================================================================

# Tolerierung bis zu welcher Epochenzeit
$epoche_duldung_bis_schueler=
    $epoche_jetzt+86400*$Conf::schueler_duldung_tage;
$epoche_duldung_bis_lehrer=
    $epoche_jetzt+86400*$Conf::lehrer_duldung_tage;
# Deaktivierung bis zu welcher Epochenzeit
$epoche_deaktivierung_bis_schueler=
    $epoche_jetzt+86400*$Conf::schueler_deaktivierung_tage;
$epoche_deaktivierung_bis_lehrer=
    $epoche_jetzt+86400*$Conf::lehrer_deaktivierung_tage;

if($Conf::log_level>=3){
   print "Epochenzeit:                                 ",
         "$epoche_jetzt (right now)\n";
   print "Toleration(T) for Pupils(Schueler) ends:     ",
         "$epoche_duldung_bis_schueler ",
   &zeit($epoche_duldung_bis_schueler),
         " (in $Conf::schueler_duldung_tage Days)\n";
   print "Toleration(T) for Teachers(Lehrer) ends:     ",
         "$epoche_duldung_bis_lehrer ",
   &zeit($epoche_duldung_bis_lehrer),
         " (in $Conf::lehrer_duldung_tage Days)\n";
   print "Deactivation(D) for Pupils(Schueler) ends:   ",
         "$epoche_deaktivierung_bis_schueler ",
   &zeit($epoche_deaktivierung_bis_schueler),
         " (in $Conf::schueler_deaktivierung_tage Days)\n";
   print "Deactivation(D) for Teachers(Lehrer) ends:   ",
         "$epoche_deaktivierung_bis_lehrer ",
   &zeit($epoche_deaktivierung_bis_lehrer),
         " (in $Conf::lehrer_deaktivierung_tage Days)\n";
}

# ===========================================================================
# create hash of valid years
# ===========================================================================
&year_ok();


# --analyze-encoding
if ($analyze_encoding ne ""){
    if($Conf::log_level>=3){
        print "\n";
        print "Analyzing encoding\n";
    }
    &reading_firstnames();
    &reading_firstname_errors();
    &analyze_encoding($analyze_encoding);
    &log_script_exit("",1,1,0,@arguments);
}


# remove tmp
system("rm -r ${DevelConf::pruef_pfad}");
if (not -e ${DevelConf::pruef_pfad}){
    mkdir("${DevelConf::pruef_pfad}",0700) 
           || die "Kann Verzeichnis ${DevelConf::pruef_pfad} nicht anlegen: $!"; 
}
&check_verzeichnis_mkdir("${DevelConf::var_lib_pfad}");
&check_verzeichnis_mkdir("${DevelConf::pruef_pfad}");
&check_verzeichnis_mkdir("${DevelConf::ergebnis_pfad}");
&check_verzeichnis_mkdir("${DevelConf::druck_pfad}");


# copy the schueler.txt to location where it is checked, or
# call the filter script
my $filter_return=12;
if ($Conf::filter_script eq ""){
    &titel("No filter script defined, copying schueler.txt ...");
    system("cp ${DevelConf::users_pfad}/schueler.txt ${DevelConf::schueler_datei}");
} else {
    if (-x $Conf::filter_script){
        &titel("Running filter script $Conf::filter_script ");
        $filter_return = system("$Conf::filter_script");
        if (not $filter_return==0){
            print "\n\n$scriptname: Filter script\n",
                  "    ${Conf::filter_script}\n",
                  "ended with errors($filter_return)\n\n";
            print "$scriptname: I do not continue!\n\n";
            &log_script_exit("",101,1,0,@arguments);
        }
    } else {
        &log_script_exit("",100,1,0,@arguments);
    }
}



# --filter-only
if ($filter_only==1){
    print "\n$scriptname ended by option --filter-only\n\n";
    &log_script_exit("",1,1,0,@arguments);
}






# replace the configured name for teacher with teacher
&rename_teacher_groupname();

# ===========================================================================
# Datei extrakurse.students erzeugen aus extrakurse.txt 
# ===========================================================================
&extra_kurs_schueler($tag, $monat, $jahr);

# ===========================================================================
# Open the output-files
# ===========================================================================
&open_report_files();

# ===========================================================================
# Get a hash of classes from schueler.txt
# ===========================================================================
my %schueler_classes = &get_schueler_classes();

# ===========================================================================
# read class.map
# ===========================================================================
my %class_map = &get_class_map();

while (($k,$v) = each %class_map){
    print $k , " ", $v, "\n";
}


# ===========================================================================
# Get information from the System
# ===========================================================================
&titel("Asking the system for users ...");

my ($ref_login, 
    $ref_adminclass,
    $ref_status,
    $ref_subclass,
    $ref_toleration_date,
    $ref_deactivation_date,
    $ref_unid_identifier,
    $ref_exit_adminclass,
    $ref_account_type,
    $ref_usertoken,
    $ref_scheduled_toleration,
   ) = &get_sys_users();

# identifier - klasse (ohne k = sophomorixAdminClass)
%schueler_im_system_hash = %$ref_adminclass;

# identifier - loginname (uid in ldap)
%schueler_im_system_loginname = %$ref_login;

# identifier - sophomorixStatus
%sophomorix_status = %$ref_status;

# identifier - sophomorixSubClass
%schueler_subklassen = %$ref_subclass;

# identifier - sophomorixTolerationDate
%user_duldungsbeginn = %$ref_toleration_date;

# identifier - sophomorixDeactivationDate
%user_deaktivierungsbeginn = %$ref_deactivation_date;

# unid - sophomorixIdentifier
%unid_ok_system = %$ref_unid_identifier;

# identifier - ExitAdminClass
%schueler_im_system_exit_admin_class = %$ref_exit_adminclass;

# identifier - AccountType 
%schueler_im_system_account_type = %$ref_account_type;

# identifier - usertoken 
%identifier_sys_usertoken = %$ref_usertoken;

# identifier - usertoken 
%identifier_sc_tol = %$ref_scheduled_toleration;



#exit;


# ===========================================================================
# Get information from old files
# ===========================================================================
if ($old_info_dir ne ""){
   &titel("Extracting information from old files ...");
   my ($ref_old_id_login, 
       $ref_old_id_password,
       $ref_old_id_id,
       $ref_old_group_gid,
       $ref_new_id_old_id
      ) = &get_old_info($old_info_dir, $no_kclass);
   # old files: identifier - old_login 
   %old_id_login = %$ref_old_id_login;
   # old files: identifier - old_login 
   %old_id_password = %$ref_old_id_password;
   # old files: identifier - old_login 
   %old_id_id = %$ref_old_id_id;
   # old files: groupname - old_id 
   %old_gname_gid = %$ref_old_group_gid;
   # old files: groupname - old_id 
   %new_id_old_id = %$ref_new_id_old_id;

   #print "\n\nDas ist der old-id-login hash\n";
   #while (($k,$v) = each %old_id_login){
   #    print $k , " ", $v, "\n";
   #}
}

# ===========================================================================
# Einlesen der Entfern-Klassen in eine Hash-Tabelle
# ===========================================================================
%entfern_klassen = &get_entfern_klassen();

# ===========================================================================
# Einlesen der Sperr-Klassen in eine Hash-Tabelle
# ===========================================================================
%sperrklassen = &get_sperrklassen(); 


# ===========================================================================
# Einlesen der Schülerdateien
# ===========================================================================
$zeilen_anzahl=0; # Zähler erstreckt sich über alle Dateien
foreach $schuelerdatei (@schueler_to_check_dateien){
   # encoding wählen

   my $encoding;

   if ($schuelerdatei eq $schueler_to_check_dateien[0]){
       # schueler
       if ($encoding_students ne ""){
           # option wins
           $encoding=$encoding_students;
       } elsif (defined $Conf::encoding_students){
           # sophomorix.conf wins
           $encoding=$Conf::encoding_students;
       } elsif (defined $DevelConf::encoding_students){
           # sophomorix-devel.conf wins
           $encoding=$DevelConf::encoding_students;
       }
   } elsif ($schuelerdatei eq $schueler_to_check_dateien[1]){
       # extraschueler
       if ($encoding_students_extra ne ""){
           # option wins
           $encoding=$encoding_students_extra;
       } elsif (defined $Conf::encoding_students_extra){
           # sophomorix.conf wins
           $encoding=$Conf::encoding_students_extra;
       } elsif (defined $DevelConf::encoding_students_extra){
           # sophomorix-devel.conf wins
           $encoding=$DevelConf::encoding_students_extra;
       }
   } elsif ($schuelerdatei eq $schueler_to_check_dateien[2]){
       # extrakurse
       if ($encoding_courses_extra ne ""){
           # option wins
           $encoding=$encoding_courses_extra;
       } elsif (defined $Conf::encoding_courses_extra){
           # sophomorix.conf wins
           $encoding=$Conf::encoding_courses_extra;
       } elsif (defined $DevelConf::encoding_courses_extra){
           # sophomorix-devel.conf wins
           $encoding=$DevelConf::encoding_courses_extra;
       }
   } elsif ($schuelerdatei eq $schueler_to_check_dateien[3]){
       # lehrer
       if ($encoding_teachers ne ""){
           # option wins
           $encoding=$encoding_teachers;
       } elsif (defined $Conf::encoding_teachers){
           # sophomorix.conf wins
           $encoding=$Conf::encoding_teachers;
       } elsif (defined $DevelConf::encoding_teachers){
           # sophomorix-devel.conf wins
           $encoding=$DevelConf::encoding_teachers;
       }
   }

   # Abbruch, wenn Dateiname leer (option --file)
   if ($schuelerdatei eq "") {
      next;
   }
   $zeilen_anzahl_datei=0; # Zähler innerhalb der Dateien
   # wunsch-login leeren, da er nicht von jedem Datensatz überschrieben wird
   $wunsch_login="---";
   $wunsch_passwort="---";
   &titel("Open ${schuelerdatei} ($encoding)");

   print REPORTSEKRETARIAT  "\n\n#####################################", 
                            "#########################################\n";
   printf REPORTSEKRETARIAT "######## %-60s ########\n",$schuelerdatei;
   printf REPORTSEKRETARIAT "######## Encoding: %-50s ########\n",$encoding;
   print REPORTSEKRETARIAT  "#######################################",
                            "#######################################\n\n";

   # exit if filterscript does not create schueler.txt.tmp
   if (not -e ${DevelConf::schueler_datei} and $Conf::filter_script ne ""){
       &log_script_exit("ERROR: Filterscript $Conf::filter_script doesnt create ${schuelerdatei}",102,1,0,@arguments);
   }

   open(USERDATEI, "${schuelerdatei}") || 
             die "Fehler: $! ${schuelerdatei} nicht gefunden!"; 

    while(<USERDATEI>){
       $wunsch_login="";
       $unid="";
       $usertoken="";
       # Zeile abspeichern in $zeile_orig (für Fehlerreport)
       chomp($_); # Returnzeichen abschneiden
       $zeile_orig=$_;
       $zeilen_anzahl++;
       $zeilen_anzahl_datei++;

       # wenn keine Kommentarzeile
       if($Conf::log_level>=3){
          print("\n","##### Reading new line:","  $zeile_orig","\n");
       }

       # ====================================================================
       # Alle Fehlervariablen auf null stellen (0=kein Fehler, 1=Fehler)
       $datensatz_korrupt=0;
       $datum_korrupt=0;

       $feld_klasse_leer=0;
       $feld_nachname_leer=0;
       $feld_vorname_leer=0;
       $feld_geburt_leer=0;
       $feld_geburt_chars_error=0;

       $klasse_unerlaubtes_zeichen=0;
       $nachname_unerlaubtes_zeichen=0;
       $vorname_unerlaubtes_zeichen=0;
       $datum_unrealistisch=0;
       $date_nonexistent=0;

       $schueler_sternchenklasse=0;

       # =====================================================================
       # Gesamze Zeile modifizieren:
       # =====================================================================
       # whitespace entfernen (mit nichts ersetzen) \s wäre [\r\t\n\f], 
       # Hier: ohne Return zu entfernen
       s/[\r\t\f]//g;
       # Leerzeichen ersetzen
       s/ //g;

       # Sonderzeichen umwandeln
       $_ = &filter_school_admin_software($_,$encoding);

       # ????? custom-filter aus Datei, in der netzadmin was eintraegt

       # =====================================================================
       # SYNTAX-PRÜFUNGEN, Teil 1
       # =====================================================================
       # Ist Datensatz überhaupt aufspaltbar/brauchbar?)

       # Bei Kommentarzeilen abbrechen
       if ( (/^\#/) && ($schuelerdatei ne $schueler_to_check_dateien[0]) ) {
            # Kommentare zulassen, außer in der ersten Datei 
            # (= Datei des Schulverwaltungsprogramms)
            if($Conf::log_level>=3){
               print "Kommentarzeile in $schuelerdatei\n";
	    }
            # Kommentarzeilen WERDEN NICHT mitgezählt 
            # ($zeilen_anzahl)-> abziehen
            # Kommentarzeilen WERDEN mitgezählt 
            # ($zeilen_anzahl_datei)-> nix tun
            # Gesamt-Zeilenzähler wurde schon erhöht, nun zurücksetzen 
            $zeilen_anzahl--;
            next; # Abbruch
       }

       # Strichpunktanzahl checken
       $strichpunkt_anzahl=tr/;//;

       if ( &semicolon_number_ok($strichpunkt_anzahl, $schuelerdatei) == 0 ) {
	   next;
       }

       # Zerteilen der Zeile in 4 bis 6 Elemente, 
       # diese in Variablen abspeichern
       ($klasse,$nachname,$vorname,$geburt,
        $field_five,$wunsch_passwort,$usertoken)=split(/;/);

       # A-Z in Kleinbuchstaben umwandeln
       $klasse=~tr/A-Z/a-z/; # in Kleinbuchstaben umwandeln
 
       # Decide, what to do with field_five
       if($field_five eq ""){
	   $wunsch_login= "---";
           $unid = "";
       } elsif ($field_five ne "" 
         && $schuelerdatei eq $schueler_to_check_dateien[0] ){
           $unid = $field_five;
	   $wunsch_login = "---";
       } else {
           $unid = "";
           $field_five=~tr/A-Z/a-z/; # in Kleinbuchstaben umwandeln
           $wunsch_login = $field_five;
       }

       if (not defined $wunsch_passwort){
          # $wunsch_passwort Kann evtl nicht mehr definiert sein
          $wunsch_passwort="---"
       } elsif($wunsch_passwort eq "" or $wunsch_passwort eq "erstpw"){
          # oder leer oder alt: erstpw
          $wunsch_passwort="---"
       };

       if (not defined $usertoken){
          $usertoken=""
       }

       # Datum pruefen
       ($geburts_tag, 
        $geburts_monat, 
        $geburts_jahr) = &birthdate_usable($geburt);

       if ($geburts_tag eq "0" ) {
          # return = 0 -> Fehler
	  next;
       } 


       if($Conf::log_level>=3){	
          print "Extracted data: ", $klasse," ", 
                                    $nachname," ", 
                                    $vorname," ",
                                    $geburts_tag," ", 
                                    $geburts_monat," ", 
                                    $geburts_jahr," ",
                                    $unid, 
                                    "\n";
       }

       # Klassenbezeichnung in Kleinbuchstaben umwandeln
       $klasse=~tr/A-Z/a-z/; 

       # Falls Sternchenklassen ausgefiltert werden sollen:
       if ($Conf::splan_sternchenklassen_filtern eq "yes") {
         if($klasse=~/^\*/){ # Bei * am Beginn des Klassennamens aussteigen
                 # Splan-Sternchenklassen ausfiltern
                 if($Conf::log_level>=2){	
  	            print "##### $vorname"." "."$nachname", 
                          " wird NICHT überprüft (Splan-Sternchenklasse)\n\n";
	         }
                 # Datensatz in report.splan schreiben
	         print REPORTSPLAN "$_\n";
                 $schueler_sternchenklasse=1;
                 $schueler_sternchenklasse_anzahl++;
	         next;
	 }
       }
       # Falls unterstrichklassen ausgefiltert werden sollen:
       if (defined $Conf::underscore_klassen_filtern) {
       if ($Conf::underscore_klassen_filtern eq "yes") {
         if($klasse=~/^\_/){ # Bei * am Beginn des Klassennamens aussteigen
                 # Underscoreklassen ausfiltern
                 if($Conf::log_level>=2){	
  	            print "##### $vorname"." "."$nachname", 
                          " wird NICHT überprüft (Underscore-Klasse)\n\n";
	         }
                 # Datensatz in report.splan schreiben
	         #print REPORTSPLAN "$_\n";
                 #$schueler_sternchenklasse=1;
                 #$schueler_sternchenklasse_anzahl++;
	         next;
	 }
       }
       }
    
       # =====================================================================
       # SYNTAX-PRÜFUNGEN, Teil 2 (Haben sich kleinere Fehler eingeschlichen)
       # Wenn ein Fehler vorkommt, dann entsprechende Variable auf 1 setzen
       # =====================================================================


       # / durch das ersetzen was in der Variable 
       # $DevelConf::ersetze_slash_mit steht (editieren in sophomorix.conf)
       $klasse=~s/\//$DevelConf::ersetze_slash_mit/g;

       # The unid is NOT syntax-checked
       ($klasse,
        $nachname, 
        $vorname,
        $geburts_tag, 
        $geburts_monat, 
        $geburts_jahr,
        $geburt,
        $wunsch_login,
       ) = &syntax_check_entry($klasse,
                               $nachname, 
                               $vorname,
                               $geburts_tag, 
                               $geburts_monat, 
                               $geburts_jahr,
                               $geburt,
                               $wunsch_login,
                               $schuelerdatei
                              );

       # =====================================================================
       # Einsortieren des Datensatzes --- NUR 1X EINSORTIEREN
       # =====================================================================
       # Alles OK(Fehlervariablen alle 0)     
       #                  --> sophomorix.ok
       #                      variable $schueler_ok_anzahl um eins erhöhen
       # Syntaxfehler     
       #                  --> report.office
       #                      Fehlerangabe
       # Prüfen, ob ein Sekretariats-Fehler aufgetreten ist
       if($feld_klasse_leer
         +$feld_nachname_leer
         +$feld_vorname_leer
         +$feld_geburt_leer
         +$feld_geburt_chars_error
         +$klasse_unerlaubtes_zeichen
         +$nachname_unerlaubtes_zeichen
         +$vorname_unerlaubtes_zeichen
         +$datum_unrealistisch
         +$date_nonexistent
         +$vorname_unerlaubtes_zeichen
         +$datensatz_korrupt
         +$datum_korrupt
         ==0){
            # ============================================================
            # Korrekter Datensatz
            # ============================================================
            if($Conf::log_level>=3){ 
               print "Line is OK\n";
            }
            # Datensatz zusammensetzen
            $datum_ok = join(".",($geburts_tag,$geburts_monat,$geburts_jahr));
            $datensatz_ok = join(";",($klasse,$nachname,$vorname,$datum_ok));
            $datensatz_ok = $datensatz_ok.";";
            if ($unid ne "" and (defined $unid) ) {
               $datensatz_ok = $datensatz_ok.$unid.";";
	    }
            # identifier zusammensetzen
            # identifier_ok 0 aus korrigierte schueler.txt
            $identifier_ok = join(";", ($nachname,$vorname,$datum_ok));
            if($Conf::log_level>=3){
               print "Identifier is:  ", "$identifier_ok", "\n";
            }
            if($Conf::log_level>=3){
               print "Using UNID  :  $unid", "\n";
            }

            $klasse=~tr/A-Z/a-z/; # Klasse in Kleinbuchstaben umwandeln

            # ==============================================================
            # Schueler in Sperrklassen ausfiltern
            if ( &is_in_inhibit_class($klasse)==1) {
                next;
            }

            # ==============================================================
            # Schueler in Entfernen-Klasse  ausfiltern
            if ( &is_in_remove_class($klasse)==1) {
	        next;
            }

            # Subklassen bilden
            if (exists $schueler_subklassen{$identifier_ok}) {
              # Subklasse anhängen
            }

            # Ausgabe des Ergebnisses
            if($Conf::log_level>=3){
                print "### Result:\n";
            }

            # fill a hash
            $identifier_origline{$datensatz_ok}="$zeile_orig";

            if($Conf::log_level>=2){
                #printf  "%-10s %-60s","Eingelesen:","$zeile_orig\n";
	        if ($schuelerdatei eq $schueler_to_check_dateien[3]) {
                   # Lehrerdatei
                   # Leerzeichen entfernen
                   $zeile_orig=~s/ //g;
                }
                print "Eingelesener Datensatz:","  $zeile_orig","\n";
                print "Korrigierter Datensatz:  $datensatz_ok","\n\n";
                
            }

            # ============================================================
            # Kein Fehler aufgetreten, also in sophomorix.ok schreiben:
            # ============================================================
            $schueler_ok_anzahl++;

            print SOPHOMORIXOK "$datensatz_ok\n"; # neu zusammengesetzt
            # In sophomorix.ok sind nun die korrigierten Datensätze 
            # aus schueler.txt, ....   
            # Korrekter Datensatz in Hashtabelle %schueler_ok_hash speichern
            if ( &add_user_to_queue($datensatz_ok,
                                    $wunsch_login,
                                    $wunsch_passwort,
                                    $schuelerdatei) ==0 ){
                next;
            }

       } else {
            # ============================================================
            # Fehler aufgetreten, also in report.office schreiben:
            # ============================================================
	    &report_error();
       }
   } # Nächste Zeile bearbeiten
   close(USERDATEI);
} # Ende foreach, Naechste Schuelerdatei bearbeiten


# ===========================================================================
# Closing some files
# ===========================================================================
close(SOPHOMORIXOK);


&generate_class_user_count_file();


close(REPORTSEKRETARIAT);
close(REPORTGESPERRT);

# Falls Sternchenklassen ausgefiltert werden sollen, Datei schließen
if ($Conf::splan_sternchenklassen_filtern eq "yes") {
   close(REPORTSPLAN);
}

#&titel("Die Datei sophomorix.ok liegt nun vor");

#my $date= &get_action_date("beck;ruediger;04.09.1972","kill");


# ===========================================================================
# Ausgabe von Hashes vor dem updaten der Systemdatenbank
# ===========================================================================

&show_all_hashes();

#exit;

# ===========================================================================
# Load UNIDS into the Database and System
# ===========================================================================
if ($no_auto_unids==1){
  # no auto-teach-in
} else {
  &update_unids();
}

# ===========================================================================
# Load DB_PUSHDATA (usertoken, ...) into the Database and System
# ===========================================================================
&update_db_pushdata();


# ===========================================================================
# Remove UNIDS from the Database and System
# ===========================================================================
&remove_unids();



# ===========================================================================
# Load new data into the Database and System by use of AI
# ===========================================================================
if ($no_auto_teach_in==1){
  # no auto-teach-in
} else {
  &auto_teach_in();
}

my %go_come=();
my %come_points=();




# ===========================================================================
# Updating Data in the user-Database, create sophomorix.add .move .kill
# ===========================================================================

# versetzen
&u_u_check();

# A) U,E --->>> T
#================================================== 
### 1. Usable(U,scheduled_toleration) -> Tolerated(T)  .move (old class -> attic)
###&ust_t_check();
# 1. Usable(U) -> Tolerated(T)  .move (old class -> attic)
&u_t_check();
# 2. Enabled(E) -> Tolerated(T) .move (old class -> attic)
&e_t_check();


# B) T, A, S --->>> E,U
#==================================================
# 1. Tolerated(T) -> Enabled(E) .move (attic -> new class)
&t_e_check();
# 2. Activated(A) -> Usable(U)  .move (aktive klasse -> neue klasse)
&a_u_check();
# 3. Selfactivated(S) -> Usable(U)  .move (aktive klasse -> neue klasse)
&s_u_check();


# C) T, A, S -> D
#==================================================
# 1. Tolerated(T) -> Disabled(D)   .move (old class -> attic)
&t_d_check();
# 2. Activated(A) ->   -> Disabled(D)   .move (old class -> attic)
&a_d_check();
# 3. Selfactivated(S) ->   -> Disabled(D)  .move (old class -> attic)
&s_d_check();


# D) D to E
#==================================================
# 1. Disabled(D) -> E(Enable)  .kill .move (old class  -> attic)
&d_e_check();

# 2. Disabled(D) -> R(Removeable) .kill .move (old class -> attic)
&d_r_check();


# E) R
#==================================================
# 1. Removeable(R) und Killable(K) bleibt   .kill
&r_r_check();

# F) scheduled_toleration
#==================================================
# 1. Usable(U,scheduled_toleration) -> Tolerated(T)  .move (old class -> attic)
&ust_t_check();


&show_all_hashes();


# ===========================================================================
# Datei erzeugen, in die die neuen User geschrieben werden
# ===========================================================================
&generate_add_file();


# ===========================================================================
# Empfehlung in Admin-Report und Konsole zum umbenennen von Nummern-Klassen
# ===========================================================================
if (defined $auto_class_rename[0]){
    print RED "Sie sollten folgende Klassen in /etc/sophomorix/user/class.map\n";
    print RED "umbenennen, da sie nur Ziffern enthalten:\n";
    print RED "  @auto_class_rename\n";
}

# ===========================================================================
# Empfehlung in Admin-Report und Konsole zum Teach-in
# ===========================================================================
if($schueler_hinzu_anzahl!=0 
      and ($schueler_duldungs_beginn_anzahl!=0 
      or $schueler_weg_anzahl!=0)){
    print RED "Sie sollten sophomorix-teach-in aufrufen!\n";
}


# ===========================================================================
# Alle daten in report.admin ausgeben
# ===========================================================================
&generate_admin_report();


# ===========================================================================
# Alle Dateien admin zugänglich machen
# ===========================================================================

# Nur zum ausgeben  
#while (($key,$value) = each %geburts_jahreszahl_erlaubt){
#   printf "%-40s %3s\n","$key","-$value-";
#}

# ===========================================================================
# Ende des Scripts
# ===========================================================================
&log_script_end(@arguments);

# ===========================================================================
# Subroutinen
# ===========================================================================

# ===========================================================================
# status check subs
# ===========================================================================

# subs for status check subs
sub append_move_entry {
    # appends a line to sophomorix.move
    my ($identifier, $old_class, $new_class,$old_status) = @_;
    if (not defined $old_status){$old_status=""};
    my $login=$schueler_im_system_loginname{$identifier};
    my $move_line = ();
    my $move_message = "";
    $move_line="$login"."::".$old_class."::"."$new_class"."::"."$old_status\n";
    $move_message="   $identifier (Login: $login)  $old_class ".
                  "---> $new_class ($old_status)\n";

    #print REPORTADMIN "$move_message";
    print "$move_message";
    open(SOPHOMORIXMOVE, 
       ">>${DevelConf::ergebnis_pfad}/sophomorix.move") 
        || die "Fehler: $!";
    print SOPHOMORIXMOVE "$move_line";
    close(SOPHOMORIXMOVE);
    $move++;
}



sub append_kill_entry {
    # appends a line to sophomorix.kill
    my ($identifier)=@_;
    my $login=$schueler_im_system_loginname{$identifier};
    my $admin_class=$schueler_im_system_hash{$identifier};
    my $kill_line="$identifier"."::"."$login"."::"."$admin_class"."\n";
    my $kill_message="   $identifier (Login: $login)".
                     " ---> Removable/Killable\n";

    #print REPORTADMIN "$kill_message";
    open(SOPHOMORIXKILL, 
       ">>${DevelConf::ergebnis_pfad}/sophomorix.kill") 
        || die "Fehler: $!";
    #print REPORTADMIN "$kill_message";
    print "$kill_message";
    print SOPHOMORIXKILL "$kill_line";
    close(SOPHOMORIXKILL);
}



sub u_u_check {
   my $count=0;
   &titel("u_u_check: Looking for users to be moved ...");
    while ( my ($identifier, $new_class) = each %schueler_ok_hash ){
      if (exists $schueler_im_system_hash{$identifier}) {
         # schueler, die im System sind 
         # und in sophomorix.ok weitervergleichen
	 my $old_class=$schueler_im_system_hash{$identifier};
	 my $old_status=$sophomorix_status{$identifier};
	 my $login=$schueler_im_system_loginname{$identifier};
         if ($new_class=~/[^0-9]/){
             # adminclass is string, nothing to do
         } else {
	     $new_class=&rename_admin_class($new_class);
         }
         if ("$new_class" ne "$old_class"){
           $schueler_zuversetzen_anzahl++;
           $count++;
           # Admin-Report
           push @admin_list_move, "$login  $old_class ---> $new_class\n";
           # File
           &append_move_entry($identifier,
                              $old_class,
                              $new_class,
                              $old_status);

         }
      }
   }
   &titel("u_u_check: $count users can be moved ...");
}



sub ust_t_check {
   my $count=0;
   &titel("ust_t_check: Looking for scheduled_toleration Users -> attic ...");
   # Usable(U,scheduled toleration) -> Tolerated(T)  .move (old class -> attic) 
   while ( my ($identifier, $sc_tol) = each %identifier_sc_tol ){
       # next if user is tolerated already
       if ($sophomorix_status{$identifier} ne "A"
           and $sophomorix_status{$identifier} ne "U"
           and $sophomorix_status{$identifier} ne "E"
          ){
           next;
       }
       print "   checking user $identifier with scheduled_toleration in $sc_tol \n";
       my ($year,$month,$day)=split(/-/,$sc_tol);
       my $sc_tol_perl=&date_pg2perl($sc_tol);
       $month=$month-1;
       $year=$year-1900;

       my $epoche_scheduled=timelocal(0,0,0,$day,$month,$year);
       print "     Scheduled:   $epoche_scheduled ($sc_tol_perl) \n";
       print "     Today:       $epoche_jetzt ($heute) \n";
       if ($epoche_scheduled<$epoche_jetzt){
              print "     INFO: $identifier nur noch toleriert\n";
              my $login=$schueler_im_system_loginname{$identifier};
              &update_user_db_entry($login, 
                                   "Status=T",
                                    "TolerationDate=$heute");
              # comment line in lehrer.txt
              my $origline=$identifier_origline{"teachers;${identifier};"}."\n";
              $origline=~s/^teachers/lehrer/;
              open(TEACHER,"<${DevelConf::users_pfad}/lehrer.txt");
              my $tmp_file=${DevelConf::pruef_pfad}."/lehrer.tmp";
              open(TEACHERTMP,">$tmp_file");
              while(<TEACHER>){
                  if ($_ ne $origline){
                      print TEACHERTMP "$_";
                  } else {
                      print "     commenting in lehrer.txt line of $identifier\n";
		      print TEACHERTMP "# scheduled_toleration # $_";
                  }
              }
              close(TEACHER);
              close(TEACHERTMP);
              system("cp $tmp_file ${DevelConf::users_pfad}/lehrer.txt");

              # report.admin
              push @admin_list_toleration, 
                    "$identifier wird ab $heute nur noch toleriert\n";
              &append_move_entry($identifier,
                                $schueler_im_system_hash{$identifier},
                                "attic",
                                $sophomorix_status{$identifier});
              
              # Counter
              $count++;

	  } else {
              print "     $identifier: nothing to do\n";
          }
   }
   &titel("ust_t_check:  ... $count scheduled_toleration Users are tolerated");
}

sub u_t_check {
   my $count=0;
   &titel("u_t_check: Looking for users to be tolerated/-> attic ...");
   # Usable(U) -> Tolerated(T)  .move (old class -> attic) 
   # Step 1: Tolerate the user
   while ( my ($identifier, $status) = each %sophomorix_status ){
     if ($status eq "U" or $status eq "E" or $status eq ""){
         unless (exists $schueler_ok_hash{$identifier}) {
           # User is not anymore in Files
           if (not exists $user_duldungsbeginn{$identifier}){
              print "INFO: $identifier wird ab $heute nur noch toleriert\n";
              my $login=$schueler_im_system_loginname{$identifier};
              &update_user_db_entry($login, 
                                   "Status=T",
                                    "TolerationDate=$heute");
              # report.admin
              push @admin_list_toleration, 
                    "$identifier wird ab $heute nur noch toleriert\n";
              &append_move_entry($identifier,
                                $schueler_im_system_hash{$identifier},
                                "attic",
                                $status);
              # Counter
              $count++;
              next;
           }
         }
     }
   }
   &titel("u_t_check:  ... $count users were tolerated/-> attic");
}



sub e_t_check {
   # Enabled(E) -> Tolerated(T) .move (alte klasse -> attic)
   # wird mit u_t gemacht??? 
}



sub t_e_check {
   # doppelt mit d_e_check
}



sub a_u_check {
   # Activated(A) -> Usable(U)  .move (aktive klasse -> neue klasse)
   my $count=0;
   &titel("a_u_check: Looking for activated users to enable ...");
   while ( my ($identifier, $status) = each %sophomorix_status ){
      if ($status eq "A"){
         my $login=$schueler_im_system_loginname{$identifier};
         my $new=$schueler_im_system_exit_admin_class{$identifier};
         my $old=$schueler_im_system_hash{$identifier};
         if (not defined $new){
             return;
         }
         if ($new=~/[^0-9]/ or $new eq ""){
             # new adminclass is string or empty, nothing to do
         } else {
	     $new=&rename_admin_class($new);
         }
         if ($old ne $new and $new ne ""){
            # do not move in empty class
            &append_move_entry($identifier,$old,$new,"A");
	    $count++;
         }
      }
   }
   &titel("a_u_check:  ... $count activated users were enabled");
}



sub s_u_check {
   # Selfactivated(S) -> Usable(U)  .move (aktive klasse -> neue klasse)
}



sub t_d_check {
   my $count_move=0;
   my $count_disabled=0;
   &titel("t_d_check: Looking for tolerated users to be moved/deactivated ...");
   # Tolerated(T) -> Disabled(D)   .move (alte klasse -> attic)
   while ( my ($identifier, $status) = each %sophomorix_status ){
      # wenn status T und NICHT wieder auftaucht
      if ($status eq "T" and not exists $schueler_ok_hash{$identifier}){
         my $login=$schueler_im_system_loginname{$identifier};
         my $sys_class=$schueler_im_system_hash{$identifier};
         # check if tolerated user is not in attic
         if ($sys_class ne "attic") {
            &append_move_entry($identifier,$sys_class,"attic",$status);
            $count_move++;
         }
         # checking for deactivation
         if (not exists $user_deaktivierungsbeginn{$identifier}){
            $epoche_deaktivierung_ab = &get_action_date($identifier, 
                                                         "deactivation");
            if ($epoche_deaktivierung_ab != 0) {
               print "   $identifier wurde am $heute deaktiviert\n";
               push @admin_list_deactivation, 
                   "$identifier wurde am $heute deaktiviert\n";
               # Alle zum deaktivieren notwendigen Schritte durchführen
               &update_user_db_entry($login, 
                                     "Status=D", 
                                     "DeactivationDate=$heute");
               &user_deaktivieren($login);
               $count_disabled++;
            } else {
               if($Conf::log_level>=2){
                  print "### User $identifier ist im Tolerierungs-Zeitraum\n"; 
               }
	    }

	 }
      }
   }
   &titel("t_d_check:  ... $count_move tolerated users -> attic");
   &titel("t_d_check:  ... $count_disabled  users were disabled");
}



sub a_d_check {
   # Activated(A) ->   -> Disabled(D)   .move (alte klasse -> attic)
 
}



sub s_d_check {
   # Selfactivated(S) ->   -> Disabled(D)  .move (alte klasse -> attic)
}



sub d_e_check {
   my $count=0;
   &titel("d_e_check: Looking for users coming back ...");
   # Disabled(D) -> Enabled(E)  .kill .move (alte klasse -> attic)
   while ( my ($identifier, $status) = each %sophomorix_status ){
      if ($status eq "D" or $status eq "T" or $status eq "R"){
         my $date=$user_deaktivierungsbeginn{$identifier};
         my $login=$schueler_im_system_loginname{$identifier};
         my $unid="";
         # check if there is a unid
         if (defined $schueler_ok_unid{$identifier} ){
	     $unid=$schueler_ok_unid{$identifier};
         }
         # Check if user reappears in files
         if (exists $schueler_ok_hash{$identifier}) {
            # taucht wieder auf -> d
            print "   $identifier ist wieder da\n";
            &update_user_db_entry($login, 
                                  "Status=E",
                                  "TolerationDate=",
                                  "DeactivationDate=",
                                  "Unid=$unid",
                                  "ScheduledToleration=");
            &user_reaktivieren("$login");
            $count++;
         }
      }
   }
   &titel("d_e_check: ... $count users are coming back");
}



sub d_r_check {
   # Disabled(D) -> R(Removeable) .kill
   my $count_removable=0; 
   my $count_move=0; 
   &titel("d_r_check: Looking for disabled users to be moved/kille ...");
   while ( my ($identifier, $status) = each %sophomorix_status ){
      if ($status eq "D"){
         my $login="$schueler_im_system_loginname{$identifier}";

         my $sys_class=$schueler_im_system_hash{$identifier};
         # check if disabled user is not in attic
         if ($sys_class ne "attic") {
            &append_move_entry($identifier,$sys_class,"attic",$status);
            $count_move++;
         }
         if (not exists $schueler_ok_hash{$identifier}) {
            if (exists $user_deaktivierungsbeginn{$identifier}){
               # Check if user has to be made removeable
               $epoche_loeschen_ab = &get_action_date($identifier, "kill");
	       if ($epoche_loeschen_ab !=0) {
                  &update_user_db_entry($login, 
                                       "Status=R");
                  # report.admin
                  push @admin_list_kill, "Loeschbar:     $identifier $login\n";
                  &append_kill_entry($identifier);
                  $count_removable++;
                  $schueler_weg_anzahl++;
               } else {
                  if($Conf::log_level>=2){
                     print "### User $identifier nicht Removable\n"; 
	          }
               }
	    }
         }
      }
   }
   &titel("d_r_check: ... $count_move disabled users -> attic");
   &titel("d_r_check: ... $count_removable users are killable.");
}



sub r_r_check {
   # Removeable(R)/Killable(K) bleibt   .kill
   my $count=0;
   &titel("r_r_check: Looking for 'removable/killable users to be killed ...");
   while ( my ($identifier, $status) = each %sophomorix_status ){
      if ($status eq "R" or $status eq "K"){
         &append_kill_entry($identifier);
         $count++;
      }
   }
   &titel("r_r_check: ... $count users are removable/killable");
}


# ===========================================================================
# Neue Datei lehrer.txt.tmp erzeugen (evtl. Namen tauschen)
# ===========================================================================
sub rename_teacher_groupname {
   open (TEACHER, "<${DevelConf::users_pfad}/lehrer.txt") 
     || die "Fehler: $!";
   open (TEACHERTMP, ">${DevelConf::lehrer_datei}") 
     || die "Fehler: $!";
   while(<TEACHER>){
       s/^${Conf::teacher_group_name}/${DevelConf::teacher}/g;
       print TEACHERTMP $_;
   }
   close(TEACHER);
   close(TEACHERTMP);
}



# ===========================================================================
# Open the report-files an fill them with the current date, ...
# ===========================================================================
sub open_report_files {
# sophomorix.ok      --> All syntactically, not filtered users
open(SOPHOMORIXOK, ">${DevelConf::ergebnis_pfad}/sophomorix.ok") 
     || die "Fehler: $!";

# report.office   --> Infos für das Sekretariat über zu korrigierende Datensätze
open(REPORTSEKRETARIAT, ">${DevelConf::ergebnis_pfad}/report.office")
                        || die "Fehler: $!";
print REPORTSEKRETARIAT "Report fuer das Sekretariat \n";
print REPORTSEKRETARIAT "Start-Zeitpunkt: ",
                         $tag,".",
                         $monat,".",
                         $jahr,", Zeit: ",
                         $hours,":",
                         $min, "-",
                         $sec;

# report.filter      --> Alle Schüler in Sperrklassen, die nicht angelegt werden sollen
open(REPORTGESPERRT, ">${DevelConf::ergebnis_pfad}/report.filter") 
                     || die "Fehler: $!";
print REPORTGESPERRT "Gesperrt-Report:\n\n";
print REPORTGESPERRT "Schüler, die nicht angelegt werden,\n";
print REPORTGESPERRT "weil sie in einer Sperrklasse sind, und\n";
print REPORTGESPERRT "das Ende des Sperrdatums noch nicht erreicht ist:\n\n";


# Falls Sternchenklassen ausgefiltert werden sollen, entsprechende Datei öffnen
if ($Conf::splan_sternchenklassen_filtern eq "yes") {
  # report.splan      --> Alle Klassen, die mit * beginnen (Splan-Sternchenklassen)
  open(REPORTSPLAN, ">${DevelConf::ergebnis_pfad}/report.splan") 
                       || die "Fehler: $!";
  print REPORTSPLAN "Splan-Sternchenklassen-Report:\n\n";
  print REPORTSPLAN "Klassen, die ausgefiltert werden\n";
  print REPORTSPLAN "weil sie mit * beginnen\n";
  print REPORTSPLAN "(zukünftige Klassen):\n\n";
}


}




sub get_entfern_klassen {
    # ========================================================================
    # entfernklassen checken und in einen hash schreiben
    # ========================================================================
    # Auslesen aus sperrklassen: alle Buchstaben klein machen
    # Nur kleinbuchstaben ins Hash
    # Vor dem Vergleich mit hash in Kleinbuchstaben umbauen
    my $entfern_klasse="";
    my $entfern_datum="";
    my $entfern_tag=0;
    my $entfern_monat=0;
    my $entfern_jahr=0;
    my $entfern_datum_epoche=0;
    my %hash=();

    &titel("Reading entfernen.txt ...");

    open(DELCLASSES,"${DevelConf::config_pfad}/entfernen.txt") || die "Fehler: $!";
    while(<DELCLASSES>){
       chomp($_); # Returnzeichen abschneiden
       # Wenn ein # vorkommt, Zeile nicht beachten, da Kommentarzeile
       s/^ //g; # Leerzeichen am Zeilenangfang entfernen
       if(/^\#/){ # # am Anfang bedeutet Kommentarzeile
           # Inhalt der Kommentarzeile ausgeben
           if($Conf::log_level>=3){
              print "$_\n";
	    }
	   next;
       }
       if($_ eq ""){ # 
           # print "Leere Zeile in sperrklassen.txt\n";
	   next;
       }
       ($entfern_klasse,$entfern_datum)=split(/;/);
       # Alle Großbuchstaben im Klassennamen in Kleinbuchstaben umwandeln
       $entfern_klasse=~tr/A-Z/a-z/; 
       ($entfern_tag,$entfern_monat,$entfern_jahr)=split(/\./,$entfern_datum);
       print "$entfern_klasse wird aus dem System entfernt bis $entfern_datum";
       ## Achtung, Januar=0
       $entfern_monat=$entfern_monat-1;
       $entfern_jahr=$entfern_jahr-1900;
       # 0,0,0 = 0:00 Uhr am sperrtag
       $entfern_datum_epoche=
           timelocal(0,0,0,$entfern_tag,$entfern_monat,$entfern_jahr);
       if($Conf::log_level>=3){
          print "    Epochenzeit $entfern_datum_epoche";
        }
       ## Werte in hash-Tabelle schreiben
       $hash{"$entfern_klasse"}="$entfern_datum_epoche";
   }
   close(DELCLASSES);
   return %hash;
}




sub get_schueler_classes {
    my %classes=();
    my %unids=();
    open(TXT,"${DevelConf::schueler_datei}") || die "Fehler: $!";
    while(<TXT>){
       chomp($_); # Returnzeichen abschneiden
       s/^ //g; # Leerzeichen am Zeilenangfang entfernen
       if(/^\#/){ # # am Anfang bedeutet Kommentarzeile
           next;
       }
       if($_ eq ""){
	   next;
       }
       my ($class,$a2,$a3,$a4,$unid)=split(/;/);
       if (not exists $classes{$class}){
           $classes{$class}="exists";
       }
       # exit, when unid is double!
       if (not defined $unid){
           # do nothing
       } else {
           # check for doubling
           if (exists $unids{$unid}){
               $unid =~ s/\s//g;
               if ($unid ne "" and $unid ne "
" ){
                   &log_script_exit("ERROR: Unid ->$unid<- was seen twice already!",
                         1,1,0,@arguments);
	       }
           } else {
               $unids{$unid}="seen";
           }
       }
    }
    close(TXT);
    return %classes;
}


sub get_class_map {
    my %map=();
    open(MAP,"${DevelConf::config_pfad}/class.map");
    while(<MAP>){
       chomp($_); # Returnzeichen abschneiden
       s/^ //g; # Leerzeichen am Zeilenangfang entfernen
       if(/^\#/){ # # am Anfang bedeutet Kommentarzeile
           next;
       }
       if($_ eq ""){ # # am Anfang bedeutet Kommentarzeile
	   next;
       }
       my ($old_name,$new_name)=split(/;/);
       $map{$old_name}="$new_name";
    }
    close(MAP);
    return %map;

}



sub rename_admin_class {
    my ($old_name) = @_;
    my $new_name="";
    # check for entry in class.map
    if (exists $class_map{$old_name}){
	$new_name=$class_map{$old_name};
    } else {
        push @auto_class_rename, $old_name;
        $new_name="noclass";
        #$new_name=$old_name;
	#until (not exists $schueler_classes{$new_name}){
        #    $new_name=$new_name."y";
        #}
    }
    return $new_name;
}



sub get_sperrklassen {
    # ========================================================================
    # sperrklassen checken und in einen hash schreiben
    # ========================================================================
    # Auslesen aus sperrklassen: alle Buchstaben klein machen
    # Nur kleinbuchstaben ins Hash
    # Vor dem Vergleich mit hash in Kleinbuchstaben umbauen
    my $sperrklasse="";
    my $sperrdatum="";
    my $sperr_tag=0;
    my $sperr_monat=0;
    my $sperr_jahr=0;
    my $sperrdatum_epoche=0;
    my %hash=();

    &titel("Reading sperrklassen.txt ...");

    open(SPERRKLASSEN,"${DevelConf::config_pfad}/sperrklassen.txt") || die "Fehler: $!";
    while(<SPERRKLASSEN>){
       chomp($_); # Returnzeichen abschneiden
       # Wenn ein # vorkommt, Zeile nicht beachten, da Kommentarzeile
       s/^ //g; # Leerzeichen am Zeilenangfang entfernen
       if(/^\#/){ # # am Anfang bedeutet Kommentarzeile
           # Inhalt der Kommentarzeile ausgeben
           if($Conf::log_level>=3){
              print "$_\n";
	   }
	   next;
       }
       if($_ eq ""){ # # am Anfang bedeutet Kommentarzeile
           # print "Leere Zeile in sperrklassen.txt\n";
	   next;
       }
       ($sperrklasse,$sperrdatum)=split(/;/);
       # Alle Großbuchstaben im Klassennamen in Kleinbuchstaben umwandeln
       $sperrklasse=~tr/A-Z/a-z/; 
       ($sperr_tag,$sperr_monat,$sperr_jahr)=split(/\./,$sperrdatum);
       print "Neuzugänge in $sperrklasse werden gesperrt bis $sperrdatum";
       ## Achtung, Januar=0
       $sperr_monat=$sperr_monat-1;
       $sperr_jahr=$sperr_jahr-1900;
       # 0,0,0 = 0:00 Uhr am sperrtag
       $sperrdatum_epoche=timelocal(0, 0, 0, $sperr_tag , $sperr_monat, $sperr_jahr);
       if($Conf::log_level>=3){
          print "    Epochenzeit $sperrdatum_epoche";
       }
       ## Werte in hash-Tabelle schreiben
       $hash{"$sperrklasse"}="$sperrdatum_epoche";
    }
    ## Abfragen der Hashtabelle, Beispiel
    close(SPERRKLASSEN);
    return %hash;
}




sub semicolon_number_ok {
   my ($number, $filename) = @_;
   if ($number==4){
        # 4 Strichpunkte: In Ordnung -> weiter
        if($Conf::log_level>=3){
           print " ... $number semicolon found ...... OK\n";
        }
        return 1;
   } elsif ($number==5 && $filename eq $schueler_to_check_dateien[0] ){
        if($Conf::log_level>=3){
	    print " ... $number semicolon found (UNID)",
                  " ... OK in $filename\n";
        }
        return 1;
   } elsif ($strichpunkt_anzahl==5 && 
            $filename eq $schueler_to_check_dateien[1] ){
            # 5 Strichpunkte erlaubt in extraschueler.txt
            if($Conf::log_level>=3){
               print " ... $number semicolon found",
                     " in $filename ... OK\n";
	     }
	return 1;
   } elsif ($_ eq "") {
        # Leere Zeile -> abbrechen
        if($Conf::log_level>=2){
           print "Leere Zeile in  $filename\n";
        }
        $leere_zeilen_anzahl++; 
        # Zeilenzähler wurde schon erhöht, nun zurücksetzen 
        # Abbruch
        return 0;  
   } elsif ($number==3 
            and not m/;$/ 
            and $filename eq $schueler_to_check_dateien[0]){
        # 3 Strichpunkte: automatischer Korrekturversuch
        # Den letzten Strichpunkt zu vergessen  
        # ist ein häufiger Fehler von Schulverwaltungsprogrammen
        $_="$_;"; # Strichpunkt anhängen
        if($Conf::log_level>=3){
           print " ... $number semicolon found/last semicolon missing ... AUTOCORRECTING\n";
        }
        return 1;
   } elsif ($number==6 && $filename eq $schueler_to_check_dateien[2]) {
        # 6 Strichpunkte sind OK in Extrakurse-Datei
        if($Conf::log_level>=3){
           print " ... $number semicolon found in $filename ...... OK\n";
        }
        return 1;
   } elsif ($number==9 && $filename eq $schueler_to_check_dateien[3]) {
        # Eintragen wieviele Strichpunkt erlaubt sind
        # 8 Strichpunkte sind OK in Lehrer-Datei
        if($Conf::log_level>=3){
           print " ... $number semicolon found in $filename ...... OK\n";
        } 
        return 1;
   } else {
        $datensatz_korrupt=1;
        # autocorrection failed before, this IS a corrupt line!
        print  "##### Fehlerhafter Datensatz (Zeile: $zeilen_anzahl_datei)\n","$_\n";
        print " ... $number semicolon found in $filename ... ERROR\n";
        $datensatz_korrupt_anzahl++;
        push (@admin_list_corrupt, "Korrupter Datensatz (", $number ,
	      " Strichpunkte):\n");
        push (@admin_list_corrupt, "Linie $zeilen_anzahl_datei:   $_\n\n");

  	# Abbruch
        return 0;
   }
}







sub birthdate_usable {
    my ($date) = @_;
    my ($tag, $monat, $jahr);
       # Wenn im Geburtsdatum zweimal . vorkommt, oder ganz leer ist ????? 
       # wird der Datensatz weiterverarbeitet,
       #    sonst --> report.admin
       #              $datum_korrupt=1 und Abbruch
       my $punkte_anzahl=$date=~tr/\.//;
       if ($punkte_anzahl==2){
            if($Conf::log_level>=3){
               print " ... $punkte_anzahl dots in date found ... OK\n";
	     }
       } elsif ($date eq ""){
           if($Conf::log_level>=3){
              print " ... Date is empty, Line not corrupted ...\n";
	   }
       } else {
	    $datum_korrupt=1;
            print " ... $punkte_anzahl dots in date found ... ERROR\n";
	    $datum_korrupt_anzahl++;
            
            # Eintrag in report.admin
            push (@admin_list_corrupt, "Corrupted Line (" , $punkte_anzahl ,
                              " dots in date):\n" , $_ , "\n\n");
            # Set tag for Abbruch
            $tag = "0"  
       }

       ($tag,$monat,$jahr)=split(/\./,$date);

       # Falls einer der Datumswerte undefined ist mach ihn zum leerstring
       # verhindert ein paar Fehlermeldungen
      if (not defined $tag){
          $tag="";
	}
      if (not defined $monat){
          $monat="";
	}
      if (not defined $jahr){
          $jahr="";
	}

      return ($tag,$monat,$jahr);
}






sub syntax_check_entry {
    my ($class, $surname, $name, $day, $month, $year, 
        $birthdate, $wish_login, $filename) = @_;
    # check for empty fields
    if($class eq ""){$feld_klasse_leer=1;}
    if($surname eq ""){$feld_nachname_leer=1;}
    if($name eq ""){$feld_vorname_leer=1;}
    # Pruefen nach nichterlaubten Zeichen

    # Klasse
    # Zulassen: a-z  0-9  und -
    if ($class=~/[^a-z0-9\-]/) { # Wenn diese Zeichen nicht enthalten
        # Dann ist der Klassenamen vermutlich syntaktisch falsch
        $klasse_unerlaubtes_zeichen=1;
    } else {
        if($Conf::log_level>=3){
	   print " ... Class      OK ...\n";
	}
    }
 
    # Nachname
    # Punkt mit Nix ersetzen    
    $surname=~s/\.//g;
    # Zulassen: a-z  und - (Für Doppelnamen) sowie ' (O'Reilly)
    #if (($surname=~/[^A-Za-zÄäÖöÜüß\-\']/) &&
    if (($surname=~/[^A-Za-z\-\']/) &&
       ($filename eq $schueler_to_check_dateien[0]))# Schülerdatei 
    { # Wenn Zeichen nicht enthalten
        # Dann ist der Nachnamen vermutlich syntaktisch falsch
        $nachname_unerlaubtes_zeichen=1;
    #} elsif ($surname=~/[^A-Za-z0-9ÄäÖöÜüß\-\']/){# Extraschülerdatei
    } elsif ($surname=~/[^A-Za-z0-9\-\']/){# Extraschülerdatei
        $nachname_unerlaubtes_zeichen=1;
    } else { 
        if($Conf::log_level>=3){
           print " ... Surname    OK in $filename ...\n";
	}
    }

    # Vorname
    # Zulassen: a-z  und - (Für Doppelnamen) sowie ' (z. B. O'Reilly)
    #if (($name=~/[^A-Za-zÄäÖöÜüß\-\']/) && 
    if (($name=~/[^A-Za-z\-\']/) && 
       ($filename eq $schueler_to_check_dateien[0]))# Schülerdatei
    { # Wenn Zeichen nicht enthalten
        # Dann ist der Klassenamen vermutlich syntaktisch falsch
        $vorname_unerlaubtes_zeichen=1;
    #} elsif ($name=~/[^A-Za-z0-9ÄäÖöÜüß\-\']/){# Extraschülerdatei
    } elsif ($name=~/[^A-Za-z0-9\-\']/){# Extraschülerdatei
        $vorname_unerlaubtes_zeichen=1;
    } else {
       if($Conf::log_level>=3){
	  print " ... Firstname  OK in $filename ...\n";
       }
    }

    if($birthdate eq ""){
       # empty, dont check for other errors
       $feld_geburt_leer=1
    } elsif ($birthdate=~/[^0-9\.]/)  {
print "Wrong character\n";
       # birtdate contains characters other than 0-9 and .
       $feld_geburt_chars_error=1;
    } else {
       # check if date exists
       # return 1 when valid
       # return 0 when invalid
       my $return=check_date($year,$month,$day);
       if ($return==0){
          if($Conf::log_level>=3){
             print " ... Date       ERROR ... date nonexisting\n";
	  }
          $date_nonexistent=1;
       } else {
          if($Conf::log_level>=3){
             print " ... Date       OK (date exists) ...\n";
	  }
       }

       # convert Day 1 -> 01, ....
       $day=$convert_days{$day};
       # Monat: von 1 bis 12, bzw 01 bis 12, siehe Hash-Tabelle %convert_month
       $month=$convert_month{$month};

       # Geburtsjahreszahl von bis
       if ((exists($geburts_jahreszahl_erlaubt{$year})) &&
          ($filename eq $schueler_to_check_dateien[0]))# Schülerdatei
       {
           # Wenn Geburtsjahreszahl im hash zu finden ist
           # Ersetze den Key(z. B. 97) mit dem Value (1997)
           $year=$geburts_jahreszahl_erlaubt{$year};
           if($Conf::log_level>=3){
              print " ... Year        OK in $filename ...\n";
           }
       } elsif ((not exists($geburts_jahreszahl_erlaubt{$year})) &&
           ($filename eq $schueler_to_check_dateien[0])) {
           if($Conf::log_level>=3){
               print " ... Year        ERROR ...\n";
           }
           $datum_unrealistisch=1;
       } elsif (($year>=1900) && ($year<=2100)) {
           if($Conf::log_level>=3){
              print " ... Year        OK in $filename ...\n";
           }
       } else {
           print " ... Year ERROR ...\n";
           $datum_unrealistisch=1;
       }
    }

    
    # Wunschloginnamen checken
    # Zulassen: a-z  sowie 0-9
    if ($wish_login eq "---") {
        # nix tun, kein wunsch_login angegeben
    }  elsif ($wish_login=~/[^a-z0-9]_-\./){
        # Fehlerhafter Wunsch-Login
#        print "\n\n\nSYNTAXFEHLER!\n";
#        print "\n\n  ---$wish_login---    enthält Sonderzeichen\n\n";
#        print "\n\n  Beheben sie diesen Fehler in /etc/sophomorix/user/extrakurse.txt\n\n";
#        print "\n\n  Und starten sie das Programm neu.\n\n";
        # Programm beenden
        &log_script_exit("ERROR: $wish_login contains unvalid chars",
                         1,1,0,@arguments);

        #   exit;
    } else {
        if($Conf::log_level>=3){
	   print " ... Wunschlogin  $wish_login OK in $filename ...\n";
        }
    }
    # Wunsch-Passwort checken: Fuer nicht notwendig erachtet
    return($class, $surname, $name, $day, $month, $year, $birthdate, $wish_login);
}



sub is_in_inhibit_class {
    # schueler in sperrklassen ausfiltern
    my ($class) = @_;
    if ($sperrklassen{"$class"}){
        print " ... $vorname $nachname ist in einer Sperrklasse!\n";
        ## Datumscheck durchführen:
        print " ... Sperrdatum in Epochenzeit für die Klasse $class ist: ", 
               $sperrklassen{"$class"},"\n";
        print " ... Epochenzeit zu Beginn der Programmablaufes war     : ", 
               $epoche_jetzt, "\n";
        if ($epoche_jetzt>$sperrklassen{"$class"}){
            print $vorname , " " , $nachname , 
                  " wird weiterverarbeitet (Sperrzeit überschritten!)\n";
            # Datensatz in sophomorix.ok schreiben
            return 0;
        }elsif ($schueler_im_system_hash{$identifier_ok}){
            # Wenn Schüler im System
            print "########################################",
                  "########################################\n",
                  "Der Schüler ist schon im System,",
                  " Datensatz wird deshalb NICHT ausgefiltert.\n",
                  " (zum Ausfiltern müsste ein Eintrag in ",
                  "entfernen.txt vorliegen)\n",
                  "########################################",
                  "########################################\n";
            # Schüler im System lassen
            # Datensatz in sophomorix.ok schreiben
            return 0;
        }else {
            print $vorname , $nachname , 
                  " wird NICHT angelegt (Sperrzeit noch nicht erreicht!)\n";
            # Datensatz in report.filter schreiben
	    print REPORTGESPERRT "$_\n";
            $schueler_gesperrt_anzahl++;
            # Schueler IS in inhibit class
            return 1;
        }
    } else {
        if($Conf::log_level>=3){
           print " ... Keine Sperrklasse ...\n";
	}
        return 0;
    } #Einträge in Sperrklassen sind ausgefiltert
}







sub is_in_remove_class {
    # Eintraege in Entfernen-Klasse ausfiltern
    my ($class) = @_;
    if ($entfern_klassen{"$class"}){
        print " ... $vorname $nachname ist in einer zu",
              " entfernenden Klasse!\n";
        ## Datumscheck durchführen:
        print " ... Einlasszeit in Epochenzeit für die Klasse $class ist: ", 
              $entfern_klassen{"$class"},"\n";
        print " ... Epochenzeit zu Beginn der Programmablaufes war      : ", 
              $epoche_jetzt, "\n";
        if ($epoche_jetzt>$entfern_klassen{"$class"}){
            print $vorname , " " , $nachname , 
                  " wird weiterverarbeitet (Sperrzeit überschritten!)\n";
            # Datensatz in sophomorix.ok schreiben
            return 0;
        }else {
           print "########################################",
                 "########################################\n",
                 "$vorname $nachname", 
                 " wird NICHT angelegt/Entfernt ",
                 "(Einlasszeit nicht erreicht!)\n",
                 "########################################",
                 "########################################\n";
           # Datensatz in report.filter schreiben
	   print REPORTGESPERRT "$_\n";
           $schueler_entfernt_anzahl++;
           # Schueler IS in remove class
           return 1;
        }
    } else {
        if($Conf::log_level>=3){
           print " ... Keine zu entfernende Klasse ...\n";
        }
        return 0;
    } #Einträge in Entfernen-Klasse sind ausgefiltert
}



sub add_user_to_queue {
    my ($user, $wunsch_login, $wunsch_passwort,$file) = @_;
    # Zeile zerlegen
    my ($admin_class,$okfeld2,$okfeld3,$okfeld4,$unid)=split(/;/, $user);
    # Identifier bilden
    my $identifier=join("",($okfeld2,";",$okfeld3,";",$okfeld4));
    my $real_class = ${DevelConf::teacher};
    # Ausser bei lehrern ein k hinzufuegen
# ??????????????
#    if (not $admin_class eq ${DevelConf::teacher}){
#          $real_class="k"."$admin_class";
#    }

    # Sub-Klasse beruecksichtigen
#    if (exists $schueler_subklassen{$identifier}) {
#    #   print "$identifier ist in subklasse -$schueler_subklassen{$identifier} \n";
#       $real_class="$real_class"."-"."$schueler_subklassen{$identifier}";  
#    }



    # Create Error, when wunschlogin seen twice
    if (not $wunsch_login eq "---" and exists $wunsch_login_seen{$wunsch_login}){
        my $old_number=1;
        print "\nWARNING: Users will not be added!\n\n";
	print "   Loginname    $wunsch_login  in  $filename_map{ $file }  ",
              "is double ($identifier)!\n";
	print "   I have seen  $wunsch_login  in ",
              " $filename_map{ $wunsch_login_seen{ $wunsch_login } } ",
              " already!\n\n";
	print "   Please edit $filename_map{$file} and correct this error ",
              "to add this user. \n\n";
        if (exists $wunsch_login_forbidden{$wunsch_login}){
#	   print "$wunsch_login is old ($wunsch_login_forbidden{$wunsch_login})\n";
           $old_number=$wunsch_login_forbidden{$wunsch_login};
        }
        $wunsch_login_forbidden{$wunsch_login}=$old_number+1;
    } else {
       $wunsch_login_seen{$wunsch_login}="$file";
if (not exists $schueler_ok_hash{$identifier}){
    $schueler_ok_hash{$identifier}=$admin_class;
}

    }




    # Unid in hash-table einordnen, falls moeglich
    if ($unid ne ""){
       # Eintrag in die Hash-Tabelle, falls identifier noch nicht vorhanden
       $unid_ok_file{$unid}="$identifier";
       $schueler_ok_unid{$identifier}="$unid";

    } elsif ($unid eq ""){
       #	print "UNID is empty\n";
       # nix tun, da leer
    } else {
       # Fehler
	print "Error UNID $unid is not unique!\n";
    }   

    # Usertoken in hash-table einordnen, falls moeglich
    if ($usertoken ne ""){
       # Eintrag in die Hash-Tabelle, falls identifier noch nicht vorhanden
       $schueler_ok_usertoken{$identifier}="$usertoken";
    } elsif ($unid eq ""){
       #	print "UNID is empty\n";
       # nix tun, da leer
    }


    # User in hash-table einordnen, falls moeglich
    if (not exists $schueler_ok_hash{$identifier} and $unid ne ""){
       # Eintrag in die Hash-Tabelle, falls identifier noch nicht vorhanden
       $schueler_ok_hash{$identifier}="$admin_class";
    } elsif ($unid eq ""){
       # nix tun, da leer
    } else {
        # Identifier schon vorhanden, kommt also mehrfach vor
        # Pruefen ob die Klasse auch gleich ist und Datei schueler.txt ist
#        if ($schueler_ok_hash{$identifier} eq $real_class 
        if ($schueler_ok_hash{$identifier} eq $admin_class 
            && ($schuelerdatei eq $schueler_to_check_dateien[0])){
            # Klasse ist gleich/Datensatz wird verwendet
            print REPORTSEKRETARIAT "----------------------------------------",
                                    "----------------------------------------";
            printf REPORTSEKRETARIAT "\n%-23s(Zeile:%6s) :  %-30s\n",
                                     "Mehrfacher Datensatz", 
                                     $zeilen_anzahl_datei,
                                     "$klasse".";"."$identifier_schueler_ok";
            printf REPORTSEKRETARIAT "%38s:  %-30s\n" ,
                                     "ORIGINALDATENSATZ ", 
                                     $zeile_orig;
            print REPORTSEKRETARIAT ("Der Datensatz kommt mehrfach vor!\n");
        } elsif ($klasse ne $schueler_ok_hash{$identifier}) {
            # Klasse ist verschieden
            # Meldung: Identifier muss eindeutig bleiben
            print REPORTSEKRETARIAT "----------------------------------------",
                                    "----------------------------------------";
            printf REPORTSEKRETARIAT "\n%-23s(Zeile:%6s) :  %-30s\n",
                                     "Mehrfacher Datensatz", 
                                     $zeilen_anzahl_datei,
                                     "$klasse".";"."$identifier";
            printf REPORTSEKRETARIAT "%38s:  %-30s\n" ,
                                     "ORIGINALDATENSATZ ", 
                                     $zeile_orig;
            print REPORTSEKRETARIAT "Fuer diese SchuelerIn gibt es ebenfalls",
                                    " eine Zuordnung zur Klasse  ", 
                                    ,$schueler_ok_hash{$identifier},
#                                    ,substr($schueler_ok_hash{$identifier},1,30),
                                    " !\n"; 
            return 0;
            #next;
        }
    }
        
    # User in Hashtable (key = identifier, value = wunschlogoin)
    $schueler_ok_wunsch_login_hash{$identifier}="$wunsch_login";


    # User in Hashtable (key = identifier, value = wunschpasswort)
    $schueler_ok_wunsch_passwort_hash{$identifier}="$wunsch_passwort";

    # Schueler in den Klassen zaehlen
    if (exists($klasse_schueleranzahl{$admin_class})){
        $klasse_schueleranzahl{$admin_class}++; # 1 dazuzählen
    } else {
        $klasse_schueleranzahl{$admin_class}=1;
    }
    return 1;
}




sub auto_teach_in {
   my %will_come=();
   my %will_go=();

   &titel("auto_teach-in: Modyfying the following accounts:");

   # will go:
   foreach (keys %schueler_im_system_hash) {
      # will go
      if (not exists $schueler_ok_hash{$_}){
         if($Conf::log_level>=3){
             print "will_go  :  $_  $schueler_im_system_hash{$_}\n";
         }
         $will_go{$_}=$schueler_im_system_hash{$_};
      }
   }

   # will come:
   foreach (keys %schueler_ok_hash) {
      # will come
      if (not exists $schueler_im_system_hash{$_}){
         if($Conf::log_level>=3){
            print "will_come:  $_  $schueler_ok_hash{$_}\n";
         }
         $will_come{$_}=$schueler_ok_hash{$_};

      }
   }

   foreach my $go (keys %will_go) {
      my $go_class=$will_go{$go};
      # Attribute eines users
      my @go = split(/;/, $go);
#      if ($go_class eq "dachboden" or $go_class eq "speicher"){
#          $go_class=$schueler_im_system_exit_admin_class{$go}
#      }
      if ($go_class eq "attic"){
          $go_class=$schueler_im_system_exit_admin_class{$go}
      }
      push @go, $go_class;

      foreach my $come (keys %will_come) {
         my $come_class=$will_come{$come};
         my @come=split(/;/, $come);
         push @come, $come_class;
  
         if($Conf::log_level>=3){
            print "\n### Checking vanising  $go  for matches:\n";
            &linie();
            printf "%-25s %-25s %-11s %-11s \n",
                   $go[0],$go[1],$go[2],$go[3];
            printf "%-25s %-25s %-11s %-11s \n",
                   $come[0],$come[1],$come[2],$come[3];
            &linie();
         }
         # string-approx: immer das lange im kurzen suchen
         # da substring-match
         my $name_long;
         my $name_short;
         my $first_long;
         my $first_short;

         if (length($come[0]) < length($go[0])){
	     $name_long=$go[0];
             $name_short=$come[0];
         } else {
	     $name_long=$come[0];
             $name_short=$go[0];
         }
         if (length($come[1]) < length($go[1])){
	     $first_long=$go[1];
             $first_short=$come[1];
         } else {
	     $first_long=$come[1];
             $first_short=$go[1];
         }
         # creating param_list
         my @param=($go,
                    $come,
                    $name_long,
                    $name_short,
                    $first_long,
                    $first_short,
                    $come[2],
                    $go[2],
                    $come[3],
                    $go[3]);
         # varying the parameters of the matcheswith difference
         # of class-names (old class - new class) 
         # abbruch mit use of uninitiale ....

         if ($go[3] eq $come[3]) {
            # class names are identical
            if($Conf::log_level>=3){
               print "### Identical Class:         Yes!\n";
	    }
            &single_change_match(@param, "30%", "30%","30%");
            &multi_change_match(@param, "15%", "15%","15%");
            &add_first_match(@param, "15%", "10%","15%");
            # marriage
            # ?? single_change_match includes marriage ??
            # following line can be commented ??
            &single_change_match(@param, "100%", "0%","0%");
        } elsif (amatch($go[3], ["S1"], $come[3])){
            # one single character diff. (Substitution)
            if($Conf::log_level>=3){
              print "### ApproxMatchClass:        Yes!\n";    
            }
            &single_change_match(@param, "15%", "15%", "15%");
            &multi_change_match(@param, "30%", "30%","10%");
            &add_first_match(@param, "15%", "10%","15%");
        } else { 
            # completely different classes
            if($Conf::log_level>=3){
               print "### ApproxMatchClass:        No!\n";
            }
            # ?? single_change_match includes marriage ??
            #&single_change_match(@param, "0%","0%","0%");
            &multi_change_match(@param, "30%", "30%","10%");
            &add_first_match(@param, "15%", "10%","15%");
        }
     }
   }
   while (my ($identifier_sys,$identifier_file) = each %go_come){
      my $points = $come_points{$identifier_file};
      # go through all users that have to be changed 
      printf "  %-34s %-34s %-3sPts\n",
             $identifier_sys,
             $identifier_file,
             $points;
      $auto_teach_in_count++;
      # updating the hashes that sophomorix uses internally
      &update_all_hashes($identifier_sys,$identifier_file);

      # update the database
      my $login=$schueler_im_system_loginname{$identifier_file};
      my ($last,$first,$birth)=split(/;/, $identifier_file);
      &update_user_db_entry($login, 
                            "Name=$first",
                            "LastName=$last",
                            "Gecos=$first $last",
                            "Birthday=$birth");
      # log
      &append_teach_in_log("auto-teach-in",
                                $login,
                                $identifier_sys,
                                $identifier_file,
                               );

      # update the system
      #my $gecos="$first"." "."$last";
      #&Sophomorix::SophomorixPgLdap::update_user_db_entry($login,
      #                "Gecos=$gecos");
   }
   &titel("auto_teach-in: ... $auto_teach_in_count accounts modified");
}


# should be possible to also push other data
sub update_db_pushdata {
   while ( my ($identifier,$login) = each %schueler_im_system_loginname ) { 
       #print "Verifying $identifier ($login) \n";
       if (not exists $schueler_ok_usertoken{$identifier}){
           $schueler_ok_usertoken{$identifier}="";
       }
       if ($schueler_ok_usertoken{$identifier} eq "usertoken"){
           $schueler_ok_usertoken{$identifier}="";
       }
       if (not exists $identifier_sys_usertoken{$identifier}){
	   $identifier_sys_usertoken{$identifier}="";
       }
       if ($schueler_ok_usertoken{$identifier} ne 
           $identifier_sys_usertoken{$identifier}){
           if ($schueler_ok_usertoken{$identifier} ne "usertoken"){
               if ($schueler_ok_usertoken{$identifier} eq ""){
                   print "  Uploading empty Usertoken to user $login \n";
	       } else {
                   print "  Uploading Usertoken $schueler_ok_usertoken{$identifier} ",
                         "to user $login \n";
               }
           &update_user_db_entry($login,
             "Usertoken=$schueler_ok_usertoken{$identifier}");
           }
       }
   }
}


sub update_unids {
   my $count=0;
   my $identifier_file = "";
   my $identifier_sys = "";
   my $login="";
   my $last="";
   my $first="";
   my $birthday="";
   my $gecos="";
   &titel("update_unids: Modifying the following accounts:");
   # walk through all UNID's
   while ( my ($unid_file, $identifier_file) = each %unid_ok_file ) { 
     if (exists $unid_ok_system{$unid_file}) {
         $identifier_sys = $unid_ok_system{$unid_file};
         if($Conf::log_level>=3){
             print "\nUNID $unid_file exists in the \n",
                   "  System with identifier  : $identifier_sys\n";
             print "  Files  with identifier  : $identifier_file \n";
         }
         # UNID exists in system, trying to update identifier
         # are they the same Person?
         if ($identifier_file eq $identifier_sys) {
             # identifiers are identical, do nothing
             if($Conf::log_level>=3){
                print "Identical identifiers, I'm doing nothing\n";
	    }
             next;
         } else {
             # dentifier has changed, unid is the same
             if (amatch("$identifier_sys", ["i 35%"], $identifier_file) ) {
                # OK, identifiers are similar
             } else {
                # very different, show warning
                print "  Warning: Unid $unid_file:\n";
                print "  $identifier_sys / $identifier_file\n";
                print "  Looks like different persons to me!\n";
                print "  I'm updating because of identical UNID!\n";
	     }

             $login=$schueler_im_system_loginname{$identifier_sys};
             if($Conf::log_level>=3){
                print "Looks like the same person --> ";
	     }
             print "  $login (Unid $unid_file) ",
                   "$identifier_sys -> $identifier_file\n";
             # push the new identifier to the system
             ($last,$first,$birthday)=split(/;/, $identifier_file);
             &update_user_db_entry($login, "LastName=$last",
                                           "Name=$first",
                                           "Gecos=$first $last",
                                           "Birthday=$birthday");
             # log
             &append_teach_in_log("update_unids",
                                  $login,
                                  $identifier_sys,
                                  $identifier_file,
                                  $unid_file);

             # update the system
             #$gecos="$first"." "."$last";
             #&Sophomorix::SophomorixPgLdap::update_user_db_entry($login,
             #         "Gecos=$gecos");

             # updating the hashes that sophomorix uses internally
             &update_all_hashes($identifier_sys,
                                $identifier_file,
                                $unid_file,
                               );
             $count++;
         }
     } else {
         if($Conf::log_level>=3){
           # UNID doesn't exist
           print "   UNID  $unid_file  doesn't exist in the System\n";
           # ermittle den identifier zu dieser unid
           print "   UNID  $unid_file  has identifier ",
                 " $identifier_file (file)\n";
         }
         if (exists $schueler_im_system_hash{$identifier_file} ) {
            if($Conf::log_level>=3){
               print "   Identifier  $identifier_file  exists in the system\n";
	    }
            # login des identifier holen
	    my $login=$schueler_im_system_loginname{$identifier_file};
            my $id_sys=$identifier_file;
	    print "  Uploading Unid $unid_file to user $login \n";
	    &update_user_db_entry($login, "Unid=$unid_file");
            &append_teach_in_log("new-unid",
                                 $login,
                                 $id_sys,
                                 $identifier_file,
                                 $unid_file);
            # deleting the old entry
            my $unid_sys_old;
            # find old unid of $id_sys
            while (my ($unid_1,$ident_1) = each %unid_ok_system){
		if ($ident_1 eq $id_sys){
                    # remove this entry
		    $unid_sys_old=$unid_1;
                }  
            }
            if (defined $unid_sys_old){
                # delete this old unid if existing
                # (doesnt exist when user had no unid before)
                delete $unid_ok_system{$unid_sys_old};
            }
            # create entry with the new unid, identifier-hash
            $unid_ok_system{$unid_file} = "$id_sys";
            $count++;
	}
     }
   }
   &titel("update_unids: ... $count accounts updated.");
}



sub remove_unids {
   my $count=0;
   my $login="";
   &titel("remove_unids: Modifying the following accounts:");
   while ( my ($unid_system, $identifier_system) = each %unid_ok_system ) { 
     if ($unid_system eq ""){
	 next;
     }
     if($Conf::log_level>=3){
         print "Working on $unid_system from  $identifier_system\n";
     }
     if (exists $unid_ok_file{$unid_system}) {
         if($Conf::log_level>=3){
            print "   UNID $unid_system exists in the System and Files:",
                  " Nothing to do.\n";
         }
     } else {
        if($Conf::log_level>=3){
  	   print "   UNID $unid_system of $identifier_system missing in files\n";
        }
        $login=$schueler_im_system_loginname{$identifier_system};
        &update_user_db_entry($login,"Unid=","ExitUnid=$unid_system");
        print "   Removing UNID $unid_system from account $login\n"; 
        $count++;
     }
   }
   &titel("remove_unids: ... $count unid in system removed.");
}





sub update_all_hashes {
    my ($identifier_sys,
        $identifier_file,
        $unid_file) = @_;

    # Hashes, in which entries MUST exist

    # identifier - AdminClass
    my $old_class = $schueler_im_system_hash{$identifier_sys};
    delete $schueler_im_system_hash{$identifier_sys};
    $schueler_im_system_hash{$identifier_file} = "$old_class";

    # identifier - loginname
    my $old_login = $schueler_im_system_loginname{$identifier_sys};
    delete $schueler_im_system_loginname{$identifier_sys};
    $schueler_im_system_loginname{$identifier_file} = "$old_login";

    # identifier - status
    my $old_status = $sophomorix_status{$identifier_sys};
    delete $sophomorix_status{$identifier_sys};
    $sophomorix_status{$identifier_file} = "$old_status";

    # Hashes, in which entries MAY exist

    # identifier - TolerationDate
    if (exists $user_duldungsbeginn{$identifier_sys}) {
       my $old_tol = $user_duldungsbeginn{$identifier_sys};
       delete $user_duldungsbeginn{$identifier_sys};
       $user_duldungsbeginn{$identifier_file} = "$old_tol";
    }

    # identifier - DeactivationDate
    if (exists $user_deaktivierungsbeginn{$identifier_sys}) {
       my $old_deact = $user_deaktivierungsbeginn{$identifier_sys};
       delete $user_deaktivierungsbeginn{$identifier_sys};
       $user_deaktivierungsbeginn{$identifier_file} = "$old_deact";
    }

    # identifier - SubClass
    if (exists $schueler_subklassen{$identifier_sys}) {
       my $old_sub = $schueler_subklassen{$identifier_sys};
       delete $schueler_subklassen{$identifier_sys};
       $schueler_subklassen{$identifier_file} = "$old_sub";
    }

    # identifier - ExitAdminClass
    if (exists $schueler_im_system_exit_admin_class{$identifier_sys}) {
       my $old_eac= $schueler_im_system_exit_admin_class{$identifier_sys};
       delete $schueler_im_system_exit_admin_class{$identifier_sys};
       $schueler_im_system_exit_admin_class{$identifier_file} = "$old_eac";
    }


    # identifier - AccountType
    if (exists $schueler_im_system_account_type{$identifier_sys}) {
       my $old_at = $schueler_im_system_account_type{$identifier_sys};
       delete $schueler_im_system_account_type{$identifier_sys};
       $schueler_im_system_account_type{$identifier_file} = "$old_at";
    }

    # only if unid has to be updated
    if (defined $unid_file) {
       if (exists $unid_ok_system{$unid_file} ) {
          delete $unid_ok_system{$unid_file};
          $unid_ok_system{$unid_file} = "$identifier_file";
      } elsif (not exists $unid_ok_system{$unid_file} ) {
          $unid_ok_system{$unid_file} = "$identifier_file";
      }
    }


}




sub report_error {
    print REPORTSEKRETARIAT "----------------------------------------",
                            "----------------------------------------";
    printf REPORTSEKRETARIAT "\n%-23s(Zeile:%6s) :  %-30s\n",
                             "Fehlerhafter Datensatz", 
                             $zeilen_anzahl_datei,$_;
    printf REPORTSEKRETARIAT "%38s:  %-30s\n" ,
                             "ORIGINALDATENSATZ ", 
                             $zeile_orig;
    print  "##### Fehlerhafter Datensatz (Zeile: $zeilen_anzahl_datei)\n","$_\n";
    if ($feld_klasse_leer==1){
	print REPORTSEKRETARIAT "   - Feld Klasse ist leer\n";
        print "   - Feld Klasse ist leer\n";
	$feld_klasse_leer_anzahl++;
    };
    if ($feld_nachname_leer==1){
	print REPORTSEKRETARIAT "   - Feld Nachname ist leer\n";
        print "   - Feld Nachname ist leer\n";
        $feld_nachname_leer_anzahl++;
    };
    if ($feld_vorname_leer==1){
	print REPORTSEKRETARIAT "   - Feld Vorname ist leer\n";
        print "   - Feld Vorname ist leer\n";
	$feld_vorname_leer_anzahl++;
    };
    if ($feld_geburt_leer==1){
	print REPORTSEKRETARIAT "   - Feld Geburtsdatum ist leer\n";
	print "   - Feld Geburtsdatum ist leer\n";
	$feld_geburt_leer_anzahl++;
    };
    if ($feld_geburt_chars_error==1){
	print REPORTSEKRETARIAT "   - Feld Geburtsdatum hat unerlaubte Zeichen\n";
	print "   - Feld Geburtsdatum hat unerlaubte Zeichen\n";
	$feld_geburt_chars_error_anzahl++;
    };
    if ($klasse_unerlaubtes_zeichen==1){
	print REPORTSEKRETARIAT "   - Unerlaubtes Zeichen in der Klasse\n";
	print "   - Unerlaubtes Zeichen in Klasse\n";
	$klasse_unerlaubtes_zeichen_anzahl++;
    };
    if ($nachname_unerlaubtes_zeichen==1){
	print REPORTSEKRETARIAT "   - Unerlaubtes Zeichen im Nachnamen\n";
	print "   - Unerlaubtes Zeichen im Nachnamen\n";
	$nachname_unerlaubtes_zeichen_anzahl++;
    };
    if ($vorname_unerlaubtes_zeichen==1){
	print REPORTSEKRETARIAT "   - Unerlaubtes Zeichen im Vornamen\n";
	print "   - Unerlaubtes Zeichen im Vornamen\n";
	$vorname_unerlaubtes_zeichen_anzahl++;
    };
    if ($datum_unrealistisch==1){
	print REPORTSEKRETARIAT "   - Jahr ist unrealistisch" , 
                                "/Datum mit unerlaubtem Zeichen oder leer\n";
	print "   - Jahr ist unrealistisch/Datum mit unerlaubtem ",
              "Zeichen oder leer\n";
	$datum_unrealistisch_anzahl++;
    };
    if ($date_nonexistent==1){
	print REPORTSEKRETARIAT "   - Datum existiert nicht\n";
	print "   - Datum existiert nicht\n";
	$date_nonexistent_count++;
    };
#    print "\n";
}







sub generate_admin_report {
    # report.admin         --> Infos nur für den Administrator
    open(REPORTADMIN, ">${DevelConf::ergebnis_pfad}/report.admin") || die "Fehler: $!";
    print REPORTADMIN "Administrator-Report:\n";
    print REPORTADMIN "Start-Zeitpunkt: ",
                       $tag,".",
                       $monat,".",
                       $jahr,", Zeit: ",
                       $hours,":",
                       $min, "-",
                       $sec, "\n\n\n";

    print REPORTADMIN "Korrupte Datensaetze:\n";
    print REPORTADMIN "----------------------------------------",
                  "----------------------------------------\n";

    foreach my $line (@admin_list_corrupt) {
        print REPORTADMIN $line;
    }

    print REPORTADMIN "\n\n";

    print REPORTADMIN  "Abschluss-Report für den Administrator:\n";              
    print REPORTADMIN  "----------------------------------------",
                   "----------------------------------------\n";
    printf REPORTADMIN "%-50s:%6s\n", 
                       "Eingelesene Zeilen (ohne Kommentarzeilen)", $zeilen_anzahl;

    printf REPORTADMIN "%-50s:%6s\n",
                       "Davon Korrupte Datensätze (Strichpunkt)", 
                       $datensatz_korrupt_anzahl;

    printf REPORTADMIN "%-50s:%6s\n", 
                       "Korruptes Datum (Punkte)", 
                       $datum_korrupt_anzahl;

    printf REPORTADMIN "%-50s:%6s\n", 
                       "Leere Zeilen", 
                       $leere_zeilen_anzahl;

    print REPORTADMIN  "                            ",
                       "                     ---------\n";
    $weiterverarbeitet_anzahl=
                    $zeilen_anzahl
                   -$datensatz_korrupt_anzahl
                   -$datum_korrupt_anzahl
                   -$leere_zeilen_anzahl;

    printf REPORTADMIN "%-50s:%6s\n", 
                       "Weiterverarbeitete Datensätze",  
                       $weiterverarbeitet_anzahl;

    print REPORTADMIN  "\nVon den weiterverarbeiteten Datensätzen\n";
    printf REPORTADMIN "%-50s:%6s\n", 
                       "wurden von sperrklassen.txt gesperrt", 
                       $schueler_gesperrt_anzahl;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "wurden von entfernen.txt gesperrt", 
                       $schueler_entfernt_anzahl;

    # Ausgabe von 
    if ($Conf::splan_sternchenklassen_filtern eq "yes") {
        printf REPORTADMIN "%-50s:%6s\n", 
                           "wurden als *klassen gesperrt (SPLAN)", 
                           $schueler_sternchenklasse_anzahl;
    }

    print REPORTADMIN  "                          ",
                       "                       ---------\n";
    $ungesperrt_anzahl=
                    $weiterverarbeitet_anzahl
                   -$schueler_gesperrt_anzahl
                   -$schueler_entfernt_anzahl
                   -$schueler_sternchenklasse_anzahl;# ist null, wenn ausfiltern
               
    printf REPORTADMIN "%-50s:%6s\n", 
                       "Ungesperrte/Nicht entfernte Schüler ", 
                       $ungesperrt_anzahl;

    print REPORTADMIN  "\n";

    printf REPORTADMIN "%-50s:%6s\n", 
                       "Davon anzulegende Schüler", 
                       $schueler_ok_anzahl;
    print REPORTADMIN "\n";

    print REPORTADMIN  "Fehler in Datensätzen:\n";
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Leeres Feld Klasse", 
                       $feld_klasse_leer_anzahl ;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Leeres Feld Nachname", 
                       $feld_nachname_leer_anzahl;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Leeres Feld Vorname", 
                       $feld_vorname_leer_anzahl;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Leeres Feld Geburtsdatum", 
                       $feld_geburt_leer_anzahl;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Unerlaubte Zeichen in Geburtsdatum", 
                       $feld_geburt_chars_error_anzahl;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Unerlaubtes Zeichen in der Klasse", 
                       $klasse_unerlaubtes_zeichen_anzahl;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Unerlaubtes Zeichen im Nachnamen", 
                       $nachname_unerlaubtes_zeichen_anzahl;
    printf REPORTADMIN "%-50s:%6s\n",
                       "  Unerlaubtes Zeichen im Vornamen", 
                       $vorname_unerlaubtes_zeichen_anzahl;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Datum unrealistisch/unerlaubte Zeichen/leer", 
                       $datum_unrealistisch_anzahl;
    printf REPORTADMIN "%-50s:%6s\n", 
                       "  Datum existiert nicht", 
                       $date_nonexistent_count;
    print REPORTADMIN "\n";


    # WARNUNGEN
    print REPORTADMIN  "\n";
    print REPORTADMIN  "========================================",
                       "========================================\n";
    print REPORTADMIN  "WARNUNGEN !\n";
    print REPORTADMIN  "========================================",
                       "========================================\n";

    # Klassen mit zuviel oder zuwenigen SchuelerInnen
    print REPORTADMIN  "Bezüglich der Schüleranzahl befinden sich in\n";
    print REPORTADMIN  "sophomorix.ok folgende Zweifels-Fälle:\n";
    print REPORTADMIN  "----------------------------------------",
                       "----------------------------------------\n";
    while (($klass,$sch_zahl) = each %klasse_schueleranzahl){
        if ($klass eq ${DevelConf::teacher}){
            # dont show class teacher because its not a doubt
	    next;
        }
        if ($sch_zahl<=$Conf::mindest_schueler_anzahl_pro_klasse){
	    print  REPORTADMIN "Klassenstärke ",
                               $Conf::mindest_schueler_anzahl_pro_klasse,
                               " oder weniger SchülerInnen:\n";
            printf REPORTADMIN "  %-12s:%6s\n\n", "$klass" , $sch_zahl; 
        }
        if ($sch_zahl>=$Conf::maximale_schueler_anzahl_pro_klasse){
	    print  REPORTADMIN "Klassenstärke ",
                               $Conf::maximale_schueler_anzahl_pro_klasse,
                               " oder mehr SchülerInnen:\n";
            printf REPORTADMIN "  %-12s:%6s\n\n", "$klass" , $sch_zahl; 
        }

    }         


    print REPORTADMIN  "\n";
    print REPORTADMIN  "========================================",
                   "========================================\n";
    print REPORTADMIN  "WAS WÄRE WENN ... !\n";
    print REPORTADMIN  "========================================",
                   "========================================\n";

    print REPORTADMIN "\n\nEntfernt werden KÖNNEN:\n";
    print REPORTADMIN "----------------------------------------",
                  "----------------------------------------\n";


    foreach my $line (@admin_list_toleration) {
        print REPORTADMIN $line;
    }





    foreach my $line (@admin_list_deactivation) {
        print REPORTADMIN $line;
    }



    foreach my $line (@admin_list_kill) {
        print REPORTADMIN $line;
    }

    print REPORTADMIN "----------------------------------------",
                      "----------------------------------------\n";
    print REPORTADMIN "Insgesamt KÖNNEN $schueler_weg_anzahl", 
                      " User entfernt werden.\n";

    print REPORTADMIN "\n\nVersetzen nach attic möglich:\n";
    print REPORTADMIN "----------------------------------------",
                  "----------------------------------------\n";

    foreach my $line (@admin_list_attic) {
        print REPORTADMIN $line;
    }

    print REPORTADMIN "----------------------------------------",
                      "----------------------------------------\n";
    print REPORTADMIN "Insgesamt KÖNNEN $schueler_duldungs_beginn_anzahl", 
                      " User nach attic versetzt werden.\n";

    print REPORTADMIN "\n\nAngelegt würden:\n";
    print REPORTADMIN "----------------------------------------",
                      "----------------------------------------\n";

    foreach my $line (@admin_list_add) {
        print REPORTADMIN $line;
    }

    print REPORTADMIN "----------------------------------------", 
                      "----------------------------------------\n";
    print REPORTADMIN "Insgesamt KÖNNEN ", "$schueler_hinzu_anzahl", 
                      " User angelegt werden.\n";

    print REPORTADMIN "\n\nVersetzt würden:\n";
    print REPORTADMIN "----------------------------------------",
                      "----------------------------------------\n";

    foreach my $line (@admin_list_move) {
        print REPORTADMIN $line;
    }

    print REPORTADMIN "----------------------------------------", 
                      "----------------------------------------\n";
    print REPORTADMIN "Insgesamt KÖNNEN ", "$schueler_zuversetzen_anzahl", 
                      " Schüler versetzt werden.\n";

    print REPORTADMIN "\n\n";
    print REPORTADMIN "########################################",
                      "########################################\n";
    print REPORTADMIN "Sie sollten sophomorix-teach-in aufrufen!\n";
    print REPORTADMIN "########################################",
                      "########################################";
    print REPORTADMIN "\n";

    if (defined $auto_class_rename[0]){
        print REPORTADMIN "\n\n";
        print REPORTADMIN "########################################",
                          "########################################\n";
        print REPORTADMIN "Sie sollten folgende Klassen in ",
                          "/etc/sophomorix/user/class.map\n";
        print REPORTADMIN "umbenennen, da sie nur Ziffern enthalten:\n";
        print REPORTADMIN "  @auto_class_rename\n";

        print REPORTADMIN "########################################",
                          "########################################";
        print REPORTADMIN "\n";
    }
    close(REPORTADMIN);
}


sub generate_class_user_count_file {
    # Klasse-Schueleranzahl-Datei
    open(KLASSESCHUELER,">${DevelConf::ergebnis_pfad}/AdminClass.students") 
                        || die "Fehler: $!";
    while (($klass,$sch_zahl) = each %klasse_schueleranzahl){
        print KLASSESCHUELER $klass , ";" , $sch_zahl ,";\n";
    }         
    close(KLASSESCHUELER);
}





sub close_report_files {
    # ===========================================================================
    # Close the report-files 
    # ===========================================================================

}





# ===========================================================================
# Erlaubte Zeichen für das Datum erzeugen
sub year_ok {
# erlaubte Zeichen für die Tage
%convert_days = qw(
     1     01          01    01
     2     02          02    02
     3     03          03    03
     4     04          04    04
     5     05          05    05
     6     06          06    06
     7     07          07    07
     8     08          08    08
     9     09          09    09
     10    10          11    11
     12    12          13    13
     14    14          15    15
     16    16          17    17
     18    18          19    19
     20    20          21    21
     22    22          23    23
     24    24          25    25
     26    26          27    27
     28    28          29    29
     30    30          31    31
);

# erlaubte Zeichen für die Monate
%convert_month = qw(
     1     01          01    01
     2     02          02    02
     3     03          03    03
     4     04          04    04
     5     05          05    05
     6     06          06    06
     7     07          07    07
     8     08          08    08
     9     09          09    09
     10    10          11    11
     12    12
);



# erlaubte zeichen für die Jahreszahl in ein Hash schreiben
$geburts_jahreszahl_lauf=$Conf::geburts_jahreszahl_start;
#print "Erlaubte Geburts-Jahreszahlen sind:\n";
while ($geburts_jahreszahl_lauf<=$Conf::geburts_jahreszahl_stop){
    # 19xx
    $geburts_jahreszahl_erlaubt{$geburts_jahreszahl_lauf}
     =$geburts_jahreszahl_lauf;
    #print $geburts_jahreszahl_lauf, 
    #      " ist ", 
    #      $geburts_jahreszahl_erlaubt{$geburts_jahreszahl_lauf}, 
    #      "\n";
    # xx 
    $geburts_jahreszahl_erlaubt{substr($geburts_jahreszahl_lauf,2)}
    = $geburts_jahreszahl_lauf;
    #print (substr($geburts_jahreszahl_lauf,2), 
    #      " ist auch erlaubt\n"); 
    $geburts_jahreszahl_lauf++;
}
}




sub generate_add_file {
    my $identifier = "";
    my $admin_class = "";
    my $string="";
    my $login="";
    my $password="";
    my $id="";
    my $gid="";
    my $unid="";
    open(SOPHOMORIXADD, 
          ">${DevelConf::ergebnis_pfad}/sophomorix.add") 
         || die "Fehler: $!";

    &titel("generate_add_file: Looking for users to be added:");

    while ( ($identifier, $admin_class) = each %schueler_ok_hash ){
      unless (exists $schueler_im_system_hash{$identifier}) {
	 if ($old_info_dir eq ""){
            # Dont use old data
            $string="$identifier"."::";
            if (exists $schueler_ok_wunsch_login_hash{$identifier}){
                $string=$string.
                  "$schueler_ok_wunsch_login_hash{$identifier}"."::";
                $login=$schueler_ok_wunsch_login_hash{$identifier}
            } else {
                $string=$string."---"."::";
            }

            if (exists $schueler_ok_wunsch_passwort_hash{$identifier}){
                $string=$string.
                  "$schueler_ok_wunsch_passwort_hash{$identifier}"."::";
            } else {
                $string=$string."---"."::";
            }
            $string=$string."---"."::"."---"."::";

	 } else {
            # Use old data
            # in Kleinbuchstaben umwandeln

            # save this for later use
  	    my $identifier_capital=$identifier;

            # only in 2.x ???
         #   $identifier=~tr/A-Z/a-z/;
         #   $identifier=~s/-//g;

	    print "Looking for $identifier \n";
            if (exists $new_id_old_id{$identifier}){
		print "Old identifier found (Using it!)\n";
                $identifier=$new_id_old_id{$identifier};
            }
	    print "Looking for $identifier \n";

	    if (exists $old_id_login{$identifier}){
		$login=$old_id_login{$identifier};
            } else {
                $login="---";
            }

	    if (exists $old_id_password{$identifier}){
		$password=$old_id_password{$identifier};
            } else {
                $password="---";
            }

	    if (exists $old_id_id{$identifier} and $use_uid==1){
		$id=$old_id_id{$identifier};
            } else {
                $id="---";
            }

	    if (exists $old_gname_gid{$admin_class} and $use_gid==1){
		$gid=$old_gname_gid{$admin_class};
            } else {
                $gid="---";
            }

            if (exists $schueler_ok_unid{$identifier}){
                $unid=$schueler_ok_unid{$identifier};
            } else {
                $unid="";
            }

            # use identifier with capital letters for sophomorix.add
            $string="$identifier_capital"."::".
                    "$login"."::".
                    "$password"."::".
                    "$id"."::".
                    "$gid"."::";
         }

         # use old data or not: append unid
         if (exists $schueler_ok_unid{$identifier}){
              $unid=$schueler_ok_unid{$identifier};
         } else {
                $unid="";
         }

	 $string=$string."$unid"."\n";
         print "  $string";

         # check if login is not forbidden
         if (not exists $wunsch_login_forbidden{$login}){
            # loginname is allowed
            push @admin_list_add, "$string";
            # check if class must be renamed
            if ($admin_class=~/[^0-9]/){
                  # adminclass is string, nothing to do
            } else {
		$admin_class=&rename_admin_class($admin_class);
            }
            # Put admin_class and string in file
            print SOPHOMORIXADD ("$admin_class"."::"."$string");
            $schueler_hinzu_anzahl++;
	} else {
            # loginname is forbidden
            push @admin_list_add, "*$login is forbidden*$string";
            print "$login as loginname is forbidden (used more than once)\n";
        }
      }
    }

    &titel("generate_add_file: $schueler_hinzu_anzahl users can be added.");
    close(SOPHOMORIXADD);
}





sub get_action_date {
    # Parameter 1: identifier
    # Parameter 2: type (deactivation, kill)
    my ($identifier, $type) = @_;
    my $action_epoche;
    my $usertype="";
    my $days;
    my $last_action;
    my ($l_day, $l_month, $l_year)=(0, 0, 0);
    my $sequenztype="";
    if ($type eq "deactivation") {
       $last_action = $user_duldungsbeginn{$identifier};
       ($l_day, $l_month, $l_year) = split(/\./, $last_action);
       $sequenztype="TolerationDate";

       if ($schueler_im_system_hash{$identifier} eq ${DevelConf::teacher} ||
           $schueler_im_system_exit_admin_class{$identifier} 
           eq ${DevelConf::teacher} ) {
           # Falls Lehrer
           $usertype="Lehrer/in";
           $days=$Conf::lehrer_duldung_tage;
       } else {
           # Falls Schüler
           $usertype="Schueler/in";
           $days=$Conf::schueler_duldung_tage;
       }

    } elsif ($type eq "kill") {
       $last_action = $user_deaktivierungsbeginn{$identifier};
       ($l_day, $l_month, $l_year) = split(/\./, $last_action);
       $sequenztype="DeactivationDate";
       if ($schueler_im_system_hash{$identifier} eq ${DevelConf::teacher} ||
           $schueler_im_system_exit_admin_class{$identifier} 
           eq ${DevelConf::teacher} ) {
           # Falls Lehrer
           $usertype="Lehrer/in";
           $days=$Conf::lehrer_deaktivierung_tage;
       } else {
           # Falls Schüler
           $usertype="Schueler/in";
           $days=$Conf::schueler_deaktivierung_tage;
       }
    } else {
#        print "Error fetching sequenz_date! \n";
#        print "Unknown Parameter $type! \n";
        &log_script_exit("ERROR: fetching sequenz_date!",1,1,0,@arguments);
        #exit;
    }
    $l_month=$l_month-1;
    $l_year=$l_year-1900;
    $action_epoche=timelocal(0, 0, 0, $l_day , $l_month, $l_year);
    $action_epoche=$action_epoche+$days*86400;
       if($Conf::log_level>=3){
          print  "\n";
          printf "%-20s %3s\n","$usertype","$identifier";
          printf "%-20s %3s\n","Klasse","$schueler_im_system_hash{$identifier}";
          printf "%-20s %3s\n","$sequenztype:","$last_action";
          print  "\n";
          printf "%-20s %3s\n", "Checking for:", "$type";
          printf "%-20s %3s\n","$type in:","$days days after $last_action";
          printf "%-20s %-15s %3s\n",
                 "Today is:","$epoche_jetzt", &zeit("$epoche_jetzt");
          printf "%-20s %-15s %3s\n","$type starts:",
                 "$action_epoche", &zeit("$action_epoche");
       }
    if ($action_epoche <  $epoche_jetzt) {
        # do something
        if($Conf::log_level>=3){
           printf "%-20s %3s\n", "Take this Action:", "$type";
        }
        return $action_epoche;
    } else {
        # do nothing
        if($Conf::log_level>=3){
           print "Don't do anything ...\n";
        }
        return 0;
    }
}




sub show_all_hashes {
if($Conf::log_level>=3){
   &linie();
   &titel("This is the contents of the hashes");
   &linie();

   # filename_map
   print("Filename Map (filename_map):\n");
   &linie();
   while (($k,$v) = each %filename_map){
     printf "%-60s %3s\n","$k","$v";
   }
   &linie();



   # schueler_ok-hash
   print("User in sophomorix.ok (schueler_ok_hash):",
         "                    AdminClass:\n");
   &linie();
   while (($k,$v) = each %schueler_ok_hash){
     printf "%-60s %3s\n","$k","$v";
   }
   &linie();



   # Print all users in the system
   print("Schüler im System:                   ",
         "                        AdminClass:\n");
   &linie();
   while (($k,$v) = each %schueler_im_system_hash){
     printf "%-60s %3s\n","$k","$v";
   }
   &linie();



   # Print all users in the system and loginname
   print("Schüler im System:                   ",
         "                        Login:\n");
   &linie();
   while (($k,$v) = each %schueler_im_system_loginname){
     printf "%-60s %3s\n","$k","$v";
   }
   &linie();



   # Print all users in the system and subclass
   print("Schüler im System:                   ",
         "                        SubClass:\n");
   &linie();
   while (($k,$v) = each %schueler_subklassen){
     printf "%-60s %3s\n","$k","$v";
   }
   &linie();



   # Print all users in the files - Wunschlogin
   print("User in sophomorix.ok (schueler_ok_wunsch_login_hash):",
         "       Wunsch-Logins:\n");
   &linie();
   while (($k,$v) = each %schueler_ok_wunsch_login_hash){
     printf "%-60s %3s\n","$k","$v";
   }
   &linie();


   # Print all users in the files - Password
   print("User in sophomorix.ok (schueler_ok_hash):",
         "                    Wunsch-Passwörter:\n");
   &linie();
   while (($k,$v) = each %schueler_ok_wunsch_passwort_hash){
     printf "%-60s %3s\n","$k","$v";
   }
   &linie();



   # Print Unids and identifier in the system
   print("UNID im System (unid_ok_system):",
         "         Identifier:\n");
   &linie();
   while (($k,$v) = each %unid_ok_system){
     printf "%-40s %-30s\n","$k","$v";
   }
   &linie();



   # Print Unids and identifier in the files
   print("UNID in files (unid_ok_file):",
         "            Identifier:\n");
   &linie();
   while (($k,$v) = each %unid_ok_file){
     printf "%-40s %-30s\n","$k","$v";
   }
   &linie();



   # print alle Status-Information 
   print("Identifier (sophomorix_status): ",
         "         Status:\n");
   &linie();
   while (($k,$v) = each %sophomorix_status){
     printf "%-40s %-3s\n","$k","$v";
   }
   &linie();


   # print alle Duldungs-Data 
   print("Identifier (user_duldungsbeginn): ",
         "         TolerationDate:\n");
   &linie();
   while (($k,$v) = each %user_duldungsbeginn){
     printf "%-40s %-3s\n","$k","$v";
   }
   &linie();


   # print alle Deaktivierungs-Data 
   print("Identifier (user_deaktivierungsbeginn): ",
         "         DeactivationDate:\n");
   &linie();
   while (($k,$v) = each %user_deaktivierungsbeginn){
     printf "%-40s %-3s\n","$k","$v";
   }
   &linie();

   # print scheduled-toleration
   print("Identifier (identifier_sc_tol): ",
         "         ScheduledToleration:\n");
   &linie();
   while (($k,$v) = each %identifier_sc_tol){
     printf "%-40s %-3s\n","$k","$v";
   }
   &linie();


   # print ExitAdminClass of system-users
   print("schueler_im_system_exit_admin_class:",
         "     ExitAdminClass:\n");
   &linie();
   while (($k,$v) = each %schueler_im_system_exit_admin_class){
     printf "%-40s %-12s\n","$k","$v";
   }
   &linie();

   # print AccountType of system users
   print("schueler_im_system_account_type:",
         "         AccountType:\n");
   &linie();
   while (($k,$v) = each %schueler_im_system_account_type){
     printf "%-40s %-3s\n","$k","$v";
   }
   &linie();

   # print usertoken of system users
   print("identifier_sys_usertoken:",
         "         Usertoken:\n");
   &linie();
   while (($k,$v) = each %identifier_sys_usertoken){
     printf "%-40s %-3s\n","$k","$v";
   }
   &linie();

   # print origlines of system users
   print("identifier_origline \n");
   &linie();
   while (($k,$v) = each %identifier_origline){
       print "   Identifier: $k\n";
       print "   Origline:   $v\n";
   }
   &linie();

   # print Forbidden Wish-Logins
   print("Forbidden Wish-Logins:",
         "         Number:\n");
   &linie();
   while (($k,$v) = each %wunsch_login_forbidden){
     printf "%-40s %-3s\n","$k","$v";
   }
   &linie();
   &titel("End of all Hashes");
   &linie();
 }# end loglevel
}


sub add_first_match {
    # identifiy additional Firstname in first -> (peter hanspeter)
    my $go=shift @_;
    my $come=shift@_;
    my $max_points=90;
    # small First is amatch in the large
    if (amatch($_[0], ["$_[8]"], $_[1])
       and amatch($_[3], ["$_[9]"], $_[2])
       and ( $_[2]=~/^$_[3]/ or $_[2]=~/$_[3]$/ ) 
       and amatch($_[4], ["$_[10]"], $_[5])
       ){
       my $ed_dist_points=$max_points-15*abs( adist($_[0],$_[1]))-15*abs( adist($_[4],$_[5])) ;
       if($Conf::log_level>=3){
          print "### add_first_match: \n",
                "    Name  $_[0] and $_[1] have less than $_[8] distance\n",
                "    First $_[3] is found in the beginning/end of $_[2]\n",
                "    Birth $_[4] and $_[5] have less than $_[10] distance\n";
       }
       &better_match($go,$come,$ed_dist_points);
    } else {
       if($Conf::log_level>=3){
          print "### add_first_match:         No match!\n",
       }
    }

}





sub multi_change_match {
    # identifiy small changes in EACH of name,first,birth
    my $go=shift @_;
    my $come=shift@_;
    my $max_points=100;
    if (amatch($_[0], ["$_[8]"], $_[1])
       and amatch($_[2], ["$_[9]"], $_[3])
       and amatch($_[4], ["$_[10]"], $_[5])
       ){
       my $ed_dist_points=$max_points-10*abs( adist($_[0],$_[1]))-10*abs( adist($_[2],$_[3]))-10*abs( adist($_[4],$_[5])) ;
       if($Conf::log_level>=3){
          print "### multi_change_match: \n",
                "   $_[0] and $_[1] are similar! (Limit: $_[8])\n",
                "   $_[2] and $_[3] are similar! (Limit: $_[9])\n",
                "   $_[4] and $_[5] are similar! (Limit: $_[10])\n",
                "   ( => $ed_dist_points Points)\n";
       }
       &better_match($go,$come,$ed_dist_points);
    } else {
       if($Conf::log_level>=3){
          print "### multi_change_match:      No match!\n",
       }
    }
}



sub single_change_match {
    # identifiy small changes in ONE of name,first,birth
    my $go=shift @_;
    my $come=shift@_;
    my $max_points=101;
    # 0,1 name
    # 2,3 first
    # 4,5 birth
    # 6,7 class
    # 8,9,10 %match_name,%match_first,%match_birth
    if (amatch($_[0], ["$_[8]"], $_[1])
       and $_[2] eq $_[3]
       and $_[4] eq $_[5]
       ){
       my $ed_dist_points=$max_points-abs( adist($_[0],$_[1])*10 );
       if($Conf::log_level>=3){
          print "###  single_change_match:    $_[0] and $_[1] are similar!\n",
                "     ( => $ed_dist_points Points)\n";
       }
       &better_match($go,$come,$ed_dist_points);
    }elsif ($_[0] eq $_[1]
       and amatch($_[2], ["$_[9]"], $_[3])
       and $_[4] eq $_[5]
       ){
       my $ed_dist_points=$max_points-abs( adist($_[2],$_[3])*10 );
       if($Conf::log_level>=3){
          print "###  single_change_match:    $_[2] and $_[3] are similar!\n",
                "     ( => $ed_dist_points Points)\n";
       }
       &better_match($go,$come,$ed_dist_points);
    } elsif ($_[0] eq $_[1]
       and $_[2] eq $_[3]
       and amatch($_[4], ["$_[10]"], $_[5])
       ){
       my $ed_dist_points=$max_points-abs( adist($_[4],$_[5])*10 );
       if($Conf::log_level>=3){
          print "###  single_change_match:    $_[4] and $_[5] are similar!\n",
                "     ( => $ed_dist_points Points)\n";
       }
       &better_match($go,$come,$ed_dist_points);
    } elsif ($_[2] eq $_[3]
       and $_[4] eq  $_[5]
       ){
       my $ed_dist_points=77; # marriage
       if($Conf::log_level>=3){
          print "###  single_change_match(marriage): $_[0] / $_[1] has married\n",
                "     ( => $ed_dist_points Points)\n";
       }
       &better_match($go,$come,$ed_dist_points);
    } else {
       if($Conf::log_level>=3){
          print "### single_change_match:     No match!\n",
       }
    }
}



sub better_match {
    my ($go, $come, $points)=@_;
    my $old_points=0;
    # hat $come schon Punkte
    if (exists $go_come{$go}){
        # get user that matches
        my $user=$go_come{$go};
	$old_points=$come_points{$user}
    }
    if($Conf::log_level>=3){
       print "### $come  has $old_points points, can get $points points.\n"; 
    }
    if ($points > $old_points){
        if($Conf::log_level>=3){
           print "### Updating: $come -> $go ($points pts) \n";
        }
	$go_come{$go}=$come;
        $come_points{$come}=$points;
    } else {
        if($Conf::log_level>=3){
           print "### Not updating: Not enough points.\n";
        }
    }
}





sub filter_school_admin_software {
     my ($line,$encoding) = @_;

     # ascii (immer filtern)
     # '
     $line=~s/\x27//g;
     $line=~s/\x60//g;
     # -
     $line=~s/\x2D/-/g;
     $line=~s/\x5F/-/g;


     # nur win1252
     if ($encoding eq "win1252"){
         # '-Variation
         $line=~s/\x91/-/g;
         $line=~s/\x92/-/g;
         # --Variation (Bindestrich)
         $line=~s/\x96/-/g;
         $line=~s/\x97/-/g;
     }


     # nur 8859-1 und win1252
     if ($encoding eq "8859-1" or
         $encoding eq "win1252"){
         # '-Variation
         $line=~s/\xB4//g;
     }


     # file_coding: 8859-1,8859-15,win1252,utf8 
     if ($encoding eq "8859-1" or 
         $encoding eq "8859-15" or
         $encoding eq "win1252" ){
         # common in 
         # iso 8859-1
         # iso 8859-15
         # windows 1252
         # -
         $line=~s/\xAF/-/g;
         # A
         $line=~s/\xC0/A/g;
         $line=~s/\xC1/A/g;
         $line=~s/\xC2/A/g;
         $line=~s/\xC3/A/g;
         # Ae
         $line=~s/\xC4/Ae/g;
         # A
         $line=~s/\xC5/A/g;
         # Ae
         $line=~s/\xC6/Ae/g;
         # C
         $line=~s/\xC7/C/g;
         # E
         $line=~s/\xC8/E/g;
         $line=~s/\xC9/E/g;
         $line=~s/\xCA/E/g;
         $line=~s/\xCB/E/g;
         # I
         $line=~s/\xCC/I/g;
         $line=~s/\xCD/I/g;
         $line=~s/\xCE/I/g;
         $line=~s/\xCF/I/g;
         # D
         $line=~s/\xD0/D/g;
         # I
         $line=~s/\xD1/N/g;
         # O
         $line=~s/\xD2/O/g;
         $line=~s/\xD3/O/g;
         $line=~s/\xD4/O/g;
         $line=~s/\xD5/O/g;
         # Oe
         $line=~s/\xD6/Oe/g;
         # O
         $line=~s/\xD8/O/g;
         # X
         $line=~s/\xD7/X/g;
         # U
         $line=~s/\xD9/U/g;
         $line=~s/\xDA/U/g;
         $line=~s/\xDB/U/g;
         # Ue
         $line=~s/\xDC/Ue/g;
         # Y
         $line=~s/\xDD/Y/g;
         # Th
         $line=~s/\xDE/Th/g;
         # ss
         $line=~s/\xDF/ss/g;
         # a
         $line=~s/\xE0/a/g;
         $line=~s/\xE1/a/g;
         $line=~s/\xE2/a/g;
         $line=~s/\xE3/a/g;
         # ae 
         $line=~s/\xE4/ae/g;
         # a
         $line=~s/\xE5/a/g;
         # ae
         $line=~s/\xE6/ae/g;
         # c
         $line=~s/\xE7/c/g;
         # e
         $line=~s/\xE8/e/g;
         $line=~s/\xE9/e/g;
         $line=~s/\xEA/e/g;
         $line=~s/\xEB/e/g;
         # i
         $line=~s/\xEC/i/g;
         $line=~s/\xED/i/g;
         $line=~s/\xEE/i/g;
         $line=~s/\xEF/i/g;
         # d
         $line=~s/\xF0/d/g;
         # n
         $line=~s/\xF1/n/g;
         # o
         $line=~s/\xF2/o/g;
         $line=~s/\xF3/o/g;
         $line=~s/\xF4/o/g;
         $line=~s/\xF5/o/g;
         # oe
         $line=~s/\xF6/oe/g;
         # ???
         $line=~s/\xF8/o/g;
         # u
         $line=~s/\xF9/u/g;
         $line=~s/\xFA/u/g;
         $line=~s/\xFB/u/g;
         # ue
         $line=~s/\xFC/ue/g;
         # y
         $line=~s/\xFD/y/g;
         $line=~s/\xFF/y/g;
         # FE thorn
         $line=~s/\xFE/th/g;
     }


     if ($encoding eq "8859-15"){
         # additional to 8859-1
         # S
         $line=~s/\xA6/S/g;
         # s
         $line=~s/\xA8/s/g;
         # Z
         $line=~s/\xB4/Z/g;
         # z
         $line=~s/\xB8/z/g;
         # Oe
         $line=~s/\xBC/Oe/g;
         # oe
         $line=~s/\xBD/oe/g;
         # y
         $line=~s/\xBE/Y/g;
     }


     if ($encoding eq "win1252"){
         # additional to 8859-1
         # S
         $line=~s/\x8A/S/g;
         # s
         $line=~s/\x9A/s/g;
         # Z
         $line=~s/\x8E/Z/g;
         # z
         $line=~s/\x9E/z/g;
         # Oe
         $line=~s/\x8C/Oe/g;
         # oe
         $line=~s/\x9C/oe/g;
         # y
         $line=~s/\x9F/Y/g;
     }


     if ($encoding eq "utf8"){
         # utf8
         # -
         $line=~s/\xC2\xAF/-/g;
         # '
         $line=~s/\xC2\xB4//g;

         # iso 8859-1 stuff
         # A
         $line=~s/\xC3\x80/A/g;
         $line=~s/\xC3\x81/A/g;
         $line=~s/\xC3\x82/A/g;
         $line=~s/\xC3\x83/A/g;
         $line=~s/\xC3\x85/A/g;
         # Ae
         $line=~s/\xC3\x84/Ae/g;
         $line=~s/\xC3\x86/Ae/g;
         # C
         $line=~s/\xC3\x87/C/g;
         # E
         $line=~s/\xC3\x88/E/g;
         $line=~s/\xC3\x89/E/g;
         $line=~s/\xC3\x8A/E/g;
         $line=~s/\xC3\x8B/E/g;
         # I
         $line=~s/\xC3\x8C/I/g;
         $line=~s/\xC3\x8D/I/g;
         $line=~s/\xC3\x8E/I/g;
         $line=~s/\xC3\x8F/I/g;
         # D
         $line=~s/\xC3\x90/D/g;
         # I
         $line=~s/\xC3\x91/N/g;
         # O
         $line=~s/\xC3\x92/O/g;
         $line=~s/\xC3\x93/O/g;
         $line=~s/\xC3\x94/O/g;
         $line=~s/\xC3\x95/O/g;
         $line=~s/\xC3\x98/O/g;
         # Oe
         $line=~s/\xC3\x96/Oe/g;
         # X
         $line=~s/\xC3\x97/x/g;
         # U
         $line=~s/\xC3\x99/U/g;
         $line=~s/\xC3\x9A/U/g;
         $line=~s/\xC3\x9B/U/g;
         # Ue
         $line=~s/\xC3\x9C/Ue/g;
         # Y
         $line=~s/\xC3\x9D/Y/g;
         # Th
         $line=~s/\xC3\x9E/Th/g;
         # ss
         $line=~s/\xC3\x9F/ss/g;
         # a
         $line=~s/\xC3\xA0/a/g;
         $line=~s/\xC3\xA1/a/g;
         $line=~s/\xC3\xA2/a/g;
         $line=~s/\xC3\xA3/a/g;
         $line=~s/\xC3\xA5/a/g;
         # ae
         $line=~s/\xC3\xA4/ae/g;
         $line=~s/\xC3\xA6/ae/g;
         # c
         $line=~s/\xC3\xA7/c/g;
         # e
         $line=~s/\xC3\xA8/e/g;
         $line=~s/\xC3\xA9/e/g;
         $line=~s/\xC3\xAA/e/g;
         $line=~s/\xC3\xAB/e/g;
         # i
         $line=~s/\xC3\xAC/i/g;
         $line=~s/\xC3\xAD/i/g;
         $line=~s/\xC3\xAE/i/g;
         $line=~s/\xC3\xAF/i/g;
         # d
         $line=~s/\xC3\xB0/d/g;
         # n
         $line=~s/\xC3\xB1/n/g;
         # o
         $line=~s/\xC3\xB2/o/g;
         $line=~s/\xC3\xB3/o/g;
         $line=~s/\xC3\xB4/o/g;
         $line=~s/\xC3\xB5/o/g;
         # \xC3\xB7 is DIVISION SIGN
         # o
         $line=~s/\xC3\xB8/o/g;
         # oe
         $line=~s/\xC3\xB6/oe/g;
         # u
         $line=~s/\xC3\xB9/u/g;
         $line=~s/\xC3\xBA/u/g;
         $line=~s/\xC3\xBB/u/g;
         # ue
         $line=~s/\xC3\xBC/ue/g;
         # y
         $line=~s/\xC3\xBD/y/g;
         $line=~s/\xC3\xBF/y/g;
         # FE thorn
         $line=~s/\xC3\xBE/th/g;
         # iso 8859-1 stuff end
      
         # \xc4 stuff (U+0100)
         $line=~s/\xC4\x80/A/g;
         $line=~s/\xC4\x81/a/g;
         $line=~s/\xC4\x82/A/g;
         $line=~s/\xC4\x83/a/g;
         $line=~s/\xC4\x84/A/g;
         $line=~s/\xC4\x85/a/g;
         $line=~s/\xC4\x86/C/g;
         $line=~s/\xC4\x87/c/g;
         $line=~s/\xC4\x88/C/g;
         $line=~s/\xC4\x89/c/g;
         $line=~s/\xC4\x8A/C/g;
         $line=~s/\xC4\x8B/c/g;
         $line=~s/\xC4\x8C/C/g;
         $line=~s/\xC4\x8D/c/g;
         $line=~s/\xC4\x8E/D/g;
         $line=~s/\xC4\x8F/d/g;
         $line=~s/\xC4\x90/D/g;
         $line=~s/\xC4\x91/d/g;
         $line=~s/\xC4\x92/E/g;
         $line=~s/\xC4\x93/e/g;
         $line=~s/\xC4\x94/E/g;
         $line=~s/\xC4\x95/e/g;
         $line=~s/\xC4\x96/E/g;
         $line=~s/\xC4\x97/e/g;
         $line=~s/\xC4\x98/E/g;
         $line=~s/\xC4\x99/e/g;
         $line=~s/\xC4\x9A/E/g;
         $line=~s/\xC4\x9B/e/g;
         $line=~s/\xC4\x9C/G/g;
         $line=~s/\xC4\x9D/g/g;
         $line=~s/\xC4\x9E/G/g;
         $line=~s/\xC4\x9F/g/g;
         $line=~s/\xC4\xA0/G/g;
         $line=~s/\xC4\xA1/g/g;
         $line=~s/\xC4\xA2/G/g;
         $line=~s/\xC4\xA3/g/g;
         $line=~s/\xC4\xA4/H/g;
         $line=~s/\xC4\xA5/h/g;
         $line=~s/\xC4\xA6/H/g;
         $line=~s/\xC4\xA7/h/g;
         $line=~s/\xC4\xA8/I/g;
         $line=~s/\xC4\xA9/i/g;
         $line=~s/\xC4\xAA/I/g;
         $line=~s/\xC4\xAB/i/g;
         $line=~s/\xC4\xAC/I/g;
         $line=~s/\xC4\xAD/i/g;
         $line=~s/\xC4\xAE/I/g;
         $line=~s/\xC4\xAF/i/g;
         $line=~s/\xC4\xB0/I/g;
         $line=~s/\xC4\xB1/i/g;
         $line=~s/\xC4\xB2/Ij/g;
         $line=~s/\xC4\xB3/ij/g;
         $line=~s/\xC4\xB4/J/g;
         $line=~s/\xC4\xB5/j/g;
         $line=~s/\xC4\xB6/K/g;
         $line=~s/\xC4\xB7/k/g;
         $line=~s/\xC4\xB8/k/g;
         $line=~s/\xC4\xB9/L/g;
         $line=~s/\xC4\xBA/l/g;
         $line=~s/\xC4\xBB/L/g;
         $line=~s/\xC4\xBC/l/g;
         $line=~s/\xC4\xBD/L/g;
         $line=~s/\xC4\xBE/l/g;
         $line=~s/\xC4\xBF/L/g;

         # \xc5 stuff (U+0140)
         $line=~s/\xC5\x80/l/g;
         $line=~s/\xC5\x81/L/g;
         $line=~s/\xC5\x82/l/g;

         $line=~s/\xC5\x83/N/g;
         $line=~s/\xC5\x84/n/g;
         $line=~s/\xC5\x85/N/g;
         $line=~s/\xC5\x86/n/g;
         $line=~s/\xC5\x87/N/g;
         $line=~s/\xC5\x88/n/g;
         $line=~s/\xC5\x89/n/g;
         $line=~s/\xC5\x8A/N/g;
         $line=~s/\xC5\x8B/n/g;
         $line=~s/\xC5\x8C/O/g;
         $line=~s/\xC5\x8D/o/g;
         $line=~s/\xC5\x8E/O/g;
         $line=~s/\xC5\x8F/o/g;
         $line=~s/\xC5\x90/O/g;
         $line=~s/\xC5\x91/o/g;
         $line=~s/\xC5\x92/Oe/g;
         $line=~s/\xC5\x93/oe/g;
         $line=~s/\xC5\x94/R/g;
         $line=~s/\xC5\x95/r/g;
         $line=~s/\xC5\x96/R/g;
         $line=~s/\xC5\x97/r/g;
         $line=~s/\xC5\x98/R/g;
         $line=~s/\xC5\x99/r/g;
         $line=~s/\xC5\x9A/S/g;
         $line=~s/\xC5\x9B/s/g;
         $line=~s/\xC5\x9C/S/g;
         $line=~s/\xC5\x9D/s/g;
         $line=~s/\xC5\x9E/S/g;
         $line=~s/\xC5\x9F/s/g;
         $line=~s/\xC5\xA0/S/g;
         $line=~s/\xC5\xA1/s/g;
         $line=~s/\xC5\xA2/T/g;
         $line=~s/\xC5\xA3/t/g;
         $line=~s/\xC5\xA4/T/g;
         $line=~s/\xC5\xA5/t/g;
         $line=~s/\xC5\xA6/T/g;
         $line=~s/\xC5\xA7/t/g;
         $line=~s/\xC5\xA8/U/g;
         $line=~s/\xC5\xA9/u/g;
         $line=~s/\xC5\xAA/U/g;
         $line=~s/\xC5\xAB/u/g;
         $line=~s/\xC5\xAC/U/g;
         $line=~s/\xC5\xAD/u/g;
         $line=~s/\xC5\xAE/U/g;
         $line=~s/\xC5\xAF/u/g;
         $line=~s/\xC5\xB0/U/g;
         $line=~s/\xC5\xB1/ue/g;
         $line=~s/\xC5\xB2/U/g;
         $line=~s/\xC5\xB3/u/g;
         $line=~s/\xC5\xB4/W/g;
         $line=~s/\xC5\xB5/w/g;
         $line=~s/\xC5\xB6/Y/g;
         $line=~s/\xC5\xB7/y/g;
         $line=~s/\xC5\xB8/Y/g;
         $line=~s/\xC5\xB9/Z/g;
         $line=~s/\xC5\xBA/z/g;
         $line=~s/\xC5\xBB/Z/g;
         $line=~s/\xC5\xBC/z/g;
         $line=~s/\xC5\xBD/Z/g;
         $line=~s/\xC5\xBE/z/g;
         $line=~s/\xC5\xBF/s/g;
         $line=~s/\xC6\x80/b/g;
         $line=~s/\xC6\x81/B/g;
         $line=~s/\xC6\x82/B/g;
         $line=~s/\xC6\x83/b/g;
         # not a letter
         $line=~s/\xC6\x84/6/g;
         # not a letter
         $line=~s/\xC6\x85/6/g;
         $line=~s/\xC6\x86/O/g;
         $line=~s/\xC6\x87/C/g;
         $line=~s/\xC6\x88/c/g;
         $line=~s/\xC6\x89/D/g;
         $line=~s/\xC6\x8A/D/g;
         $line=~s/\xC6\x8B/D/g;
         $line=~s/\xC6\x8C/d/g;
         $line=~s/\xC6\x8D/d/g;
         $line=~s/\xC6\x8E/E/g;
         # grosses Schwa
         $line=~s/\xC6\x8F/E/g;

         # ?????? continue here

     }
     return $line;
}


sub reading_firstnames {
#    my @datafiles=("../data/firstnames.utf8.txt", 
#                   "../data/firstnames.ISO_8859-1.txt", 
#                  );
    my @datafiles=("firstnames.utf8.txt", 
                   "firstnames.ISO_8859-1.txt", 
                  );
    foreach my $file_rel (@datafiles){
        my $file_abs=${DevelConf::encoding_data_pfad}."/".$file_rel;
        my @list = split(/\//,$file_abs);
        my $filename = pop @list;
        my ($string1,$enc,$string2)=split(/\./,$filename); 
        open(DATAFILE, "$file_abs") || 
             die "Fehler: $! $file_abs nicht gefunden!"; 
        while (<DATAFILE>){
            chomp();
            s/^ //g; # Leerzeichen am Zeilenangfang entfernen
            if(/^\#/){ # # am Anfang bedeutet Kommentarzeile
               next;
	    }
            if($_ eq ""){
                next;
            }
            my ($first,$first_new) = split(/:/);
            $firstnames_data{$enc}{$first}=0;
        }
        if($Conf::log_level>=3){
            print "   Reading $file_abs for encoding: $enc\n";
        }
        close(DATAFILE);
    }
}


sub reading_firstname_errors {
    my @datafiles=("firstname_errors.utf8.txt", 
                   "firstname_errors.ISO_8859-1.txt", 
                  );
    foreach my $file_rel (@datafiles){
        my $file_abs=${DevelConf::encoding_data_pfad}."/".$file_rel;
        my @list = split(/\//,$file_abs);
        my $filename = pop @list;
        my ($string1,$enc,$string2)=split(/\./,$filename); 
        open(DATAFILE, "$file_abs") || 
             die "Fehler: $! $file_abs nicht gefunden!"; 
        while (<DATAFILE>){
            chomp();
            s/^ //g; # Leerzeichen am Zeilenangfang entfernen
            if(/^\#/){ # # am Anfang bedeutet Kommentarzeile
               next;
	    }
            if($_ eq ""){
                next;
            }
            my ($error,$message) = split(/:/);
            $message=~s/^\s+ //g;
            $firstnames_errors{$enc}{$error}=$message;
        }
        if($Conf::log_level>=3){
            print "   Reading $file_abs for errors: $enc\n";
        }
        close(DATAFILE);
    }
}



sub analyze_encoding {
    my ($file) = @_;
    # set all result counters to 0
    foreach my $enc (@encodings_to_check){
        $encoding_check_results{$file}{'firstnames'}{'count_hits'}{$enc}=0;
        $encoding_check_results{$file}{'firstnames'}{'count_errors'}{$enc}=0;
    }
    $encoding_check_results{$file}{'firstnames'}{'count_hits'}{'none'}=0;
    $encoding_check_results{$file}{'firstnames'}{'count_errors'}{'none'}=0;
    $encoding_check_results{$file}{'firstnames'}{'result'}="unknown";
  

    # start to analyze file
    print "Analyzing $file ...\n";
    open(DATAFILE, "$file") || 
         die "Fehler: $! $file nicht gefunden!"; 
    while (<DATAFILE>){
        chomp();
        if ($_ eq ""){
            next;
        }
        my ($class,$surname,$firstname,$date) = split(/;/);
        # split firstname-field into single firstnames
        # split at 'space' and '-'
        my @firstnames=split(/[ ,-]/, $firstname);
        foreach my $first (@firstnames){
            if ($first=~/[^a-zA-Z\-]/) { 
                # continue with non-standard(~non-ascii) chars
                my $hit_count=0;   
                my $error_count=0; 
                foreach my $enc (@encodings_to_check){
                    my $conv = Text::Iconv->new($enc,"utf8");
                    my $first_utf8 = $conv->convert($first);

                    # check for positive hits (known, valid firstnames)
                    if (exists $firstnames_data{$enc}{$first}){
                        # remember hits
	                push @{ $encoding_check_results{$file}{'firstnames'}{'data_hits'} }, 
                                { first => "$first", 
                                  first_utf8 => "$first_utf8",
                                  line => "$_"};
                        # count hits
                        my $old=$encoding_check_results{$file}{'firstnames'}{'count_hits'}{$enc};
                        my $new=$old+1;
                        $encoding_check_results{$file}{'firstnames'}{'count_hits'}{$enc}=$new;
                        $hit_count++;
                    }
                    # check for errors
                    if (exists $firstnames_errors{$enc}{$first}){
                        # remember errors
	                push @{ $encoding_check_results{$file}{'firstnames'}{'data_errors'} }, 
                                { first => "$first", 
                                  first_utf8 => "$first_utf8",
                                  line => "$_"};
                        # count errors
                        my $old=$encoding_check_results{$file}{'firstnames'}{'count_errors'}{$enc};
                        my $new=$old+1;
                        $encoding_check_results{$file}{'firstnames'}{'count_errors'}{$enc}=$new;
                        $hit_count++;
                    }
                }
                # non-hits and non-errors (unknown firstnames) 
                if ($hit_count==0 and $error_count==0){
                    # remember unknown names
                    push @{ $encoding_check_results{$file}{'firstnames'}{'data_unknown'} }, 
                           { first => "$first", 
                             line => "$_"};
                    # count unknown names
                    my $old=$encoding_check_results{$file}{'firstnames'}{'count_hits'}{'none'};
                    my $new=$old+1;
                    $encoding_check_results{$file}{'firstnames'}{'count_hits'}{'none'}=$new;
                }
            }
        }
    }

    print "\nEncoding check result for:\n";
    print "   $file\n";

    # calculate sum
    my $oldsum=0;
    foreach my $enc (@encodings_to_check){
        my $sum=$encoding_check_results{$file}{'firstnames'}{'count_hits'}{$enc}+
	        $encoding_check_results{$file}{'firstnames'}{'count_errors'}{$enc};
        $encoding_check_results{$file}{'firstnames'}{'count_sum'}{$enc}=$sum;
        if($sum > $oldsum){
            $encoding_check_results{$file}{'firstnames'}{'result'}=$enc;
        }
    }

    # print valid firstnames
    if($Conf::log_level>=2){
    print "\nValid firstnames: ",
          "($encoding_check_results{$file}{'firstnames'}{'result'} ---> utf8)\n";
    print "==========================================================================\n";
    foreach my $item ( @{ $encoding_check_results{$file}{'firstnames'}{'data_hits'} } ){
        printf  "%-20s %-12s %-20s\n",
                $item->{first},
                "--->",
                $item->{first_utf8};
    }
    print "--------------------------------------------------------------------------\n";
    }

    # print unknown firstnames
    print "\n";
    print "Unknown firstnames (Please report to info\@linuxmuster.net):\n";
    print "+------------------------------------------+\n";
    foreach my $item ( @{ $encoding_check_results{$file}{'firstnames'}{'data_unknown'} } ){
        printf  "| %-40s |\n",
                $item->{first},
    }
    print "+------------------------------------------+\n";


    #  print firstnames with errors
    print "\nFirstnames that should be an error (Please report the the School Office):\n";
    print "+---------------------------------------------------------------------------+\n";
    foreach my $item ( @{ $encoding_check_results{$file}{'firstnames'}{'data_errors'} } ){
        printf  "| %-15s%-60s|\n",
                $item->{first_utf8},
                $item->{line};
        my $enc_result=$encoding_check_results{$file}{'firstnames'}{'result'};
        printf  "|          ---> %-60s|\n",$firstnames_errors{$enc_result}{ $item->{first} };
        print "+---------------------------------------------------------------------------+\n";
    }



    # print debug dump
    if($Conf::log_level>=3){
        print "Dump of \%encoding_check_results:\n";
        print Dumper(\%encoding_check_results);
    }


    # Print Result
    print "\n";
    print "+---------------------+--------+--------+--------+\n";
    printf  "| %-20s|%7s |%7s |%7s |\n",
            "Tested Encodings:",
            "Hits",
            "Errors",
            "Sum";
    print "+---------------------+--------+--------+--------+\n";
    foreach my $enc (@encodings_to_check){
        printf  "| %-20s|%7s |%7s |%7s |\n",
                $enc,
                $encoding_check_results{$file}{'firstnames'}{'count_hits'}{$enc},
                $encoding_check_results{$file}{'firstnames'}{'count_errors'}{$enc},
   	        $encoding_check_results{$file}{'firstnames'}{'count_sum'}{$enc};
    }
    print "+---------------------+--------+--------+--------+\n";
    printf  "| %-20s|%7s |%7s |%7s |\n",
            "none",
            $encoding_check_results{$file}{'firstnames'}{'count_hits'}{'none'},
            "",
            "";
    print "+---------------------+--------+--------+--------+\n";
    print "$file:\n",
          "    File-Encoding is $encoding_check_results{$file}{'firstnames'}{'result'}\n"; 
   close(DATAFILE);
}



sub filter_school_admin_software_old {
     my ($line) = @_;
     # Todo: Variable with string in user.conf $admin_software="splan";
     # open splan.filter and Read the following lines from there
     # SPLAN-Sonderzeichen BEGINN
     #evtl recode ibmpc..lat1
     # Falsches ' in richtiges umwandeln
     $line=~s/`/'/g;

     # ü umwandeln
     if ($DevelConf::allow_umlaut eq "no"){
           # ü
           $line=~s/ü/ue/g;
           $line=~s/\201/ue/g;
           # Ü
           $line=~s/Ü/Ue/g;
           $line=~s/\232/Ue/g;
           # ö
           $line=~s/ö/oe/g;
           $line=~s/\224/oe/g;
           # Ö
           $line=~s/Ö/Oe/g;
           $line=~s/\231/Oe/g;
           # ä
           $line=~s/ä/ae/g;
           $line=~s/\204/ae/g;
           # Ä
           $line=~s/Ä/Ae/g;
           # ß
           $line=~s/ß/ss/g;
           $line=~s/341/ss/g;
           #$line=~s/á/ss/g;
     } else {
           # ü
           $line=~s/\201/ü/g;
           # Ü
           $line=~s/\232/Ü/g;
           # ö
           $line=~s/\224/ö/g;
           # Ö
           $line=~s/\231/Ö/g;
           # ä
           $line=~s/\204/ä/g;
           # Ä
           # nothing to do
           $line=~s/\204/Ä/g;
      }
      # e mit Akzent  umwandeln
      $line=~s/\202/e/g;
      $line=~s/\212/e/g;
      $line=~s/è/e/g;
      $line=~s/é/e/g;
      # a mit Akzent umwandeln
      $line=~s/À/a/g;
      $line=~s/\341/a/g;
      #$line=~s/\303/a/g;
      # a mit Dach umwandeln
      $line=~s/\203/a/g;
      # o mit Akzent umwandeln
      $line=~s/¢/o/g;
      # o mit Dach umwandeln
      $line=~s/\223/o/g;
      # russisches i
      $line=~s/ï/i/g;
      # u mit akzent
      $line=~s/£/u/g;
      $line=~s/\243/u/g;
      # SPLAN-Sonderzeichen ENDE

      # Atlantis-Sonderzeichen START  
      $line=~s/\264//g;
      $line=~s/\351/e/g;
      $line=~s/\363/o/g;
      $line=~s/\364/o/g;
      $line=~s/\347\343/a/g;
      $line=~s/\347/c/g;

      # Atlantis-Sonderzeichen ENDE  

      # Jaeneke Sonderzeichen
      $line=~s/\353/e/g;
      $line=~s/\351/e/g;
      # ' 
      $line=~s/\047//g;

      # ß umwandeln
      $line=~s/á/ß/g;
      $line=~s/\303/ss/g;
      # Sonderzeichen wegfallenlassen
      # Minus streichen
#      $line=~s/-//g;
      # ' Streichen
      # mit s/'//g; geht die Emacs-Formatierung nicht mehr
      $line=~s/\'//g;
      return $line;
}


# Ende Sub
