
;; definition der datei- und ordneroperationen
;; moeglicherweise teilweise noch hardware-abhaengig
;;
;; hier sind auch einiger funktionen aus funktionen MCL implementiert

;vorraussetzungen B=nachricht, und mac-pfadname und Konsorten eliminieren

;;
;; 16/04/94 bambi  b=probe-directory eingefuehrt


;************************************************************************************
; Bibliotheksfunktionen
;************************************************************************************


;********************
; Dateien
;***********

#+:aclpc
(defun at-default-directory ()
     (pathname
          (or (b=probe-directory (full-pathname "lade-platte;"))
               (user-homedir-pathname)
               )))

#+:aclpc
(defun mac-default-directory ()
     (at-default-directory))


(defun b-devices ()
   (host-directory
   (pathname-host *default-pathname-defaults*)
   "*:"
   nil)
   )
;oder (directory "*:")

(defun devices (&key (nur_platten t))
     (let ((alle (directory "*:"))
            )
         (if nur_platten
            (mapcar #'pathname
                  (remove-if #'(lambda (x)
                                          (or (char= (char x 0) #\a)
                                               (char= (char x 0) #\b)))
                      (mapcar #'namestring alle)))
            alle)
         ))

(defun directories-in-directory (dir)
     ;; man muss mit rechnen, dass ein datei-pfad uebergeben wird
     ;; oder eine pfadobjekt
     (when (pathnamep dir)
          (setq dir (directory-namestring dir))
          )
     (let* ((echter-pfad (directory-namestring (full-pathname dir :error-p nil)))
             (device (when echter-pfad
                              (pathname-device echter-pfad)))
             unter-dir-liste
             )
         (directory (make-pathname :device device
                                              :directory (append (pathname-directory echter-pfad)
                                                                   '(:wild))
                                              ))
         ))

(defun files-in-directory (dir)
     ;; man muss mit rechnen, dass ein datei-pfad uebergeben wird
     ;; oder eine pfadobjekt
     (when (pathnamep dir)
          (setq dir (directory-namestring dir))
          )
     (let* ((echter-pfad (directory-namestring (full-pathname dir :error-p nil)))
             (device (when echter-pfad
                               (pathname-device echter-pfad)))
             (ordner-liste (when echter-pfad (pathname-directory echter-pfad)))
             )
         (when echter-pfad
              (remove-duplicates
              (append (directory
                                (enough-namestring (make-pathname :device device
                                                                       :directory ordner-liste
                                                                       :name "*.*")))
                   (directory
                       (enough-namestring (make-pathname :device device
                                                              :directory ordner-liste
                                                              :name "*"))))
                  :test #'string-equal
                  :key #'namestring
                  ))))
                                  

;; neue funktion, um die existenz eines ordners zu ueberpruefen
;; bambi, 16.4.94
;; ist voll portabel, da nur CLtL2-Aufrufe
(defun b=probe-directory (log-pfad)
     "log-pfad muss ein string sein - aber nicht unbedingt logisch.
Sollte aber vollstaendig sein!"
     ;; oder eine pfadobjekt
     (when log-pfad
          (when (pathnamep log-pfad)
               (setq log-pfad (directory-namestring log-pfad))
               )
          (let* (;; das zusaetzliche directory-namestring erlaubt es auch
                  ;; einen dateipfad zu uebergeben (macht wohl Classika manchmal ;-) ,bambi)
                  (moeglicher-pfad (full-pathname log-pfad :error-p nil))
                  (echter-pfad (when moeglicher-pfad
                                           (directory-namestring moeglicher-pfad)))
                  (device (when echter-pfad
                                    (pathname-device echter-pfad)))
                  (ober-dir-teile-liste (when echter-pfad
                                                      (append (pathname-directory echter-pfad)
                                                           '(:up :wild))))
                  unter-dir-liste
                  (alle-platten (mapcar #'namestring (devices)))
                  )
              ;; sonderfall es ist die platte selbst
              (if (and echter-pfad (member echter-pfad alle-platten :test #'string-equal))
                 echter-pfad
                 (when ober-dir-teile-liste
                      (setq unter-dir-liste
                           (mapcar #'directory-namestring
                                 (directory (make-pathname
                                                       :device device
                                                       :directory ober-dir-teile-liste
                                                       ))))
                      (find echter-pfad unter-dir-liste :test #'equalp)
                      ))
              )))



#+:ccl-2
(defun b=alle-Dateien-im-Ordner (ordner)
  (let ((liste nil))
    (b=mit-allen-dateien
     ordner
     #'(lambda(datei)
         (push datei liste)))
    liste))

#+:aclpc
(defun b=alle-Dateien-im-Ordner (ordner)
     (files-in-directory ordner)
     )

#|Beispiel
(mapcar #'pathname-name (b=alle-Dateien-im-Ordner "Bibliothek;bib-fkt:"))
-> ("Funktionen clisp" "Funktionen clisp?" "Funktionen MCL" "Makros")
|#


(defun b=alle-Dateien-im-Ordner-sortiert (ordner)
  (sort (b=alle-Dateien-im-Ordner ordner)
        #'string-lessp
        :key
        #+:ccl-2 #'mac-file-namestring
       #+:aclpc #'enough-namestring
       ))

(defun b=unbenutzte-datei (filepath)
  "Aufgabe, ein unbenutzten Datei liefern, Aus stefans Oska rCODE"
  (if (probe-file filepath)
    (let (errpath)
      (loop
        (let ((path-type (pathname-type filepath))
              )         
          (setq errpath (cond ((or (eq :unspecific path-type)
                                   (null path-type))
                               (merge-pathnames (format nil "~a.1" (pathname-name filepath)) filepath))
                              ((stringp path-type)
                               (let ((typ (b=lese-von-string path-type))
                                     )
                                 (if (numberp typ)
                                   (merge-pathnames (format nil "~a.~a"
                                                            (pathname-name filepath)
                                                            (1+ (read-from-string path-type)))
                                                    filepath)
                                   (merge-pathnames (format nil "~a.1" (pathname-name filepath)) filepath)
                                   )))
                              (t (error "Fehler beim Erzeugen eines unbenutzten Dateinames"))))
          )
        
        (if (probe-file errpath)
          (setq filepath errpath)
          (return errpath)
          )
        )
      )
    filepath
    )
  )

(defun b=rename-without-merge-files (old new)
  (rename-file (b=make-unmergable-filename old)
               (b=make-unmergable-filename new)))

(defun b=make-unmergable-filename (old)
  (if (null (pathname-type old))
    (make-pathname
          #+:aclpc :device #+:aclpc (pathname-device old)
           :directory (pathname-directory old)
                   :name (pathname-name old)
                   :type #+:ccl-2 :unspecific #+:aclpc ""
                   :defaults nil)
    old))


(defun b=speichere-Funktionsausgabe-auf-datei (pfadname die_funktion parameter_liste 
                                                               &key 
                                                               ueber-Fenster-p
                                                               mac-file-type
                                                               mac-file-creator)
  "Aufgabe: Waehrend der Ausfuehrung der Funktion die_funktion wird 
           *standard-output* auf die Datei Pfadname umgelenkt
  "
  (declare (ignore ueber-Fenster-p))
  ;Autor: Karsten/Ute
  (cond 
   ((And (probe-file pfadname)
         (fboundp 'file-locked-p)(fboundp 'b=nachricht)
         (file-locked-p pfadname))
    (b=nachricht 
     (b=konkateniere-nach-string (b=s :die_datei) " \"" pfadname "\" " (b=s :geschuetzt_und_nicht_ueberschreiben))))
   (T
    (let ((dateiname-existiert-schon-p (probe-file pfadname))
          puffername)
      (when dateiname-existiert-schon-p
        (setq puffername pfadname
              pfadname (b=unbenutzte-datei pfadname)))
      (with-open-file
        (*standard-output* pfadname
                           :direction :output
                           :if-exists :overwrite
                           :if-does-not-exist :create)
        (apply die_funktion parameter_liste))
      (when (and mac-file-type (keywordp mac-file-type)(fboundp 'set-mac-file-type))
        (set-mac-file-type pfadname mac-file-type))
      (when (and mac-file-creator (keywordp mac-file-creator)(fboundp 'mac-file-creator))
        (set-mac-file-creator pfadname mac-file-creator))
      (when dateiname-existiert-schon-p
        (delete-file puffername)
        (b=rename-without-merge-files pfadname puffername))))))
#|
Beispiel

(b=speichere-Funktionsausgabe-auf-datei "platte;test" 'print '(3))
(lock-file "platte;test")
(b=speichere-Funktionsausgabe-auf-datei "platte;test" 'print '((3 4 5 6))
                                              :ueber-Fenster-p t)
(b=speichere-Funktionsausgabe-auf-datei "platte;test" 'print '((3 4 5 6))
                                              :ueber-Fenster-p t
                                              :mac-file-type :test
                                              :mac-file-creator :bibi)
(mac-file-type "platte:test" ) ==> :test
(mac-file-creator "platte:test" ) ==> :bibi
|#


(defun b=lade-Datei-sicher (Dateiname &key print fragen-ob-weitermachen-p
                                          (weitermachentext "Weitermachen"))
  "Laden einer durch Dateiname angegebenen Datei,
wobei Fehler beim Laden gemeldet, aber moeglichst weitergeladen wird
Falls der Bentzer nicht weiterladen will, wird :abgebrochen zurueckgeliefert"
  ;Karsten, 13.06.90
  (let (schongemeldet)
    (with-open-file  (datei Dateiname :direction :input)
    (do* ((letze_zeile "" wert)
          (lispausdruck 
           (multiple-value-list (b=lispfehler-abfangen
                                  (read  datei nil `lese_fehler)))
           (multiple-value-list (b=lispfehler-abfangen
                                  (read  datei nil `lese_fehler))))
          (wert (first lispausdruck)(first lispausdruck))
          (meldung (second lispausdruck)(second lispausdruck)))
         ((eql wert `lese_fehler))
      (if (eql wert :fehler)
        (progn
          (b=nachricht
           (format nil "~a \"~a\" ~a:~%~%~a ~2%~a ~2%~a." 
                   (b=s :Beim_Einlesen_der_Datei)
                   Dateiname 
                   (b=s :ist_folgender_Fehler_aufgetreten)
                   meldung
                   (b=s :nach)
                   letze_zeile)
           :Fensterbreite 460
           :icon b_stop-icon)
          (when (and ;(not schongemeldet)
                 fragen-ob-weitermachen-p)
            (when (eql (b=ja-nein weitermachentext) :nein)
              (return :abgebrochen)))
          (setq schongemeldet t))
        (multiple-value-bind 
          (ergebnis fehler)
          (b=lispfehler-abfangen
            (if print
              (micro-eval (print wert))
              (micro-eval wert)))
          (when (eql ergebnis :fehler)
            (b=nachricht
             (format nil "~a \"~a\" ~2% ~a~% ~a ~2%~a:~2%~a." 
                     (b=s :Beim_Einlesen_der_Datei)
                     Dateiname
                     (b=s :waehrend_auswerten_des_ausdrucks)
                     wert
                     (b=s :ist_folgender_Fehler_aufgetreten)
                     fehler))
            (when (and ;(not schongemeldet)
                   fragen-ob-weitermachen-p)
              (when (eql (b=ja-nein weitermachentext) :nein)
                (return :abgebrochen)))
            (setq schongemeldet t))))))))

#|Beispiele:
;vorher auch Datei mit Fehler anlegen
(b=lade-Datei-sicher (choose-file-dialog)  :fragen-ob-weitermachen-p t)
(b=lade-Datei-sicher (choose-file-dialog) :print t)
|#


(defun b=Ordner-vorhanden-p (Dateiname)
  "Dateiname kann eine Datei oder ein Ordner sein"
     #+:aclpc
     (b=probe-directory dateiname)
     #+:ccl-2
     (probe-file (mac-directory-namestring Dateiname))
     )

#+:ccl-2
(defun b=Bildauswahl (&key (ordner (mac-default-directory)) (buttontext (b=s :waehlen)))
  "Auswahldialog, um Dateien vom Typ :pict auszuwaehlen"
  #|Autor: Heinz, 22.04.1992|#
  (choose-file-dialog
   :mac-file-type :pict
   :directory ordner
   :button-string buttontext))



(defun b=Umlaute-in-Dateien-ersetzen (Datei-oder-Ordner &key (fenster-zeigen-p))
  (when (directoryp Datei-oder-Ordner)
    (setq Datei-oder-Ordner (b=konkateniere-nach-string Datei-oder-Ordner "*")))
  (dolist (Datei (directory Datei-oder-Ordner :directories NIL :files T))
    (b=Umlaute-in-Datei-ersetzen (mac-namestring datei) :fenster-zeigen-p fenster-zeigen-p))
  (dolist (Ordner (directory Datei-oder-Ordner :directories T :files NIL))
    (b=Umlaute-in-Dateien-ersetzen (mac-directory-namestring ordner) :fenster-zeigen-p fenster-zeigen-p)
    ))


;********************
; Ressourcen
;***********


;********************
; Atome und Strings
;***********

;********************
; Praedikate
;***********

#+:ccl-2
(defun b=zu-ACL-p (Symbol)
  "Liefert T, falls das Symbol im Paket ccl oder common-lisp definiert wurde"
  (member (symbol-package Symbol) (list (find-package 'ccl)
                                        (find-package 'common-lisp))
          :test 'equal)
  )
#|
(b=zu-ACL-p 'delete)
-> (#<Package "COMMON-LISP">)
(b=zu-ACL-p 'add-subviews)
-> (#<Package "CCL"> #<Package "COMMON-LISP">)
(b=zu-ACL-p  'b=zu-ACL-p )
-> NIL 
|#

;********
;Sequenzen 
;********

;********************
; Listen
;***********

;********************
; Mengen
;***********

;********************
; Texte
;***********

;********************
; Schriften
;***********

;********************
; Graphik
;***********

;********************
; LISP-Umgebung
;***********

;*********************************************
; Ausgabeformatierung mit Fonts
;*********************************




(defun b=string-umbrechen (string width &optional (schrift
                                                                                      b_normalschrift)
                                                &key (force-break-p nil))
     "Zerlegt einen String in eine Liste von einzelstrings, die kleiner sind
als width.
Achtung, falls kein Teilstring existiert, der kleiner als width ist,
klappst nicht
Returns im Text werden als gezwungene Umbrueche betrachtet.
mit force-break-p wird der umbruch im wort erzwungen.
"
     (let ((string-list (b-parse-with-delimiter string #\newline))
            (result nil))
         (do* ((rest string-list)
                 (s ))
                ((null rest)
                 (nreverse result))
              (setq s (pop rest))
              (multiple-value-bind 
                      (first second) 
                     (b-split-point s width :schrift schrift :force-break-p force-break-p)
                    (setf first (string-trim '(#\space #\tab) first))
                    (when t ;(not (string-equal first ""))
                         (push first result))
                    (when (and second (not (string-equal second "")))
                         (push second rest))))))


#|
? (B=String-Umbrechen "wal lodallohallo" 16 b_normalschrift :force-break-p t)
("w" "a" "l" "l" "o" "d" "a" "l" "l" "o" "h" "a" "l" "l" "o")
? (B=String-Umbrechen "wallodallohallo" 9 b_normalschrift :force-break-p t)
("w" "a" "ll" "o" "d" "a" "ll" "o" "h" "a" "ll" "o")

|#

(defun b-parse-with-delimiter (line &optional (delim #\newline))
  "Breaks LINE into a list of strings, using DELIM as a 
   breaking point."
  ;; what about #\return instead of #\newline?
  (let ((pos (position delim line)))
    (cond (pos
           (cons (subseq line 0 pos)
                 (b-parse-with-delimiter (subseq line (1+ pos)) delim)))
          (t
           (list line)))))

(defun b-split-point (string max-length &key (schrift b_normalschrift) (force-break-p nil))
  "Finds an appropriate point to break the string at given a target length.
mit force-break-p wird der umbruch im wort erzwungen. Buchstaben werden in sich nicht
umgebrochen.
 "
  ;; we probably should split some strings that are short enough anyway
  ;; but need a base condition to prevent infinite loops.
  (cond ((< (string-width string schrift) max-length)
         string)
        (t
         ;; Find the first space that breaks the arglist.
         (let* ((endposition (length string))
                (spaceposition)
                )
           (loop
             (setq spaceposition  (position #\space string :from-end t
                                            :end endposition))
             
               (cond ((or (null spaceposition)(= 0 spaceposition))
                         ;eigentlich weiter trennen
                         (cond (force-break-p
                                      (return (let ((bisherige-groesse 0))
                                                     (dotimes (index (length string) string)
                                                          (+= bisherige-groesse
                                                               (string-width (string (char string index)) schrift))
                                                          (when (> bisherige-groesse max-length)
                                                               (return (values (subseq string 0 (max index 1)) (subseq string (max index 1))))
                                                               ))))
                                      )
                                  (t
                                     ;jetzt ist es fehleschlagen, wir finden keine stelle die kleiner ist
                                     ;da wir keine Woerter trenne wollen suchen wir einfach die erste Trennposition von vorne
                                     ;und trennen da
                                     (setq spaceposition (position #\space string))
                                     (when (eql 0 spaceposition)
                                          ;es hat mit einem Space angefangen
                                          (setq spaceposition (position #\space string :start 1)))
                                     (if spaceposition
                                        (return (values (subseq  string 0 spaceposition)
                                                        (subseq  string spaceposition)))
                                        (return string))
                                     
                                     ))
                         )
                        ((< (string-width (subseq  string 0 spaceposition)
                                 schrift)  max-length)
                         (return (values (subseq  string 0 spaceposition)
                                         (subseq  string spaceposition))))
                        (T
                            ;weitersuchen
                            (setq endposition spaceposition))))))))

                    

#|
(defvar test "Zerhackt den Text mit Hilfe der Funktion b=breche-string-um entsprechend der textbreite-
in-pixeln und legt einen *static-text-dialog-item* an. Der Text wird dabei an der
Position (x,y) positioniert. Zurueckgeliefert wird das Dialog-item und die Gesamthoehe des 
entstehenden Textes als multiple-value.")



(time 
 (dotimes (x 2)
   (dolist (y (list 50 100 150 200 250 300 350 400 450 500 550 600 700 800 900 1000 1100 1200 1300))
     (b=string-umbrechen test y))))

(time 
 (dotimes (x 2)
   (dolist (y (list 50 100 150 200 250 300 350 400 450 500 550 600 700 800 900 1000 1100 1200 1300))
     (b=breche-string-um test y :font b_normalschrift))))
(time 
 (dotimes (x 2)
   (dolist (y (list 50 100 150 200 250 300 350 400 450))
     (b=breche-string-um test y :font b_normalschrift))))

|#

(defun b=breche-string-um (string zeilenbreite &key (font b_Chicagoschrift))
"Funktion zerhackt einen string in eine Liste von Strings, so dass die Pixellaenge der 
einzelnen Strings nicht laenger als zeilenbreite ist. Als Ergebnis wird die Liste
der Strings und die Texthoehe der Zeilen in Pixeln zurueckgeliefert."

#|Autor: Klaus, 17.04.1990|#

  (do ((str string)
       (string-liste NIL)
       (s "")
       (l NIL)
       (dummy NIL)
       (newline NIL))
      ((string= "" str) (if (string= "" s) 
                              (values string-liste
                                      (* (b=schrifthoehe font) (length string-liste)))
                              (values (append string-liste (list s)) 
                                      (* (b=schrifthoehe font) (+ (length string-liste) 1)))))
    (if (string= #\Space (subseq str 0 1))
      (setq str (subseq str 1))             ;Leerzeichen loeschen
      (progn
        (setq l (multiple-value-list (b=lese-erstes-wort-aus-string str)))
        (setq newline (string= (caddr l) #\Newline))
        (if (and (string= "" s)  (not newline))
          (setq s (car l))
          (if (< (string-width (setq dummy (b=konkateniere-nach-string s " " (car l))) font)
                 (- zeilenbreite 1))
            (if newline
              (progn
                (setq string-liste (append string-liste (list dummy)))
                (setq s ""))
              (setq s dummy))
            (if newline
              (progn
                (setq string-liste (append string-liste (list s) (list (car l))))
                (setq s ""))
              (progn
                (setq string-liste (append string-liste (list s)))
                (setq s (car l))))
            ))
        (setq str (cadr l)) )) ))

#| Beispiel:
(b=breche-string-um "hallo world hallo world hallo world" 60 )
(b=breche-string-um "hallo world
hallo world hallo
world" 400)
|#
   
;********************
; Ausgabe
;***********

(defun b=Tabelle-ausgeben (Zeilenbeschriftung 
                              Spaltenbeschriftung
                              tabellen-funktion
                              &key 
                              (tabellen-zugriff :element)
                              (tabellen-titel "")
                              (Zeilen-titel "Zeilen") (Spalten-titel "Spalten")
                              (zeilen-breite 30) (spalten-breite 30)
                              (Seiten-breite 150)
                              (stream t)
                              (Seitenlaenge :beliebig)
                              (Funktion-bei-Seitenumbruch nil))
  "
Parameter:
Zeilenbeschriftung = Liste von Zeilenelementen
Spaltenbeschriftung = Liste von Spaltenelementen
Tabellen-funktion = Funktion mit 2 Parameter
                   Liefert den Eintrag in die tabelle
Tabellen-zugriff = :index ==> Parameter Tabellenfunktion = Zeilenindex Spaltenindex
                   :element ==> Parameter Tabellenfunktion = Zeilenelement Spaltenelement
Zeilen-titel, Spalten-titel,tabellen-titel Strings
zeilen-breite, spalten-breite Breite in Buchstaben eines Eintrags
Seiten-breite klar
stream = Ziel der Ausgabe
Beispiel :
(b=tabelle-ausgeben `(1 2 3 4 5 6) `(a b c d e f g h i j k l)
 `+ :zeilen-breite 10 :spalten-breite 15
 :tabellen-titel \"Eine Beispieltabelle\"
 :seiten-breite 80 :zeilen-titel \"Y Achse\" :spalten-titel \"X achse\"
 :tabellen-zugriff :index)

(let ((stream (make-instance 'fred-window
                     
                     :scratch-p nil)))
      (b=tabelle-ausgeben `(1 2 3 4 5 6) `(a b c d e f g h i j k l)
                          `b=konkateniere-nach-String :stream stream)
  )"
  (declare (ignore seitenlaenge funktion-bei-seitenumbruch))
  (let* ((x_anzahl (ceiling (- Seiten-breite zeilen-breite)  spalten-breite)))
    
    (format stream "~A~2%" tabellen-titel)
    (let* ((y_liste Zeilenbeschriftung)
           (x_liste Spaltenbeschriftung)
           (x_format 
            (concatenate `string "~"
                         (b=erzeuge-string spalten-breite)
                         "A"))
           (y_format 
            (concatenate `string "~"
                         (b=erzeuge-string zeilen-breite)
                         "A"))
           (x_punkte_string (make-sequence `string spalten-breite :initial-element #\.)))
      
      (format stream "~?~A~%" y_format (list "") Spalten-titel)
      (format stream "~A~%" Zeilen-titel)
      
      
      ;Schleife Ueber alle Teillisten
      (do* (
            (x_liste_alles x_liste (nthcdr x_grenze x_liste_alles))
            (laenge (length x_liste_alles) (length x_liste_alles))
            (x_grenze  (min x_anzahl laenge) (min x_anzahl laenge))
            (x_teilliste)
            )
           ((endp x_liste_alles))
        (setq x_teilliste (subseq x_liste_alles 0 x_grenze))
        ;ausgabe der Y-achse
        (format stream "~?" y_format `(""))
        (dolist (x_element x_teilliste)
          (format stream "~?|" x_format (list (b=zentriere-Lispausdruck  x_element spalten-breite))))
        (format stream "~%")
        ;Ausgabe Trennlinie
        (format stream "~?" y_format `(""))
        (dotimes (x  (length x_teilliste))
          (format stream "~?" x_format (list (make-sequence `string spalten-breite :initial-element #\))))
        (format stream "~%")
        
        (dolist (y_element y_liste)
          (format stream "~?" y_format (list (b=String-abschneiden  y_element
                                                                    zeilen-breite)))
          (dolist (x_element x_teilliste)
            (format stream "~?" x_format
                    (list (b=String-abschneiden
                           (let ((eintrag (if (eql tabellen-zugriff :index)
                                            (funcall tabellen-funktion (position y_element y_liste)
                                                     (position x_element x_liste))
                                            (funcall tabellen-funktion y_element x_element))))
                             (if eintrag
                               (b=zentriere-Lispausdruck eintrag spalten-breite :auffuellzeichen #\.)
                               x_punkte_string)
                             )
                           spalten-breite)))
            (format stream "|")
            )
          (format stream "~%")
          )
        (format stream "~%")
        );do*
      );let*
    (format stream "~%")
    (when (typep stream 'fred-window)
        (fred-update stream))
    );let* 
  )


;********************
; Fenster und Bildschirm
;***********

