;;;;; -*-coding: x-ctext;-*-
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: header.el
;;;;
;;;; Code that needs to go before everything else
;;;;


;;; Check the version of Emacs when compiling
;;;
;;; If the version of Emacs is too old, signal an error.
;;; We require Gnu Emacs 19.34 or 20.3 or XEmacs 21.1.

(eval-and-compile
  (if (or (not (boundp 'emacs-major-version))
          (not (boundp 'emacs-minor-version))
          (and (string-match "XEmacs" (emacs-version))
               (or (< emacs-major-version 20)
                   (and (= emacs-major-version 20)
                        (< emacs-minor-version 4))
                   (and (= emacs-major-version 21)
                        (< emacs-minor-version 1))))
          (and (not (string-match "XEmacs" (emacs-version)))
               (or (< emacs-major-version 19)
                   (and (= emacs-major-version 19)
                        (< emacs-minor-version 34))
                   (and (= emacs-major-version 20)
                        (< emacs-minor-version 2)))))
      (error "Emacs 19.34, XEmacs 21.1 or later, or Emacs 20.2 or later is required")))

(cond ((or (not (boundp 'emacs-major-version))
           (not (boundp 'emacs-minor-version))
           (< emacs-major-version (eval-when-compile emacs-major-version)))
       (error "LysKOM was compiled for a newer version of Emacs"))
      ((and (eq (eval-when-compile emacs-major-version) 19)
            (> emacs-major-version 19))
       (error "A LysKOM client compiled for Emacs 19 will not run in newer versions of Emacs.")))

;;; Check some basic misfeatures that are still all too common



(eval-and-compile
  (defmacro lyskom-detect-read-kbd-macro-bug ()
    `(and (stringp (read-kbd-macro "<SPC>"))
          (string-equal (read-kbd-macro "<SPC>") "<SPC>")))

  (let ((libraries-to-try '("edmacro")))
    (while libraries-to-try
      (if (lyskom-detect-read-kbd-macro-bug)
          (progn (condition-case nil
                     (load-library (car libraries-to-try))
                   (error nil))
                 (setq libraries-to-try (cdr libraries-to-try)))
        (setq libraries-to-try nil)))

    (if (lyskom-detect-read-kbd-macro-bug)
        (let ((elc-location (locate-library "macedit.elc"))
              (el-location (locate-library "macedit.el"))
              (elz-location (locate-library "macedit.el.gz")))
          (cond ((or el-location elc-location)
                 (message "\

You probably have a file named macedit.elc in Emacs' load path.

This file is included in some versions of the calc package and
is no longer required as the functions that it provides are
also provided by more modern packages.

You will need to remove macedit.el and all references to it
from Emacs. These are the files you need to remove:

%s
To fully remove macedit you will also have to remove any
autoloads that refer to it. These may be found in a file
named auto-autoloads.elc (or auto-autoloads.el), located 
in the same directory as macedit or in a file named
loaddefs.el, located elsewhere.

With Gnu Emacs you may be successful in removing references
by using the command update-autoloads-from-directories.
"
                          (mapconcat (lambda (x) (or x "")) 
                                     (list elc-location el-location elz-location) 
                                     "\n"))
                 (error "Unable to run or compile due to obsolete macedit package."))
                (t
                 (message "\

Your definition of read-kbd-macro appears to be obsolete. This
is usually caused by having a package called macedit in Emacs
load path, but I am unable to find this package. LysKOM cannot
run with your version of read-kbd-macro.
")
                 (error "Unable to run or compile due to obsolete definition of read-kbd-macro.")))))))


(eval-and-compile 
  (defvar lyskom-debug-compile (eval-when-compile (getenv "LYSKOM_DEBUG_COMPILE"))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: defvar.el
;;;; Authos: David Byers
;;;;
;;;; This file contains definitions used to define variables
;;;;


(provide 'lyskom)

;; Just to get rid of compiler warnings
(defvar kom-dont-read-saved-variables)
(defvar lyskom-is-loaded)

(defvar lyskom-local-variables nil
  "List of variables to make local in a LysKOM buffer")

(defvar lyskom-local-hooks nil
  "List of hooks to make local in a LysKOM buffer.")

(defvar lyskom-protected-variables nil
  "List of variables that are protected from kill-buffer")

(defvar lyskom-inherited-variables nil
  "List of variables inherited from the LysKOM buffer")

(defvar lyskom-elisp-variables nil
  "Tells the client what flags and hooks that are to be saved in the server.
These are the flags that are saved in the elisp-client part of the server.")

(defvar lyskom-transition-variables nil
  "Tells the client what variables are not to be saved in the server, but
are to be read from the server. This is for transitioning.")

(defvar lyskom-copy-transition-variables nil
  "Tells the client about variables that need transforming when copying.")

(defvar lyskom-minibuffer-variables nil
  "These are variables that should be set in the minibuffer by 
lyskom-with-lyskom-minibuffer.")

(defvar lyskom-minibuffer-values nil
  "Dynamic binding of values that minibuffer variables are to take on")

(defvar lyskom-global-variables nil
  "List of flags that are to be saved as booleans in the common block.

Don't change these. They are defined by the protocol.")

(defvar lyskom-non-portable-server-variables nil
  "List of variables that should not be copied from server to server.")


(defmacro lyskom-save-variables (var-list &rest forms)
  "Save the values and property list of symbols in VAR-LIST and execute FORMS
The symbol value, property list and buffer-local property of all variables
is saved before executing FORMS and restored when FORMS have finished."
  (let ((sym1 (make-symbol "lyskom-saved-variables"))
        (sym2 (make-symbol "lyskom-saved-symbols"))
        (sym3 (make-symbol "lyskom-saved-local"))
        (sym4 (make-symbol "lyskom-saved-plist")))
    `(let* ((,sym2 (quote ,var-list))
	    (,sym1 (mapcar 'symbol-value ,sym2))
	    (,sym4 (mapcar 'symbol-plist ,sym2))
	    (,sym3 (mapcar (lambda (v)
			     (local-variable-p v (current-buffer)))
			   ,sym2)))
         (unwind-protect 
             (progn ,@forms)
           (while ,sym1
             (when (car ,sym3) (make-local-variable (car ,sym2)))
             (set (car ,sym2) (car ,sym1))
             (setplist (car ,sym2) (car ,sym4))
             (setq ,sym1 (cdr ,sym1)
                   ,sym2 (cdr ,sym2)
                   ,sym3 (cdr ,sym3)
                   ,sym4 (cdr ,sym4)))))))

(put 'lyskom-save-variables 'edebug-form-spec
     '(sexp body))

(defmacro lyskom-with-lyskom-minibuffer (&rest forms)
  "Run FORMS after ensuring that LysKOM minibuffer variables will be set."
  `(let* ((lyskom-minibuffer-values
	   (mapcar 'symbol-value lyskom-minibuffer-variables)))
     (unwind-protect
	 (progn
	   (add-hook 'minibuffer-setup-hook
		     'lyskom-setup-minibuffer-variables)
	   ,@forms)
       (remove-hook 'minibuffer-setup-hook
		    'lyskom-setup-minibuffer-variables))))

(put 'lyskom-with-lyskom-minibuffer 'edebug-form-spec
     '(body))


(defun lyskom-setup-minibuffer-variables ()
  (let ((syms lyskom-minibuffer-variables)
        (vals lyskom-minibuffer-values))
    (while syms
      (make-local-variable (car syms))
      (set (car syms) (car vals))
      (setq syms (cdr syms)
            vals (cdr vals)))))

         

(defmacro def-kom-var (name value &rest args)
    "Define a variable with name NAME and initial value VALUE.
Remaining args, ARGS may be
A string        Used as the documentation string for the variable
A symbol        A predefined property of the variable
A list          A widget specification for the variable

Predefined (and tested) properties are the following
server          Save the variable in the elisp block. Implies local.
local           Make the variable buffer-local.
inherited       The variable is inherited from parent buffer. Implies protected
protected       The variable is marked as permanent local. Implies local.
minibuffer      Inherit the variable as a local variable in the minibuffer.
server-hook     A hook stored in the server.
local-hook      A hook variable that is made local in LysKOM buffers.
language-force  A language-variable whose value is to be forced."
          
    (let ((inherited nil)
          (protected nil)
          (elisp-block nil)
          (transition-block nil)
          (copy-transition-block nil)
          (buffer-local nil)
          (widget-spec nil)
          (doc-string nil)
          (minibuffer nil)
          (non-portable nil)
          (local-hook-doc nil)
          (local-var-doc nil)
          (server-doc nil)
          (language-force nil)
          (arglist args))
;      (message "%S" name)
      (while arglist
        (cond ((stringp (car arglist)) (setq doc-string (car arglist)))
              ((consp (car arglist))
               (setq widget-spec 
                     `((setq lyskom-custom-variables
			     (cons  (quote ,(list name (car arglist)))
				    lyskom-custom-variables)))))
              ((symbolp (car arglist))
               (cond ((eq (car arglist) 'server)
                      (setq local-var-doc t server-doc t)
                      (setq elisp-block
                            `((add-to-list 'lyskom-elisp-variables ',name)
                              (add-to-list 'lyskom-local-variables ',name))))

                     ((eq (car arglist) 'common)
                      (setq local-var-doc t server-doc t)
                      (let ((common-name (car (cdr arglist)))
                            (type (car (cdr (cdr arglist)))))
                        (setq arglist (cdr (cdr arglist)))
                        (setq elisp-block
                              `((add-to-list 'lyskom-local-variables ',name)
                                (add-to-list 'lyskom-global-variables
                                             (vector ',common-name ',name ',type))))))

                     ((eq (car arglist) 'transition)
                      (let ((converter (car (cdr arglist))))
                        (setq arglist (cdr arglist))
                        (setq local-var-doc t server-doc t)
                        (setq transition-block
                              `((add-to-list 'lyskom-transition-variables 
                                             '(,name . ,converter))
                                (add-to-list 'lyskom-local-variables ',name)))))

                     ((eq (car arglist) 'copy-transition)
                      (let ((converter (car (cdr arglist))))
                        (setq arglist (cdr arglist))
                        (setq local-var-doc t server-doc t)
                        (setq copy-transition-block
                              `((add-to-list 'lyskom-copy-transition-variables 
                                             '(,name . ,converter))))))

                     ((eq (car arglist) 'server-hook)
                      (setq local-hook-doc t server-doc t)
                      (setq elisp-block
                            `((add-to-list 'lyskom-elisp-variables ',name)
			      (add-to-list 'lyskom-local-hooks ',name))))

                     ((eq (car arglist) 'protected)
                      (setq local-var-doc t)
                      (setq protected
                            `((put ',name 'permanent-local t)
			      (add-to-list 'lyskom-protected-variables ',name)
			      (add-to-list 'lyskom-local-variables ',name))))

                     ((eq (car arglist) 'inherited)
                      (setq local-var-doc t)
                      (setq inherited
                            `((add-to-list 'lyskom-inherited-variables ',name)
			      (put ',name 'permanent-local t)
			      (add-to-list 'lyskom-protected-variables ',name)
			      (add-to-list 'lyskom-local-variables ',name))))

                     ((eq (car arglist) 'local)
                      (setq local-var-doc t)
                      (setq buffer-local
                            `((add-to-list 'lyskom-local-variables ',name))))

                     ((eq (car arglist) 'local-hook)
                      (setq local-hook-doc t)
                      (setq buffer-local
                            `((add-to-list 'lyskom-local-hooks ',name))))

                     ((eq (car arglist) 'non-portable)
                      (setq non-portable
                            `((add-to-list 'lyskom-non-portable-server-variables ',name))))

                     ((eq (car arglist) 'minibuffer)
                      (setq minibuffer
                            `((add-to-list 'lyskom-minibuffer-variables ',name))))

                     ((eq (car arglist) 'language-force)
                      (setq language-force
                            `((put ',name 'lyskom-language-force t))))

                     (t (error "LysKOM: Unknown variable property: %S"
                               (car arglist)))))
              (t (error "LysKOM: Strange variable argument type: %S" 
                        (car arglist))))
        (setq arglist (cdr arglist)))

      (if (null doc-string) (setq doc-string "This variable is not documented."))

      (if doc-string
        (if
          (if (or local-var-doc local-hook-doc)
            (setq doc-string (concat doc-string "\n")))
          (if local-var-doc
            (setq doc-string (concat doc-string "\nThis variable is buffer-local.")))
          (if local-hook-doc
            (setq doc-string (concat doc-string "\nThis variable is a buffer-local hook.")))
          (setq doc-string (concat doc-string "\n\n\
Setting this variable in .emacs may not yield the results you expect
since that will affect all LysKOM sessions."))))

      (if (and doc-string server-doc)
        (setq doc-string (concat doc-string "

This variable is normally stored on a per-session basis in the
LysKOM server, but can be set in your .emacs simply by setting
it using setq or defvar.")))

      `(progn (if (and (boundp ',name)
		       (not lyskom-is-loaded)
		       (listp kom-dont-read-saved-variables))
		  (add-to-list 'kom-dont-read-saved-variables ',name))
	      (defvar ,name ,value ,doc-string)
	      ,@(apply 'append
		       (list inherited
			     protected
			     elisp-block
			     transition-block
			     copy-transition-block
			     buffer-local
			     minibuffer
			     non-portable
			     widget-spec
			     language-force
			     )))))


(put 'def-kom-var 'edebug-form-spec
     '(&define name form &rest sexp))



(eval-and-compile (provide 'lyskom-defvar))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: feature.el
;;;;
;;;; Functions for determining if we have a particular feature or not
;;;;


(def-kom-var lyskom-feature-defaults nil
  "Alist with default values of all feature variables")

(eval-and-compile
  (defun lyskom-feature-variable (name)
    (intern (format "lyskom-%s-feature-flag" name))))

(defmacro lyskom-define-feature (name default)
  "Define a feature variable NAME with default value DEFAULT"
  `(progn (setq lyskom-feature-defaults 
                (cons (cons ',name ,default) lyskom-feature-defaults))
          (def-kom-var ,(lyskom-feature-variable name) ,default
            "LysKOM feature flag variable."
            inherited minibuffer local)))

(defmacro lyskom-set-feature (name value)
  "Set the value of feature NAME to VALUE"
  `(setq ,(lyskom-feature-variable name) ,value))

(defmacro lyskom-have-feature (name)
  "Return non-nil if feature NAME is non-nil"
  (lyskom-feature-variable name))

(defmacro lyskom-feature-value (name)
  "Return the value of feature NAME"
  (lyskom-feature-variable name))

(defun lyskom-clear-features ()
  "Set the feature variables to their defaults"
  (mapcar (lambda (feature)
            (set (lyskom-feature-variable (car feature)) (cdr feature)))
          lyskom-feature-defaults))

;;UNUSED: lyskom-list-features
(defun lyskom-list-features ()
  "List all feature values. mapcar ROCKS!"
  (mapcar 'lyskom-insert-before-prompt
          (mapcar (lambda (x) (apply 'format "%S: %S\n" x))
                  (lyskom-mapcar2 'list
                           (mapcar 'car lyskom-feature-defaults)
                           (mapcar 'symbol-value 
                                   (mapcar 'lyskom-feature-variable
                                           (mapcar 'car lyskom-feature-defaults)))))))

;;; ======================================================================
;;; ======================================================================

;;; Utility functions

(defmacro lyskom-have-call (call-no)
  "Non-nil if call CALL-NO is supported by the server"
  `(<= ,call-no (lyskom-feature-value highest-call)))


;;; ======================================================================
;;; ======================================================================

;;; The features

(lyskom-define-feature bcc-misc nil)
(lyskom-define-feature read-ranges nil)
(lyskom-define-feature aux-items nil)
(lyskom-define-feature highest-call 0)
(lyskom-define-feature local-to-global nil)
(lyskom-define-feature dynamic-session-info nil)
(lyskom-define-feature idle-time nil)
(lyskom-define-feature long-conf-types nil)


(provide 'lyskom-feature)
;;;;; -*-coding: iso-8859-1;-*-
;;;;; Copyright (C) 1991-1994, 2013  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;;
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;;
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;;
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se.

;;;; ================================================================
;;;;
;;;; File: version.el.in
;;;;
;;;; This file contains the version number.
;;;;

(defconst lyskom-clientversion "0.48 (2020-02-26; Git *)"
  "Version of the LysKOM elisp client.")
;;;;; -*-coding: raw-text;mode: emacs-lisp;-*-
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: vars.el
;;;;
;;;; This file contains almost all the variables used in lyskom.
;;;;

;;;; Note: put "**" at the front of docstrings that are ready
;;;; for use in DocBook autogeneration.
;;;;
;;;; Rules for documentation that can be translated to DocBook
;;;; is in interndoc/vardoc


(defvar lyskom-dummy-variable-to-fool-the-byte-compiler)

(defvar lyskom-mule-compiled
  (eval-when-compile (and (fboundp 'multibyte-string-p)
                          (multibyte-string-p "ÅÄÖ")))
  "Non-nil if the client was compiled with multibyte characters enabled")

(provide 'lyskom)

(require 'lyskom-defvar "defvar")

(defconst lyskom-global-variable-types
  '((boolean (read . lyskom-flag-read-boolean)
             (write . lyskom-flag-write-boolean))
    (integer (read . lyskom-flag-read-integer)
             (write . lyskom-flag-write-integer))
    (symbol-list (read . lyskom-flag-read-symbol-list)
                 (write . lyskom-flag-write-symbol-list))
    (t (read . lyskom-flag-read-from-string)
       (write . prin1-to-string))))


(defun lyskom-protect-variable (sym) 
  "Protect SYM from being killed when deleting local variables."
  (put sym 'permanent-local t)
  (lyskom-local-variable sym)
  (add-to-list 'lyskom-protected-variables sym))

(defun lyskom-local-variable (sym)
  "Define SYM as a buffer-local variable"
  (add-to-list 'lyskom-local-variables sym))

(defun lyskom-setup-local-variables ()
  "Set up all globally defined local variables in the current buffer."
  (mapc 'make-local-variable lyskom-local-variables)
  (mapc 'lyskom-make-local-hook lyskom-local-hooks))

(defvar lyskom-is-loaded nil
  "Non-nil when lyskom has been loaded.")

(defvar lyskom-have-one-login nil
  "Non-nil after the first login")


(def-kom-var kom-dont-read-saved-variables '(kom-dont-read-saved-variables
                                             lyskom-login-hook)
  "**Determines which LysKOM variables to not store in the server.

For the most part you do not have to modify this variable. Any variables
set before LysKOM is loaded will not be read from the server.
This variable should `nil', `t' or a list of symbols. 

The value `nil' means read all variables from the server. The value
`t' means read no variables from the server. When the value is a list
of symbols, those variables will not be read from the server.

Any other values are reserved for future use.")

(defmacro lyskom-maybe-setq (var value)
  "This is a wrapper around setq that does nothing
if the variable is in kom-dont-read-saved-variables."
  `(cond ((eq kom-dont-read-saved-variables t) nil)
         ((memq ,var kom-dont-read-saved-variables) nil)
         (t (set ,var ,value))))

(defmacro lyskom-maybe-setq-default (var value)
  "This is a wrapper around setq-default that does nothing
if the variable is in kom-dont-read-saved-variables."
  `(cond ((eq kom-dont-read-saved-variables t) nil)
         ((memq ,var kom-dont-read-saved-variables) nil)
         (t (set-default ',var ,value))))

;;;; ================================================================
;;;;                     Variables and constants.


;;; User flags

(def-kom-var kom-auto-confirm-new-conferences nil
  "**Determines behavior of `kom-list-new-conferences' and `kom-list-new-persons'.

When this variable is set to `t', `kom-list-new-conferences' and
`kom-list-new-persons' will automatically mark all listed conferences
and persons as known. When set to `nil', these commands will ask for
confirmation before marking the listed conferences and persons as known.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-dont-complain-about-missing-presentation nil
  "**Controls whether the client complains when you don't have
a presentation. When set to non-nil, the client will never
complain.

This variable should not be documented. Anybody asocial enough
to want this feature ought to have to work to find it."
  server)

(def-kom-var kom-show-sync-messages nil
  "**Controls display of database saving message.

When this variable is set to `t', the client will show a message in
the echo area when the LysKOM server signals that it is saving the
database. When set to `nil', no message is shown (the mode line is
changed regardless of the setting of this variable).

Values other than `t' and `nil' are reserved for future use."
  server)


(def-kom-var kom-text-links
  '((t ("\\<bug[ \t\n]+\\([0-9]+\\)\\>"
	"http://bugzilla.lysator.liu.se/show_bug.cgi?id=\\1"
	0 t)))
  "**Defines patterns for automatic links in the text.

This variable defines patterns to look for in text and convert to
links to URLs. The value of this variable must be an alist, where the
key (car) is a conference number and the value (cdr) is a list of link
specifications for that conference. The special value `t' for the key
indicates a list of patterns to apply in all conferences. Key values
other than positive integers and `t' are reserved for future use.

Each link specification is in turn a list \(`PATTERN' `REPLACEMENT'
`HIGHLIGHT' `IGNORE-CASE'), where `PATTERN' is a regular expression to
look for in the text, `REPLACEMENT' is used to generate a URL from the
text matching `PATTERN', `HIGHLIGHT' is the pattern group to highlight
as a URL and `IGNORE-CASE' specifies whether matching is
case-sensitive or not.

Each text is examined for each `PATTERN'. If a match is found, a URL
is created from `REPLACEMENT'. All characters except backslash are
copied verbatim. The backslash character starts one of the following
sequences:

    Sequence    Meaning
    -----------------------------------------------------------------
    `\\&'          Substitute the matched text

    `\\N'          Substitute match for text matching the Nth (...)
                group in `PATTERN'

    `\\\\'          Insert one backslash.
    -----------------------------------------------------------------

The `HIGHLIGHT' parameter specifies which (...) group in `PATTERN'
to highlight as an URL. Use 0 to highlight the entire match.

Finally, if `IGNORE-CASE' is `t', then ignore character case while
looking for `PATTERN'. Values other than `t' and `nil' are reserved
for future use.

When a text has more than one recipient, links will be generated for
all recipients. The order in which the recipients are examined and
the priority among links that match the same text is undefined."
  server)


(def-kom-var lyskom-default-conference-strategy
  '((kom-start-anew (what-is-your-name
                     (default . (lyskom-default-conference-empty))))

    (kom-delete-conf (what-conf-to-delete
                      (default . (lyskom-default-conference-empty))))

    (kom-sub-self (leave-what-conf
                   (filter . (lyskom-default-conference-not-self))))

    (kom-change-priority (what-to-change-pres-you
                          (default . (lyskom-default-conference-at-point
                                      lyskom-default-conference-self))))

    (kom-change-presentation (what-to-change-pres-you
                              (default . (lyskom-default-conference-at-point
                                          lyskom-default-conference-self))))

    (kom-change-conf-motd (who-to-put-motd-for
                           (default . (lyskom-default-conference-at-point
                                       lyskom-default-conference-self))))

    (kom-set-presentation (what-to-set-pres-you
                           (default . (lyskom-default-conference-at-point
                                       lyskom-default-conference-self))))

    (kom-set-motd-text (what-to-set-motd-you
                        (default . (lyskom-default-conference-at-point
                                    lyskom-default-conference-self))))

    (kom-remove-presentation (who-to-remove-pres-for
                              (default . (lyskom-default-conference-at-point
                                          lyskom-default-conference-self))))

    (kom-unset-conf-motd (who-to-remove-motd-for
                          (default . (lyskom-default-conference-at-point
                                      lyskom-default-conference-self))))

    (kom-go-to-conf (go-to-conf-p
                     (default . (lyskom-default-conference-at-point
                                 lyskom-default-conference-self))
                     (filter . (lyskom-default-conference-not-current))))

    (kom-list-created-conferences (list-confs-created-by
                                   (default . (lyskom-default-conference-at-point
                                               lyskom-default-conference-self))))

    (kom-change-name (name-to-be-changed
                      (default . (lyskom-default-conference-at-point
                                  lyskom-default-conference-self))))

    (kom-change-parenthesis (name-to-be-changed
                             (default . (lyskom-default-conference-at-point
                                         lyskom-default-conference-self))))

    (kom-change-password (whos-passwd
                          (default . (lyskom-default-conference-at-point
                                      lyskom-default-conference-self))))

    (kom-redirect-comments (redirect-for-whom
                            (default . (lyskom-default-conference-at-point
                                        lyskom-default-conference-self)))
                           (redirect-to-which-conf))

    (kom-filter-author (filter-author
                        (default . (lyskom-default-conference-at-point
				    lyskom-default-conference-author-of-text-at-point
                                    lyskom-default-conference-last-author))
                        (filter . (lyskom-default-conference-not-self)))
                       (filter-in-conf
                        (filter . (lyskom-default-conference-not-self))))


    (kom-review-by-to (review-by-whom
                       (default . (lyskom-default-conference-at-point
                                   lyskom-default-conference-empty)))
                      (review-to-conf))

    (kom-review-roots-by-to (review-by-whom
			     (default . (lyskom-default-conference-at-point
					 lyskom-default-conference-empty)))
			    (review-to-conf))

    (kom-review-first (review-by-whom
                       (default . (lyskom-default-conference-at-point
                                   lyskom-default-conference-empty)))
                      (review-to-conf))

    (kom-review-first-roots (review-by-whom
			     (default . (lyskom-default-conference-at-point
					 lyskom-default-conference-empty)))
			    (review-to-conf))

    (kom-review-all (review-by-whom
                     (default . (lyskom-default-conference-at-point
                                 lyskom-default-conference-empty)))
                    (review-to-conf))

    (kom-review-all-roots (review-by-whom
			   (default . (lyskom-default-conference-at-point
				       lyskom-default-conference-empty)))
			  (review-to-conf))

    (kom-unread-by-to (unread-by-whom
                       (default . (lyskom-default-conference-at-point
                                   lyskom-default-conference-empty)))
                      (unread-to-conf))

    (kom-unread-roots-by-to (unread-by-whom
			     (default . (lyskom-default-conference-at-point
					 lyskom-default-conference-empty)))
			    (unread-to-conf))

    (kom-unread-first (unread-by-whom
                      (default . (lyskom-default-conference-at-point
                                  lyskom-default-conference-empty)))
                      (unread-to-conf))

    (kom-unread-first-roots (unread-by-whom
			     (default . (lyskom-default-conference-at-point
					 lyskom-default-conference-empty)))
			    (unread-to-conf))

    (kom-unread-all (unread-by-whom 
                     (default . (lyskom-default-conference-at-point
                                 lyskom-default-conference-empty)))
                    (unread-to-conf))

    (kom-unread-all-roots (unread-by-whom 
			   (default . (lyskom-default-conference-at-point
				       lyskom-default-conference-empty)))
			  (unread-to-conf))

    (kom-set-permitted-submitters (conf-to-set-permitted-submitters-q)
                                  (new-permitted-submitters-q
                                   (default . (lyskom-default-conference-empty))))

    (lyskom (what-is-your-name 
             (default . (lyskom-default-conference-empty))))


    (kom-add-recipient (who-to-add-q 
                        (default . ((lyskom-default-conference-saved last-added-rcpt)))
                        (save last-added-rcpt)))

    (kom-add-copy (who-to-add-copy-q 
                   (default . ((lyskom-default-conference-saved last-added-cc)))
                   (save last-added-cc)))

    (kom-add-bcc (who-to-add-bcc-q 
                  (default . ((lyskom-default-conference-saved last-added-bcc)))
                  (save last-added-bcc)))

    (kom-sub-recipient (who-to-sub-q
                        (default . (lyskom-default-conference-at-point
                                    (lyskom-default-conference-saved last-sub-recipient)
                                    lyskom-default-conference-current
                                    lyskom-default-conference-restriction))
                        (save last-sub-recipient)))

    (kom-move-text (who-to-move-from-q
                    (default . (lyskom-default-conference-at-point
                                (lyskom-default-conference-saved last-sub-recipient)
                                lyskom-default-conference-current
                                lyskom-default-conference-restriction))
                    (save last-sub-recipient))
                   (who-to-move-to-q
                    (default . ((lyskom-default-conference-saved last-added-rcpt)
                                lyskom-default-conference-current))
                    (save last-added-rcpt)))

    (kom-move-text-tree (who-to-move-from-q
                         (default . (lyskom-default-conference-at-point
                                     (lyskom-default-conference-saved last-sub-recipient)
                                     lyskom-default-conference-current
                                     lyskom-default-conference-restriction))
                         (save last-sub-recipient))
                        (who-to-move-to-or-sub-q
                         (default . ((lyskom-default-conference-saved last-added-rcpt)
                                     lyskom-default-conference-current))
                         (save last-added-rcpt))
                        (who-to-move-to-q
                         (default . ((lyskom-default-conference-saved last-added-rcpt)
                                     lyskom-default-conference-current))
                         (save last-added-rcpt))
                        (who-to-add-q
                         (default . ((lyskom-default-conference-saved last-added-rcpt)
                                     lyskom-default-conference-current))
                         (save last-added-rcpt)))

    (kom-send-message (who-to-send-message-to
                       (default . (lyskom-default-conference-for-send-message))))

    (kom-moronify (moronify-whom
		   (default . (lyskom-default-conference-at-point
			       lyskom-default-conference-author-of-text-at-point
			       lyskom-default-conference-last-author))))

    (kom-befriend (befriend-whom
		   (default . (lyskom-default-conference-at-point
			       lyskom-default-conference-author-of-text-at-point
			       lyskom-default-conference-last-author))))

    (kom-send-alarm (who-to-send-message-to
                     (default . (lyskom-default-conference-for-send-message))))

    (kom-is-person-member-of-conference (pers-to-check-mship-for)
                                        (conf-to-check-mship-of))



    ;; Defaults and commands that use the defaults

    (kom-remote-autoreply (remote-control-who))
    (kom-remote-set-message (remote-control-who))
    (kom-remote-list-messages (remote-control-who))
    (kom-remote-erase-messages (remote-control-who))
    (kom-remote-quit (remote-control-who))
    (kom-review-presentation (presentation-for-whom))
    (kom-unread-presentation (unread-presentation-for-whom))
    (kom-add-member (who-to-add) (where-to-add))
    (kom-add-self (where-to-add-self))
    (kom-change-priority (change-priority-for-q))
    (kom-sub-member (who-to-exclude) (where-from-exclude))
    (kom-write-text (who-send-text-to))
    (kom-send-letter (who-letter-to))
    (kom-force-logoout (who-to-throw-out))
    (kom-change-supervisor (what-to-change-supervisor-for) (new-supervisor))
    (kom-who-is-on-in-conference (who-is-on-in-what-conference))
    (kom-who-is-present-in-conference (who-is-present-in-what-conference))
    (kom-add-cross-reference (which-conf-to-xref) (which-pers-to-xref))
    (kom-edit-add-cross-reference (which-conf-to-xref) (which-pers-to-xref))
    (kom-edit-insert-link (which-conf-to-link) (which-pers-to-link))
    (kom-status-conf (conf-for-status))
    (kom-status-session (session-for-status))
    (kom-force-logout (who-to-throw-out))
    (kom-status-person (pers-for-status))
    (kom-set-garb-nice (conf-to-set-garb-nice-q))
    (kom-set-super-conf (conf-to-set-super-conf-q) (new-super-conf-q))
    (kom-set-personal-label (label-what-pers) (label-what-conf))
    (kom-will-person-read-text (pers-to-check-will-read-for))
    (kom-create-aux-item (which-conf-to-add-aux-to))
    (kom-recommend-conference (recommend-which-conf))
    (kom-limit-import (limit-import-to-conf))
    (kom-edit-add-recipient (added-recipient))
    (kom-edit-add-bcc (added-blank-carbon-copy))
    (kom-edit-add-copy (added-carbon-copy))
    (kom-edit-move-text (who-to-move-to-q))
    (kom-add-faq (conf-to-add-faq))
    (kom-del-faq (conf-to-del-faq))
    (kom-review-faq (view-which-faq))
    (kom-unread-faq (unread-which-faq))
    (kom-change-conf-faq (what-to-change-faq-you))
    (kom-list-faqs (conf-to-list-faqs))
    (kom-filter-subject (filter-in-conf))
    (kom-filter-recipient (filter-recipient))
    (kom-filter-text (filter-in-conf))
    (kom-change-conf-type (what-conf-to-change))
    (kom-change-privileges (what-pers-privs-to-change))
    (kom-change-message-flag (set-message-flag-for-conf))

    (t (t (default . (lyskom-default-conference-at-point
                      lyskom-default-conference-current))))
    )

  "Strategy for the initial value for conference defaults.

Each element in this list identifies an Emacs och LysKOM command, and
defines the initial values for queries it makes for conference names.

Each element has the following format:

    \(COMMAND . \(\(PROMPT-1 . \(\(default . \(INITIAL-VALUES\\)\\)
                             \(filter  . \(EXCLUDED-CONFS\)\)
                             \(save    . \(SAVE-GROUPS\)\)\)\)
                \(PROMPT-2 . \(\(default . \(INITIAL-VALUES\)\)
                             \(filter  . \(EXCLUDED-CONFS\)\)
                             \(save    . \(SAVE-GROUPS\)\)\)\)
                ...\)\)

COMMAND is the name of a LysKOM or Emacs command. PROMPT-1 and
PROMPT-2 \(there can be any number of prompts liste in a single
command\) are symbols representing the prompt. These are defined
in lyskom-messages in swedish-strings.el and english-strings.el. 

INITIAL-VALUES are functions to call to generate a list of possible
initial values for the prompt. These functions must return lists of
conference numbers. EXCLUDED-CONFS are functions that are called to
eliminate possible initial values. These must take a single argument,
an uconf-stat, and should return the uconf-stat itself if it is a
valid candidate for the initial value, or nil if it is not. Finally,
SAVE-GROUPS are symbols under which the value the user inputs will be
saved.

As a special case, a list in INITIAL-VALUES is interpreted as a
function with arguments. The first element in the list is applied to
the remaining elements.

If `t' is used for COMMAND, it indicates the default to use when no
COMMAND listed matches the current command. If `t' is used for PROMPT,
it indicates defaults, filters and save groups to use when no other
PROMPT matches.


Some useful functions for INITIAL-VALUES are:

lyskom-default-conference-at-point returns the conference shown where
point is. If point is on a conference name, that name will be used as
the initial value.

lyskom-default-conference-current returns the current conference.

lyskom-default-conference-self returns the logged-in person.

lyskom-default-conference-last-author returns the author of the most
recently read text.

lyskom-default-conference-author-of-text-at-point returns the author
of the text at point.

lyskom-default-conference-saved returns saved input. This must be
specified as a list in INITAL-VALUES, where the argument (second
element of the list) is the symbol under which the input was saved,
from \(SAVE-GROUPS\) in one or more commands.


Some useful functions for EXCLUDE-CONFS are:

lyskom-default-conference-not-self excludes the logged-in person.

lyskom-default-conference-not-current excludes the current conference.


The format of this variable may change in the future.
")


(defconst kom-old-farts-text-prompt-strategy
  '((kom-comment-previous (nil lyskom-get-previous-text))
    (kom-view-previous-commented-text (nil lyskom-get-previous-text))
    (kom-private-answer-previous (nil lyskom-get-previous-text))
    (t  (t   lyskom-get-last-read-text)
        (nil     lyskom-get-last-read-text)
        (0       lyskom-get-text-at-point)
        (lyskom-eq-dash   (lambda () (lyskom-get-text-above-point 1)))
        (listp   (lambda () (lyskom-tnpa-prompt (lyskom-get-last-read-text))))
        (plusp   current-prefix-arg)
        (minusp  (lambda () (lyskom-get-text-above-point (- current-prefix-arg))))))
"Put in your `kom-pick-text-no-strategy-alist' to get the 0.46 behaviour:
 * No prefix argument refers to the most recently read text.
 * The prefix argument zero refers to the text under point.
 * A positive prefix argument is interpreted as a text-no.
 * A negative prefix argument will try to find the text-no
   of the text `arg' messages above point from the current
   kom buffer.")

(defconst lyskom-old-farts-text-prompt-strategy
  kom-old-farts-text-prompt-strategy
  "Obsolete: Alias for kom-old-farts-text-prompt-strategy.")

(def-kom-var kom-pick-text-no-strategy-alist
  '(

    ;; Abstract specifications

    (read-or-written
     (t   (lambda () (lyskom-tnpa-prompt (lyskom-get-text-at-point))))
     (nil lyskom-get-text-at-point
          (lambda () (lyskom-tnpa-valid
                      (lyskom-tnpa-prompt (lyskom-get-last-written-or-read-by-me))))
          :constraint lyskom-text-written-by-me-p))
    (previous-text (nil lyskom-get-previous-text))
    (commented-text (nil lyskom-maybe-get-commented-text))
    (footnoted-text (nil lyskom-maybe-get-footnoted-text))

    ;; Specifications for actual commands

    (kom-comment-previous             :refer previous-text)
    (kom-view-previous-commented-text :refer previous-text)
    (kom-unread-previous-commented-text :refer previous-text)
    (kom-private-answer-previous      :refer previous-text)
    (kom-sub-comment                  :refer commented-text)
    (kom-sub-footnote                 :refer footnoted-text)
    (kom-write-footnote               :refer read-or-written)
    (kom-add-no-comments              :refer read-or-written)
    (kom-add-private-answer           :refer read-or-written)
    (kom-add-request-confirm          :refer read-or-written)
    (kom-move-comment		      :refer read-or-written)

    ;; Default specification

    (t (nil     lyskom-get-text-at-point)
       (0       (lambda () (lyskom-tnpa-prompt (lyskom-get-text-at-point))))
       (lyskom-eq-dash (lambda () (lyskom-get-text-above-point 1)))
       (car     (lambda () (lyskom-get-text-at-point-ancestor (/ (logb (car current-prefix-arg)) 2))))
       (plusp   (lambda () (lyskom-get-text-below-point current-prefix-arg)))
       (minusp  (lambda () (lyskom-get-text-above-point (- current-prefix-arg))))
       (t       (lambda () (lyskom-tnpa-prompt (lyskom-get-text-at-point))))
       :constraint nil
       :filter  nil
       )
    )
  "**Defines how prefix arguments are used by commands to find a text to operate on.

The format of this list is as follows:

<
\((FN-KEY . ((PFX-KEY-1 . (FN1 FN2 ... PROP1 VAL1 PROP2 VAL2 ...)) 
            (PFX-KEY-2 . (FN1 FN2 ... PROP1 VAL1 PROP2 VAL2 ...))
            GPROP1 GVAL1
            GPROP2 GVAL2
            ...))
 ...)
>

`FN-KEY' is the name of a command, `t' or a symbol that does not
correspond to any function. Keys corresponding to commands indicate
configuration for that command. The `t' key indicates fallback
condiguration that applies to all commands, unless overridden by a
command-specific configuration. Other keys can be referenced from
command-specific configurations.

`PFX-KEY' is a key that indicates a prefix. If it is an atom not bound
to a function, it indicates configuration for prefix arguments eq to
the atom \(e.g. 0, - or `nil'). The special key `t' indicates fallback
configuration, used when all prefix-specific configurationas failed to
generate valid values.

`FN' is a function or variable used to generate lists of text numbers.
If a variable name, the value of the variable is used to generate a
list of text numbers. If a function, the function is called with no
arguments, and is expected to return a list of text numbers.

`PROP' is a property name \(see below) and VAL is the corresponding
value. Properties defined at the inner level apply only to results
generated for that prefix. Properties defined at the outer level apply
irrespective of prefix arguments. Properties defined in the default
element \(with `FN-KEY' set to `t'\) always apply, unless overridden by
non-nil properties in command-specific elements.

The following properties are available:

    Key             Value
    ------------------------------------------------------------------
    `:filter'       A function that is called on all text numbers. The
                    filter function is expected to return a new or 
                    modified text number. This can be used to indicate 
                    that the command should prompt or bypass the 
                    constraint.
    
    `:constraint'  A function that is called on all text numbers. It 
                   should return non-nil for text numbers that are 
                   valid for the command.
    
    `:refer'       A symbol indicating a configuration to use. Useful 
                   when several commands share the same (complex)
                   configuration. May not be used in the default 
                   element.
    ------------------------------------------------------------------


The following filter functions may be useful:

    Name                    What it does
    ------------------------------------------------------------------
    `lyskom-tnpa-prompt'    Prompt the user for the text number. The 
                            number used as the argument will be the 
                            default. 
    
    `lyskom-tnpa-valid'     Declare that the text number is valid, 
                            regardless of any constraint functions.
    ------------------------------------------------------------------

When `lyskom-read-text-no-prefix-arg' is called, this list is searched
both for a command-specific element and for the default element. Any
`:refer' links are resolved in the command-specific element. The
filter and constraint properties are extracted. These elements are
then searched for prefix-specific elements that match the current
prefix argument. The first text number that is generated that passes
the filter and constraint functions is used as the text number
argument for the command."
)

(def-kom-var kom-url-transformation-rules 
  '(
    ("^http://[^/]*aftonbladet\\.se/.*/story/.*html?$" . "\\&.")
    )
  "**An alist specifying transformations to be applied to URLs.

Elements in this list are of the form \(`PATTERN' . `REPLACEMENT').
Before an URL is opened, it is transformed by matching the URL against
each `PATTERN' in turn, and when a match is found, generating a new
URL according to `REPLACEMENT'.

All characters in `REPLACEMENT' except \\ are copied verbatim. The
backslash character starts one of the following sequences:

    Sequence    Meaning
    ---------------------------------------------------------------
    `\\&'        Substitute the matched text

    `\\N'        Substitute match for text matching the Nth (...)
                group in `PATTERN'

    `\\\\'        Insert one backslash.
    ---------------------------------------------------------------
"
  server
  )


(def-kom-var kom-check-configuration-on-startup t
  "**When non-nil, check Emacs configuration on client startup.

Valid values for this variable are `t' and `nil'. All other values are
reserved for future extensions. When non-nil, several checks will be
performed:

* Check that multibyte character support is enabled, if available.

* Check that the character coding Emacs uses matches the coding system
  used by the server.

Warnings will be shown in the LysKOM buffer on logon.
")


(def-kom-var kom-keyboard-menu-immediate-selection nil
  "**Controls how keyboard menus work.

When displaying text-based menus (typically by pressing the `=' key
when the cursor is on an active area), there are two modes of
selection. Setting this variable to `t' means pressing the letter
corresponding to an item selects that item. When this variable is
`nil', selection has to be confirmed by pressing enter.

Values other tha `t' and `nil' are reserved for future use."
  server)


(def-kom-var kom-edit-hide-add-button nil
  "**Controls the \"add\" button in the edit buffer.

When set to `t', hide the add button shown after the headers when
editing a text. When set to `nil' (the default), show the button.

Values other than `t' and `nil' are reserved for future use."
  server
)

(def-kom-var kom-max-overlays 120
  "**Maximum number of overlays to use to highlight texts.

When a text is displayed with its own background color (see
`kom-highlight-text-body', `kom-highlight-first-line',
`kom-highlight-dashed-lines', `kom-async-highlight-text-body' and
`kom-async-highlight-dashed-lines') one or more overlays are created.
Since a large number of overlays has a serious impact on performance,
the total number of overlays in the buffer can be limited using this
variable.

When set to a positive integer, limit the number of overlays to that
number. When set to `nil', do not limit the number of overlays. Values
other than a positive integer and `nil' are reserved for future use
and may cause unpredictible results."
  server
)

(def-kom-var kom-highlight-text-body t
  "**Controls highlighting of text bodies.

If `t', overlay `kom-text-body-face' on printed text bodies in LysKOM.
When this is enabled, an overlay or extent will be created that may
override certain aspects of the underlying text's formatting. Values
other than `t' or `nil' are reserved for future use.

See `kom-max-overlays' for information on how to limit the number of
overlays."
  server
)

(def-kom-var kom-highlight-first-line t
  "**Controls highlighting of the first line of text headers.

If `t', overlay `kom-first-line-face' on the first header line of
texts. When this is enabled, an overlay or extent will be created that
may override certain aspects of the underlying text's formatting.
Values other than `t' or `nil' are reserved for future use.

See `kom-max-overlays' for information on how to limit the number of
overlays."
  server
)

(def-kom-var kom-highlight-dashed-lines t
  "**Controls highlighting of dashed lines around texts.

If `t', overlay `kom-dashed-lines-face' on the lines surrounding
texts. When this is enabled, an overlay or extent will be created that
may override certain aspects of the underlying text's formatting.
Values other than `t' or `nil' are reserved for future use.

See `kom-max-overlays' for information on how to limit the number of
overlays."
  server
)

(def-kom-var kom-async-highlight-text-body t
  "**Controls highlighting of asynchronous messages.

If `t', overlay `kom-async-text-body-face' on asynchronous messages in
LysKOM. When this is enabled, an overlay or extent will be created
that may override certain aspects of the underlying text's formatting.
Values other than `t' or `nil' are reserved for future use.

See `kom-max-overlays' for information on how to limit the number of
overlays."
  server
)

(def-kom-var kom-async-highlight-dashed-lines t
  "**Controls highlighting of dashed lines around asynchronous messages.

If `t', overlay `kom-dashed-lines-face' on the lines surrounding
messages. When this is enabled, an overlay or extent will be created
that may override certain aspects of the underlying text's formatting.
Values other than `t' or `nil' are reserved for future use.

See `kom-max-overlays' for information on how to limit the number of
overlays."
  server
)

(def-kom-var kom-extended-status-information nil
  "**Controls display of extra information when displaying LysKOM objects.

If `t', extended status information is listed for all objects in
LysKOM. If set to an association list, each element is a pair of tag
and value. The tag specifies a type of extended information. If the
corresponding value is `t', the information is shown. If the
corresponding value is `nil', the information is not shown.

Valid tags are:

    Tag                   Meaning
    ------------------------------------------------------------------
    `conf'                Show all extended information for conferences

    `pers'                Show all extended information for persons

    `server'              Show all extended information for the server

    `read-faq'            Show read FAQs

    `raw-server-stats'    Show raw server statistics

    `raw-boottime-info'   Show raw boot-time server information

    `t'                   Any information not explicitly listed
    ------------------------------------------------------------------

The information controlled by this variable is the kind of data that
most users are not interested in, and, if displayed, would make more
important information hard to find.

Currently this includes read FAQs (for letterboxes) and full
statistics and boot-time information (for the server).

Values other than those specified are reserved for future extensions."
server )

(def-kom-var kom-auto-list-faqs t
  "**Controls automatic display of FAQs.

When this variable is set to `t', list unread FAQs when entering a
conference or logging on to the server. When entering a conference,
list FAQs for that conference that have not been read. When logging
on, list FAQs for the server that have not been read.

Values other than `t' or `nil' are reserved for future extension."
  server
)

(def-kom-var kom-auto-review-faqs t
  "**Controls automatic review of FAQs.

When set to `t', automatically review unread FAQs when entering a
conference or logging on to the server. When entering a conference,
FAQs that are unread for that conference will be reviewed. When
logging on, FAQs that are unread for the server will be reviewed.

Values other than `t' or `nil' are reserved for future use."

  server
)

(def-kom-var kom-allow-incompleteness nil
  "**Allow or disallow operation on incomplete membership information.

If `nil', commands like `kom-list-news' will wait for all membership
information to be read. If this flag is set to `t', commands will not
wait for all membership information. Not waiting for complete
information allows certain commands to execute faster, particularly
during the login phase, but may result in incorrect or incomplete
answers.

Values other than `t' or `nil' are reserved for future use."
  server
)

(def-kom-var kom-bury-buffers t
  "**Controls the behaviour of `kom-next-kom' and its cousins.

If this variable is `t' the current buffer is sent to the back of the
buffer list when one of the commands `kom-next-kom',
`kom-previous-kom' or `kom-next-unread-kom' is invoked.

Values other than `t' or `nil' are reserved for future use."
  server)

(def-kom-var kom-write-texts-in-window nil
  "**Determines where to edit texts.

The value must be one of `nil', `other', `new-frame', `other-frame', a
string or a buffer.

When set to `nil', edit texts in the same window as the LysKOM buffer.
When set to `other', edit in another window, creating it if necessary.
When set to `other-frame', edit in another frame, if there is one
\(otherwise edit in the same window as the LysKOM buffer). When set to
`new-frame', create a new frame for editing. This frame will be
removed when editing is finished. A string means edit in the buffer
with that name. A buffer means edit in that buffer."
  server)

(def-kom-var kom-view-commented-in-window 'other
  "**Where to view commented texts. 

See the documentation for `kom-write-texts-in-window' for details."
  server)

(def-kom-var kom-edit-filters-in-window nil
  "**Where to edit filters with `kom-filter-edit'.

See the documentation for `kom-write-texts-in-window' for details."
  server)

(def-kom-var kom-list-membership-in-window 'other
  "**Where to list membership with `kom-list-membership'.

See the documentation for `kom-write-texts-in-window' for details."
  server)

(def-kom-var kom-personal-messages-in-window 'other
  "**Where to display personal messages. 

See the documentation for `kom-write-texts-in-window' for details."
  server)

(def-kom-var kom-customize-format 'long
  "**Determines the format of the customize buffer.

This variable currently only controls initial display of documentation
in the customize buffer (the buffer shown by `kom-customize'. When set
to the symbol `long', display documentation. When set to the symbol
`short', don't display documentation.

Values other than `long' or `short' are reserved for future use."
  server)

(def-kom-var kom-user-prompt-format "%[%c% %m%] - "
  "**Format of LysKOM prompt when waiting for input.

The value of this variable must be a string, which is displayed as the
LysKOM prompt, while waiting for a command. The string may contain the
following special sequences:

    Sequence      Meaning
    -----------------------------------------------------------------
    `%c'          Inserts the current default command.

    `%['          Inserts `[' if the ansaphone is on.

    `%]'          Inserts `]' is the ansaphone is on.

    `%m'          Inserts information about recorded messages.

    `%s'          Inserts the name of the LysKOM system

    `%S'          Inserts the server name.

    `%p'          Inserts the name of the user currently logged on.

    `%w'          Inserts the name of the current conference.

    `%a'          Inserts `anonymous' in the current language.

    `%A'          Inserts `Anonymous' in the current language.

    `%#'          Inserts the current session number.

    `% '          Inserts a space if it seems necessary (percent SPC).

    `%%'          Inserts a percent sign.
    -----------------------------------------------------------------

Here are a few examples:

    Format string                 What it does
    -----------------------------------------------------------------
    `\"%[%c% %m%] - \"'           The default prompt

    `\"%[%s: %c% %m%] - \"'       Could display \"LysKOM: Time - \"
    -----------------------------------------------------------------

Note that multiline prompts are not supported."
  server)


(def-kom-var kom-user-prompt-format-executing "%[%c% %m%]."
  "**Format of LysKOM prompt when executing a default command.

The value of this variable must be a string. See
`kom-user-prompt-format' for details."
  server)


(def-kom-var kom-enabled-prompt-format "%[%c% %m%] # "
  "**Format of LysKOM prompt when privileges are enabled..

The value of this variable must be a string. See
`kom-user-prompt-format' for details."
  server)


(def-kom-var kom-enabled-prompt-format-executing "%[%c% %m%]."
  "**Format of LysKOM prompt when executing a default command when
privileges are enabled. 

The value of this variable must be a string. See
`kom-user-prompt-format' for details."
  server)

(def-kom-var kom-anonymous-prompt-format "%[%c% %m%] (%a) - "
  "**Format of the LysKOM prompt when running anonymously.

The value of this variable must be a string. See
`kom-user-prompt-format' for details. See `kom-become-anonymous' for
information on anonymous mode.
"
  server)

(def-kom-var kom-anonymous-prompt-format-executing "%[%c% %m%] (%a)."
  "**Format of the LysKOM prompt when executing a command anonymously.

The value of this variable must be a string. See
`kom-user-prompt-format' for details. See `kom-become-anonymous' for
information on anonymous mode."
  server)

(def-kom-var kom-show-week-number t
  "**Controls display of week numbers in `kom-display-time'.

If set to `t' show the ISO week number when displaying the time. If
set to `nil', don't display the time. All other values are reserved
for future use.

For this feature to work, the calendar package must be installed."
  server)

(def-kom-var kom-show-mx-date-as-local t
  "**Controls display of creation date of imported texts.

If set to `t' then display timestamps of imported texts in the local
timezone instead of the timezone specified in the timestamp."
  server)


(def-kom-var kom-cite-string ">"
  "**String to insert before each line of a commented text.

When citing a text in edit-mode, this string will be inserted before
each line. Although citing is not common and usually not useful in
LysKOM, there is occasionally reason to cite a commented text."
  server)

(def-kom-var kom-created-texts-are-saved nil
  "**If non-nil, save all created texts to a file. 

The value of this variable is the file name on which to save new
texts. Each time a text is created, it is appended to this file.
Values other than a string may produce unpredictible results, and are
reserved for future extensions."
  server
  inherited)

(def-kom-var kom-created-texts-are-read nil
  "**non-nil means automatically mark texts that you create as read.

When set to `t', all text you write are automatically marked as read,
except when running in anonymous mode. When set to `nil', your texts
will be shown as all others.

Values other than `t' and `nil' are reserved for future extensions."
  common created-texts-are-read boolean)

(def-kom-var kom-mark-read-texts-as-read-in-new-recipient t
  "**Controls whether to mark read texts as read in new recipients the acquire.

When this is set to `t', silently mark texts as read when they are
added to a new recipient, if they have been read in any conference.
When `nil', do not mark as read. This feature only works when you are
logged on. Texts added to new conferences while you are not logged on
will not be marked as read in the new recipient.

Values other than `t' or `nil' are reserved for future extensions."
  server)

(def-kom-var kom-customize-in-window nil
  "**Where to customize LysKOM with `kom-customize'.

See the documentation for `kom-write-texts-in-window' for details."
  server)

(def-kom-var kom-prioritize-in-window nil
  "**Where to prioritize conferences with `kom-prioritize'.

See the documentation for `kom-write-texts-in-window' for details."
  server)

(def-kom-var kom-default-mark nil
  "**The default mark used by `kom-mark-text'.

When set to an integer, `kom-mark-text' will not ask for a mark, but
simply mark with that number. When set to `nil', `kom-mark-text' will
ask for a mark.

Values other than integers and `nil' are reserved for future use."
  common default-mark integer
  server)

(def-kom-var kom-symbolic-marks-alist '(("Standard" . 100))
  "**Association list that maps symbolic marks to mark numbers.

Internally, texts can only be marked with numbers. The elisp client
implements symbolic marks through this mapping from mark names to
numbers. Each element of this list is a pair \(`NAME' . `MARK'), where
`NAME' is the name of the mark, and `MARK' is the corresponding
numeric mark."
  server)

(def-kom-var kom-reading-puts-comments-in-pointers-last t
  "**Controls the location where comment pointers are shown.

When set to `t', comment references are shwon at the end of texts.
When set to `nil', comment references are shwon in the headers.

`kom-reading-puts-comments-in-pointers-last' set to `nil'

<
    398331 1996-09-24  13:22  /2 lines/ George Berkeley
    Recipient: Philosophy <1226>
    Comment in text 398374 by John Locke
    Subject: 
    ------------------------------------------------------------
    An abstract idea is a contradiction in terms.
    (398331) -----------------------------------
>

`kom-reading-puts-comments-in-pointers-last' set to `t'

<
    398331 1996-09-24  13:22  /2 lines/ George Berkeley
    Recipient: Philosophy <1226>
    Subject: 
    ------------------------------------------------------------
    An abstract idea is a contradiction in terms.
    (398331) -----------------------------------
    Comment in text 398374 by John Locke
>
"
  common reading-puts-comments-in-pointers-last boolean
  inherited)

(def-kom-var kom-review-uses-cache t
  "**Controls whether review commands use the cache or not.

When set to `t', review commands (such as `kom-review-by-to') will get
texts from the client cache, if possible. This improves performance,
but texts may have changed since they were cached. When set to `nil',
review commands will always get texts from the server.

Values other than `t' and `nil' are reserved for future use."
  server
  inherited)

(def-kom-var kom-review-marks-texts-as-read nil
  "**Controls whether viewing texts with review commands marks then as read.

When set to `t', viewing texts with review commands (e.g.
`kom-review-by-to') will mark unread texts as read. When set to `nil',
texts are not marked as read. 

It is possible to change this setting for a single review command with
the key sequence for `kom-toggle-mark-as-read-prefix'. The value can
be changed for the entire session by using
`kom-make-review-mark-as-read' or `kom-make-review-not-mark-as-read'.

Values other than `t' and `nil' are reserved for future use."
  server
  inherited)

(def-kom-var kom-postpone-default 17
  "**The default number of texts to postpone with `kom-postpone'.

The value of this variable must be a positive integer or zero. All
other values are reserved for future use."
  server)


(def-kom-var kom-dashed-lines t
  "**Controls display of dashed lines before and after texts.

when set to `t' display dashed lines. When set to `nil', don't display
dashed lines.

`kom-dashed-lines' set to `t'

<
    892343 1996-09-24  19:21  /2 lines/ Tycho Brahe
    Recipien: Presentation (of new) Members
    Subject: Tycho Brahe
    ------------------------------------------------------------
    Astronomer and discoverer of stars resident on the island of Ven.
    (892343) -----------------------------------
>

`kom-dashed-lines' set to `nil'

<
    892343 1996-09-24  19:21  /2 lines/ Tycho Brahe
    Recipien: Presentation (of new) Members
    Subject: Tycho Brahe

    Astronomer and discoverer of stars resident on the island of Ven.
    (892343)
>

Values other than `t' and `nil' are reserved for future use. 

See `kom-highlight-dashed-lines' and
`kom-async-highlight-dashed-lines' for additional settings that affect
these lines."
  common dashed-lines boolean
  inherited)

(def-kom-var kom-long-lines nil
  "**If non-nil, some lines and borders will be made longer.

When set to `t', most dashed lines will be longer than the default.
When set to `nil',  use the standard length.

See `kom-text-footer-dash-length', `kom-text-header-dash-length' and
`kom-text-footer-format' for other related settings."
  server
  inherited)

(def-kom-var kom-text-footer-dash-length 52
  "**Control the length of the text footer.

When dashed lines (see `kom-dashed-lines') are in effect, and this is
set to a positive integer, make the text footer at least that many
characters long. Values other than a positive integer are reserved for
future use.

Note that the footer may end up longer than this if one or more elements
together are longer than this length.

This length is currently ignored when `kom-text-footer-format' is used."
  server
  inherited)

(def-kom-var kom-text-header-dash-length 60
  "**Control the length of the text footer.

When dashed lines (see `kom-dashed-lines') are in effect, and this is
set to a positive integer, make the dashed line before the text that
many characters long. 

Values other than a positive integer are reserved for future use."
  server
  inherited)



(def-kom-var kom-text-footer-format nil
  "**Defines the format of the text footer.

When set to a string, use that string as the text footer, overriding
all other settings that affect the text footer. The string may contain
the following special sequences:

    Directive     Meaning
    -------------------------------------------------------------
    `%n'          Insert the text number

    `%p'          Insert the number of the author

    `%P'          Insert the name of the author

    `%-'          Insert a bunch of dashes

    `%f'          Insert special formatting information
    -------------------------------------------------------------

Format directives can be prefixed with a number specifying the minimum
field width (e.g. `%20-'. The field width can be prefixed with an
equals sign (e.g. `%=20p' which means that the field is exactly as
wide as specified (contents may be truncated). A negative field width
means left justify the contents.

The field width of %- is special. It specifies the maximum number of
dashes printed. The actual number will be the maximum minus the length
of the author's name, if it is included anywhere in the format string.

When set, this variable overrides `kom-dashed-lines' and
`kom-show-author-at-end'.

The default format is equivalent to the following strings, depending on 
the settings of kom-dashed-lines and kom-show-author-at-end.

    `kom-dashed-lines'  `kom-show-author-at-end'   Format
    --------------------------------------------------------------------
    `t'                  `t'                       `\"(%n) /%P/%42-%f\"'

    `t'                  `nil'                     `\"(%n) %42-%f\"'

    `nil'                `t'                       `\"(%n) /%P/ %f\"'

    `nil'                `nil'                     `\"(%n) %f\"'
    --------------------------------------------------------------------
"
  server
  inherited)



(def-kom-var kom-show-creating-software nil
  "**Controls display of the software used to create each text.

When creating a text, it is possible to attach information about which
client was used to create the text. When this variable is set to `t',
this information, when available,  is displayed in all text headers.
When this variable is set to `nil', the information is not shown.

To display creating software for a text when this is off, review the
text using `kom-review-noconversion'.

Values other than `t' and `nil' are reserved for future use."
  server
  inherited)

(def-kom-var kom-show-author-at-end t
  "**Controls display of author information in the text footer.

When this is set to `t', display the author of each text immediately
after the text. When set to `nil', the author is not displayed.

`kom-show-author-at-end' set to `t' (with dashed lines on):

<
  892342 1996-09-24  19:21  /2 lines/ Claude Shannon
  Mottagare: Presentation (of new) Members
  Ärende: Claude Shannon
  ------------------------------------------------------------
  Information theoretician
  (892342) /Claude Shannon/------------------------------
>

`kom-show-author-at-end' set to `nil':

<
  892342 1996-09-24  19:21  /2 lines/ Claude Shannon
  Recipient: Presentation (of new) Members
  Subject: Claude Shannon
  ------------------------------------------------------------
  Information theoretician
  (892342) -----------------------------------
>

If `kom-text-footer-format' is set, the value of this variable is
ignored. Values other than `t' and `nil' are reserved for future use."
  server
  inherited)

(def-kom-var kom-truncate-threshold nil
  "**Controls truncation of text when reviewing.

When set to a positive integer, truncate long texts when reviewing. If
the text has more lines than the value of this variable, it will be
truncated to `kom-truncate-show-lines' lines. When a message is
truncated, a note to that effect will be shown in the message footer
unless `kom-text-footer-format' has been set to a string that does not
include the `%f' directive.

When set to `nil', no truncation will occur. Values other than
positive integers and `nil' are reserved for future use."
  server)

(def-kom-var kom-truncate-show-lines 10
  "**Number of lines to display when truncating texts.

The value must be a positive integer. When a text is truncated \(see
`kom-truncate-threshold', the minimum of `kom-truncate-threshold' and
`kom-truncate-show-lines' determines the number of lines to show.

Values other than positive integers are reserved for future use."
  server)


(def-kom-var kom-print-number-of-unread-on-entrance t
  "**Controls display of number of unread when entering a conference.

When set to `t', the number of unread texts in a conference is
displayed when entering that conference. When set to `nil', the number
of unread texts is not shown. Shortly after logging on, using
`kom-set-unread' or using `kom-recover', the number shown may be
incorrect.

Values other than `t' and `nil' are reserved for future use."
  common print-number-of-unread-on-entrance boolean)

(def-kom-var kom-show-unread-in-frame-title t
  "**Controls display of the unread indicator in the frame title.

When set to `t', an indicator will be shown in the title of each frame
containing a selected LysKOM session with unread texts. When set to
`nil' no indicator will be shown.

Values other than `t' and `nil' are reserved for future extensions."
  server)

;; In the common block of the user-area is a simple boolean variable
;; `presence-messages'.  The control of presence messages in the Emacs
;; Lisp client is more advanced, using two non-boolean variables instead
;; (`kom-presence-messages-in-echo' and `kom-presence-messages-in-buffer').
;;
;; Keep track of the common block variable too, although it isn't used here.
;; `lyskom-save-options' sets it before saving it in obvious cases,
;; but generally it's not possible to know what value it should have.
;; (It could also be used for default values for the presence options for
;; users converting from another client, but that isn't done.)

(def-kom-var kom-presence-messages t
  "Equivalent to the variable `presence-messages' in the user-area.
This is a boolean where other clients may store whether the user wants
messages about people logging in and out of LysKOM.  Here this is instead
controlled by `kom-presence-messages-in-echo-area' and
`kom-presence-messages-in-buffer', so only use this variable to
influence what this client stores in the common block of the user-area."
  common presence-messages boolean)

(def-kom-var kom-presence-messages-in-echo-area t
  "**Controls display of presence messages in the echo area.

If non-nil, LysKOM prints continuous info about what other people
are doing. Info is printed on the echo area and never in the buffer.
If minibuffer is used, no message is printed.

The value `t' means show messages for everyone.

A list is treated as an `indirect association list', and is the
most flexible way to configure this variable. When a user logs
in, the person's number is matched against the list, and if the
value of the match is non-nil, a message is shown. The matching
rules are as follows:

  Element           Meaning
  ------------------------------------------------------------------
  `ATOM'            Matches with the default value if ATOM equals 
                    the person number (this means that you can set 
                    this variable to a list of person numbers).

  `(t . VAL)'       Matches with value VAL if no other match is 
                    found (i.e. sets the default value).

  `(FN . VAL)'      Matches with value VAL if FN is a function that
                    returns non-nil when called with the person 
                    number as its sole argument.

  `(SYM . VAL)'     If SYM is bound to a list, match the person 
                    number against that list using these rules. 
                    VAL is used as the default value for the 
                    matching process.

  `(SYM . VAL)'     If SYM is not bound to a list, matches with 
                    the value VAL if the value of SYM equals the
                    person number.

  `(LIST . VAL)'    Match the person number against LIST using
                    these rules. VAL is used as the default value.

  `(ATOM . VAL)'    Match with value VAL if ATOM equals the 
                    person number.
  ------------------------------------------------------------------

For example, the value `((5 . nil) (kom-friends . t))' will show
a message for every person listed in `kom-friends', except for
person number 5.

The following values work but are obsolete as they can be
expressed by a list:

The value `friends' means show messages for the users in
`kom-friends'. Equivalent to the value `((kom-friends . t))

The value `morons' means show messages for the users in
`kom-morons'. Equivalent to `((kom-morons . t)).

The value `friends-and-morons' means show messages for the users
in `kom-friends' and `kom-morons'. Equivalent to the
value `((kom-friends . t) (kom-morons . t))'.

If you want the messages in the buffer, set the variable 
`kom-presence-messages-in-buffer'.

Values other than those listed are reserved for future use."
  server
  copy-transition lyskom-copy-indirect-assq)

(def-kom-var kom-presence-messages-in-buffer nil
  "**Controls display of presence messages in the LysKOM buffer.

If non-nil, LysKOM prints information about what other people are
doing in the LysKOM buffer. All printing is done just before the
prompt.

See `kom-presence-messages-in-echo-area for a list of permitted
values."
  server
  copy-transition lyskom-copy-indirect-assq)

(def-kom-var kom-unread-mode-line-type nil
  "**Controls how information about unread sessions is shown in the mode line.

If this variable is `nil', there will be a single prompt indicating
whether you have unread texts or letters in any active session.

If this variable is `t', the mode line will indicate each session with
unread texts. Unread letters are indicated by upper-casing the session
name or (if the session name is in upper case already) surrounding it
with asterisks.

All other values are reserved for future use."
  server
  )


(def-kom-var kom-show-where-and-what t
  "**Controls display of hostname and activity information in `kom-who-is-on'. 

When this is set to `t', `kom-who-is-on' and related commands will
display the machine the session is connected from and what the user is
doing. When set to `nil', this information will not be shwon.

Listing from `kom-who-is-on' with `kom-show-where-and-what' set to `t':

<
      User                                   Is in conference             
      At                                     Activity                        
  --------------------------------------------------------------------------
   6810 George Berkeley                      Philosophy                     
        berkeley@emp1.tcd.ie                 (Writing a comment.)           
   7571 John Locke                           Philosophy                     
        eridy@cc.ox.ac.uk                    (Waiting.)                     
  --------------------------------------------------------------------------
>

Listing from `kom-who-is-on' with `kom-show-where-and-what' set to `nil':

<
      User                                   Is in conference             
  --------------------------------------------------------------------------
   6810 George Berkeley                      Philosophy                     
   7571 John Locke                           Philosophy
  --------------------------------------------------------------------------
>

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-show-since-and-when nil
  "**Controls display of connection time in `kom-who-is-on'.

When set to `t', `kom-who-is-on' and related commands will display the
time when each session connected to the server and when each session
last indicated activity.

Listing from `kom-who-is-on' with `kom-show-since-and-when' set to `t':

<
        User                                 Is in conference             
        Connected                            Active last                  
  --------------------------------------------------------------------------
   6810 George Berkeley                      Philosophy                     
        Thursday 2003-01-09 09:24:56         Active                         
   7571 John Locke                           Philosophy                     
        Saturday 2003-01-11 14:02:47         12 minutes                     
  --------------------------------------------------------------------------
>

Listing from `kom-who-is-on' with `kom-show-since-and-when' set to `nil':

<
        User                                 Is in conference             
  --------------------------------------------------------------------------
   6810 George Berkeley                      Philosophy                     
   7571 John Locke                           Philosophy
  --------------------------------------------------------------------------
>

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-idle-hide 30
  "**Controls display of idle sessions in `kom-who-is-on'.

When set to a positive integer, the number of minutes of idletime
before a user is excluded from the list of users shown by
`kom-who-is-on' and related commands. This can be overridden by a
prefix argument to these commands. When set to `nil', do not hide idle
sessions.

Variables other than positive integers and `nil' are reserved for
future use."
  server)

(def-kom-var kom-show-footnotes-immediately t
  "**Controls display of footnotes.

When set to `t', footnotes will be displayed immediately following the
text. When set to `nil', footnotes will be displayed like regular
comments.

Values other than t and `nil' are reserved for future use."
  server)

(def-kom-var kom-follow-comments-outside-membership nil
  "**Controls display of comments in conferences you are not a member of.

If this variable is set to `nil', texts with no recipient you are a
member of will be ignored when selecting comments to display when
reading. When set to `t', the client will follow comment links
regardless of whether you are a member of any of the recipients of the
comment.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-follow-attachments t
  "**Controls how attachments to imported e-mail messages are handled.

When this is set to `t', attachments are followed like regular
comments. When set to `nil', attachments are not followed. To see them
you have to review them manually.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-read-depth-first t
  "**Determines the order in which to read texts.

When set to `t' \(the default), texts are read in comment order. When
set to `nil', texts are read in chronological order.

When reading in comment order, the next text to read is the first
comment of the most recently read text. If there are no comments, the
next text is a comment to another text. Reading in comment order lets
you view threads of discussion.

Values other than `t' or `nil' are reserved for future use."
  common read-depth-first boolean)

(def-kom-var kom-read-related-first 'oldest-ancestor
  "**Read unread related texts first?

Once a candidate text to read has been found (using the rules defined
by the `kom-read-depth-first' variable), the choice can be further
refined as specified by this variable.

When set to `nil', no refinement is made.  When set to
`oldest-ancestor' \(the default), unread parents are shown first.
When set to `oldest-related', children of the parents are also
considered.

Other values are reserved for future use."
  server)

(def-kom-var kom-continuous-scrolling t
  "**Controls frequency of scrolling.

When set to `t', the LysKOM buffer is scrolled whenever text is
inserted. The last viewed position \(usually the most recent prompt)
will always be visible.

When set to `nil', scrolling takes place less frequently. This is
a suitable setting when running Emacs over a slow link, such as an old
modem.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-deferred-printing t
  "**Controls background display of non-cached information.

When set to `t', delay display of \(some) information that has to be
retreived from the server. A placeholder is shown where the
information will be printed, until it is available. This setting
results in a significant improvement in interactive performance.

When set to `nil', do not delay display of any information. This
lowers interactive performance, but may be suitable when running Emacs
over a slow connection.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var lyskom-overlay-pool nil  
  "Pool of overlays"
  local)

(def-kom-var lyskom-defer-indicator "[...]"
  "String to display while LysKOM is waiting for the real string.")

(def-kom-var kom-review-priority nil
  "**Priority to use when reviewing texts.

When set to a positive integer, use that as the priority to use when
reviewing texts. By setting `kom-review-priority' higher than 255 (the
maximum conference priority), new texts to conferences will not break
in while reviewing.

If set to `nil', use the current priority while reviewing. This allows
texts to conferences with priorities higher than that of the current
conference to break in while reviewing (depending on the setting of
`kom-higher-priority-breaks').

Values other than positive integers and `nil' are reserved for future
use."
  server)

(def-kom-var kom-higher-priority-breaks nil
  "**Controls how conference priorities are handled.

When set to `express', texts to conferences with a higher priority
than the current conferences will be shown as soon as the arrive. When
set to `t', texts to prioritized conferences will be shown after the
current comment tree. When set to `nil', prioritized conferences will
be visited when all texts in the current conference have been read.

Values other than `express', `t' and `nil' are reserved for future
use."
  server)

(def-kom-var kom-server-priority-breaks nil
  "**Controls how server priorities are handled.

A non-nil value allows servers with a higher priority than the
current server to break in when new texts arrive. This can be used to
give a work-related server a higher priority than a server used for
frivolous purposes (or, indeed, the other way around).

The following settings are available:

  Value                 Meaning
  --------------------------------------------------------------------
  `express'             Break in immediately when there are unread
                        texts.

  `express-letters'     Break in immediately when there are unread
                        letters.

  `t'                   Break in after the current comment chain when
                        there are unread texts.

  `letters'             Break in after the current comment-chain when
                        there are unread letters.

  `after-conf'          Break in after the current conference when
                        there are unread texts.

  `after-conf-letters'  Break in after the current conference when
                        there are unread letters.

  `when-done'           Prompt the user to go to the next session with
                        unreads after everything has been read. This
                        overrides `kom-do-when-done' as long as there
                        are sessions with unread texts. 
  --------------------------------------------------------------------

Values other than those listed above are reserved for future use and
may result in unpredictible behavior.

See `kom-server-priority' for information on setting the server
priority."
  server)


(def-kom-var kom-session-nickname nil
  "**Nickname for the current LysKOM session.

This variable contains the name of the current LysKOM session (a string)
or `nil'. If set, it will be used as the nickname for this LysKOM session.

See also `kom-server-aliases' and `kom-builtin-server-aliases'."
  server
  non-portable)

(def-kom-var kom-view-text-hook nil
  "**Hook that is called before a text is shown. 

When the hooks are called, `lyskom-view-text-text' is bound to the text
mass of the text and `lyskom-view-text-text-stat' to the text-stat of the
text to be shown."
  local-hook)

(def-kom-var kom-send-message-hook '(lyskom-send-message-trim-newlines)
  "**Hook that is called before a personal, group or common message is sent. 

When called, `lyskom-message-string' is bound to the message that will
be sent and `lyskom-message-recipient' to the conf-stat of the
recipient or nil if the recipient does not exist or if the message is
a common message.

If `lyskom-message-string' is set to nil by a hook, the message will not
be sent."
  local-hook)

(def-kom-var kom-send-message-setup-hook nil
  "**Hook that is called when the minibuffer is entered to read a message.

This hook can be used to set up the minibuffer in a way suitable for
writing messages. For example, the hook might enable `auto-fill-mode'
or set up automatic resizing of the minibuffer."
  local-hook)

(def-kom-var kom-send-message-exit-hook nil
  "**Hook that is called when the minibuffer is exited after reading a message.

Typically this hook will be used to undo the effects of
`kom-send-message-setup-hook'."
  local-hook)

(def-kom-var kom-send-text-hook nil
  "**Hook that is called before sending a text.

This hook is called before the headers are parsed, so it is possible
for the headers to be modified after this hook is called. It is also
possible that the text will not be sent at all."
  local-hook)

(def-kom-var kom-after-load-hook nil
  "**Hook to run once after LysKOM is loaded.

This is similar in effect to using `eval-after-load', but is
independent of the file name of the client.")

(def-kom-var lyskom-after-load-hook nil
  "Obsolete synonym for kom-after-load-hook.")

(def-kom-var kom-change-conf-hook nil
  "**Hook to run when changing conferences.

The functions in this list are run with two arguments. The first is the 
current conference number and the second is the number of the
conference being changed to.

This hook is run before `lyskom-current-conf' is changed, and before
any standard messages have been printed."
  local-hook)

(def-kom-var kom-after-change-conf-hook nil
  "**Hook to run when changing conferences.

The functions in this list are run with two arguments. The first is the 
current conference number and the second is the conference number
being changed to.

This hook is run after `lyskom-current-conf' is changed, and after any
standard messages have been printed."
  local-hook)

(def-kom-var lyskom-login-hook nil
  "**Hook called while logging in.

This hook is called after the session is logged in but before any
command is accepted from the keyboard. It is called immediately before
`kom-login-hook'."
  local-hook)

(def-kom-var kom-login-hook nil
  "**Hook called while logging in.

This hook is called after the session is logged in but before any
command is accepted from the keyboard. Unlike `lyskom-login-hook' it
can be stored in the server."
  server)

(def-kom-var kom-relogin-inhibit-commands '(kom-next-kom 
					    kom-previous-kom 
					    kom-where-is 
					    kom-next-unread-kom)
  "**Commands for which relogin is disabled.

This should be set to a list of commands for which relogin should not
be offered or used."
  server)

(def-kom-var kom-relogin-behaviour 'ask
  "**Controls how to behave when commands are issued in dead sessions.

When this is set to `t', the client will try to login automatically.
When set to `ask', the client will ask whatever the user wants to
reattach. When set to `nil', the client will not attempt to reattach."
  server)

(def-kom-var kom-remember-password nil
  "**Controls whatever to store the password in the session buffer.

When this is set to `t', the client will store the password for the 
current session as a buffert local variable. This can be used in
conjunction with `kom-relogin-behaviour' to reattach dead sessions
automatically."
  server)

(def-kom-var kom-confirm-add-recipients t
  "**Controls confirmation when adding recipients.

When this is set to `t', confirm the recipient type when adding
recipients. Commands like `kom-add-recipient' will pose a question to
determine the type of recipient to add. When set to `nil', assume that
the user always wants full recipients when using `kom-add-recipient'
and similar commands.

Values other than `t' and `nil' are reserved for future use."
  server)


(def-kom-var kom-do-when-done '(kom-review-all-marked-texts kom-display-time)
  "**What to do when all texts are read.

This is a list of commands and lists of commands that are prompted for
and executed when there are no more new texts. The last command in the
list will be prompted for over and over until new texts arrive.

The values in the list can be a LysKOM command (a symbol), an Emacs
command or a keyboard macro (a string).

Values other than those listed are reserved for future use."
  server)

(def-kom-var kom-page-before-command nil
  "**Controls clearing of the screen prior to certain commands.

When this variable is set to `t', all commands will execute at the top
of the LysKOM window. When a command is issued, the buffer will be
scroll so the last prompt is on the first line of the buffer.

When set to a list of LysKOM commands \(symbols), those commands will
execute at the top of the window. All others will execute without
scrolling the buffer.

When set to `nil', never scroll the buffer in the way described.

Values other than those listed are reserved for future use."
  server)

(def-kom-var kom-permissive-completion t
  "**Controls completion of logged-in sessions.

When the client reads names of logged-in users in the minibuffer \(e.g.
`kom-status-session'), it can permit completion of all users or just
those that are logged on.

If this variable is set to `t', completion will include all users,
including those that are not logged in. If this variable is set to
`nil', completion will be restricted to users who are logged in.

Setting this variable to `t' may improve performance significantly,
particularly on servers with many sessions.

Values other than `t' or `nil' are reserved for future use."
  server)

(def-kom-var kom-unsubscribe-makes-passive t
  "**Controls behavior of `kom-sub-self'.

If this variable is set to `t', leaving a conference with
`kom-sub-self' will make the membership passive. Leaving a second time
\(while the membership is still passive) will remove the membership
entirely. 

When set to `nil', `kom-sub-self' removes the membership immediately.

Values other than `t' and `nil' are reserved for future use."
  server)


(def-kom-var kom-membership-default-priority 'ask
  "**Default priority when joining a new conference.

If set to a valid priority (integer from 0 to 255) then new
conferences are read with this priority. When set to the symbol `ask',
the client will ask for a priority when joining new conferences.

Values other than those listed are reserved for future use."
  server)

(def-kom-var kom-membership-default-message-flag 'ask
  "**Default message flag when joining a new conference.

If set to `nil', messages will not be received for new conferences,
if set to `t', messages will be received. When set to the symbol `ask',
the client will ask if you want to receive messages when you join new 
conferences.

Values other than those listed are reserved for future use."
  server)

(def-kom-var kom-membership-default-placement 'last
  "**Default placement of new memberships.

This variable controls the placement of memberships within the
membership list when you join a conference. Note that the membership
priority has precedence over this position.

The value can be `first', `last' or a number. When set to `first', the
membership will be entered before all others. When set to `last', it
will be entered after all others. When set to an integer, the
membership is entered at that position in the list.

All other values are reserved for future use."
  server)

(def-kom-var lyskom-current-prompt nil
  "The current prompt or nil.
This is either nil, indicating that there is currently no prompt, or
a symbol indicating which command is prompted in the LysKOM buffer."
  local)

(def-kom-var lyskom-current-prompt-text nil
  "The current prompt text or nil.
This is either nil, indicating that there is currently no prompt, or
a string indicating the prompt shown in the LysKOM buffer."
  local)

(def-kom-var lyskom-current-prompt-args nil
  "The current prompt arguments.
These are arguments used to format the current prompt."
  local)

(def-kom-var lyskom-current-prompt-timestamp nil
  "The creationtime of the current prompt.
This is used when updating the prompt and on `lyskom-start-of-command'."
  local)

(def-kom-var lyskom-need-prompt-update nil
  "Non-nil if all prompts need to be updated."
  local)

(def-kom-var kom-show-personal-messages-in-buffer t
  "**Buffer to show personal, group and alarm messages in.

This variable controls which buffer personal, group and alarm messages
are shown in. When set to `nil', all messages are silently discarded.
When set to `t', messages are shown in the main LysKOM buffer. When
set to a string, messages will be inserted in a buffer by that name
\(one will be created if necessary). When set to a buffer, messages
are inserted in that buffer.

All values other than those listed are reserved for future use."
  server)

(def-kom-var kom-pop-personal-messages nil
  "**Non-nil means pop up a buffer with personal messages as they arrive.

When this variable is set to `nil', personal, group and alarm messages
are simply inserted in the appropriate buffer \(see
`kom-show-personal-messages-in-buffer'). The buffer is not displayed
automatically. When this variable is set to `t', the buffer in which a
new message is shown will be displayed automatically, possibly
splitting windows and uniconifying frames. When this variable is set
to `yes', the behavior is identical to when it is set to `t', but
frames will not be uniconified.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-lost-session-notification 'all-buffers
  "**How (and if) notification of lost sessions will be handled.

Lost (abnormally terminated) sessions are always notified in the
session buffer. If this parameter is set to `beep', the client will
also beep.  If it is set to `all-buffers', the client will beep and
insert a notification message in any active LysKOM buffer.

Values other than `all-buffers', `beep' and `nil' are reserved for
future use."
  server)

(def-kom-var kom-ding-pause-amount 0.1
  "**Number of seconds to wait between successive beeps.

When multiple beeps are used as audible notification, this determines
the amount of time (in seconds) to wait between successive beeps. The
value must be a positive number (it may be a floating point number).")

(def-kom-var kom-ding-on-priority-break 1
  "**Non-nil means ding if a higher priority text or conference breaks in.

This variable must be set to a `t', `nil', a positive integer, a
string or a function. When set to `t' use the default beep. When set
to `nil', do not beep. When set to a number, beep that many times
\(see `kom-ding-pause-amount' for additional settings). When set to a
function, call that function. When set to a string, run the program
indicated by `kom-audio-player' with the string as its sole argument."
  server)

(def-kom-var kom-ding-on-new-letter nil
  "**Non-nil means ding if a message arrives in the letter box. 

See `kom-ding-on-priority-break' for valid values."
  server)


(def-kom-var kom-ding-on-wait-done 1
  "**Non-nil means ding when `kom-busy-wait' terminates.

See `kom-ding-on-priority-break' for valid values."
  server)

(def-kom-var kom-ding-on-common-messages 0
  "**Non-nil means ding when an alarm message arrives. 

See `kom-ding-no-priority-break' for valid values. In addition to
those listed there, the value of this variable may be a list of
elements like \(`KEY' . `VALUE'). If the sender (a conference number) is
found as the key of any element, the value of that element will be
used to generate the ding (valid values are those listed for
`kom-ding-on-priority-break'). The special key `t' is used when no other
key matches the sender."
  server)

(def-kom-var kom-ding-on-group-messages 1
  "**non-nil means ding when a group messages arrives.

See `kom-ding-on-common-messages' for valid values."
  server)

(def-kom-var kom-ding-on-personal-messages 2
  "**non-nil means ding when a personal message arrives.

See `kom-ding-on-common-messages' for valid values."
server)


(def-kom-var kom-ding-on-no-subject 2
  "**How to ding if the user has not entered a subject line.

When attempting to submit a text without a subject line, this variable
determines how to beep. See `kom-ding-on-priority-break' for valid
values."
  server)


(def-kom-var kom-audio-player "audioplay"
  "**Program used to play audio files.

If a of the variables that control audio signals (dings, beeps) is set
to a string, the command (shell command, not Emacs command) indicated
by this variable will be called with that string as an argument.
Therefore, this variable should be set to the name of an external
command that takes a single argument: a sound file to play.

Non-string values are reserved for future use."
  server)


(def-kom-var kom-ignore-message-senders nil
  "**List of senders whose personal, group and alarm messages are ignored.

The value of this variable must be a list of person numbers. Personal,
group and alarm messages sent by users in this list will be silently
ignored.

Values other than those listed are reserved for future use."
  server
  copy-transition lyskom-copy-indirect-assq)

(def-kom-var kom-ignore-message-recipients nil
  "**List of recipients you do not want group messages to.

The value of this variable must be a list of conference numbers.
Messages sent to a recipient listed will be silently ignored.

Values other than those listed are reserved for future use."
  server
  copy-transition lyskom-copy-indirect-assq)

(def-kom-var kom-show-personal-message-date t
  "**Controls display of date on personal messages.

When set to `t', display the date and time when personal, group and
alarm messages arrived. When set to `nil', only display the time.

`kom-show-personal-message-date' set to `t'>:

<
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  Alarm message from Claude Chappe (1805-01-23 17:34):
  
  Je me donne la mort our éviter l'ennui de la vie qui m'accable; je
  n'ai point de reproches à me faire.
  ----------------------------------------------------------------
>

`kom-show-personal-message-date' set to `nil':

<
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  Alarm message from Claude Chappe (17:34):
  
  Je me donne la mort our éviter l'ennui de la vie qui m'accable; je
  n'ai point de reproches à me faire.
  ----------------------------------------------------------------
>

See Bulletin of Les Amis de Paris Central Télégraphe, no. 20, July
1993 for information on the quote above.
"
  server)

(def-kom-var kom-default-message-recipient 'group
  "**Determines default recipient of personal messages.

When set to `everybody', the default recipient of all messages will be
all users (i.e. the default is to send alarm messages). This may not
work properly. 

If set to `group', the default recipient depends on the message most
recently received. If that message was a group message, then the
default recipient will be the recipient of that message. If that
message was a personal message or an alarm message, the default
recipient will be the sender of the most recent message.

If set to `sender', the default recipient is always the sender of the
most recently received message, regardless of its type.

Values other than those listed are reserved for future use."
  server)


(def-kom-var kom-filter-outgoing-messages t
  "**Determines whether automatic outgoing messages are shown.

When set to `t', outgoing remote control messages and automatic
replies are not displayed. When set to `nil', these messages are shown
the same way normal messages are shown when sent.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-highlight-conferences 
  '((kom-friends . kom-friends-face)
    (kom-morons  . kom-morons-face)
    (lyskom-highlight-has-no-presentation . kom-active-strikethrough-face)
    (lyskom-pers-no . kom-me-face)
    (lyskom-highlight-i-am-supervisor . kom-active-highlight-face))
  "**How to highlight conference an person names.

The value of this variable is an alist whose keys are matched against
conference numbers and whose values are the names of faces to use for
matching conferences.

The following values are legal for keys:

    Value       Meaning
    -----------------------------------------------------------------
    Symbol      If the value is a list, matches if the conference
                number is in the list. If the value is an integer,
                matches if the conference number matches the integer.

    List        Matches if the conference number is in the list.

    Function    Matches if the function returns non-nil (see below).
    -----------------------------------------------------------------

When using a function as the key, the function will be called with a
single argument, the object to be printed. The type of the argument
will vary depending on how it is printed. Functions should be written
so they work regardless of what is passed to them.

All other values are reserved for future use."
  server
  copy-transition lyskom-copy-indirect-assq)

(def-kom-var kom-person-list-1 nil
  "**General list of persons. 

This variable has no predefined meanind and is intended for use
in variables such as `kom-presence-messages-in-buffer' and
`kom-highlight-conferences'."
  server
  non-portable)

(def-kom-var kom-person-list-2 nil
  "**General list of persons. 

This variable has no predefined meanind and is intended for use
in variables such as `kom-presence-messages-in-buffer' and
`kom-highlight-conferences'."
  server
  non-portable)

(def-kom-var kom-person-list-3 nil
  "**General list of persons. 

This variable has no predefined meanind and is intended for use
in variables such as `kom-presence-messages-in-buffer' and
`kom-highlight-conferences'."
  server
  non-portable)

(def-kom-var kom-person-list-4 nil
  "**General list of persons. 

This variable has no predefined meanind and is intended for use
in variables such as `kom-presence-messages-in-buffer' and
`kom-highlight-conferences'."
  server
  non-portable)

(def-kom-var kom-friends nil
  "**List of friends and other nice people.

The value of this variable is a list of person numbers. People listed
here will be displayed using the face in `kom-friends-face'. They can
also receive special treatment in other cases (see
`kom-presence-messages').

See `kom-morons' for a related variable.

Values other than a list of integers are reserved for future use."
  server
  non-portable)

(def-kom-var kom-morons nil
  "**List of people morons and other nasty people.

The value of this variable is a list of person numbers. People listed
here will be displayed using the face in `kom-morons-face'.

See `kom-friends' for a related variable.

Values other than a list of integers are reserved for future use."
  server
  non-portable)

(def-kom-var kom-dont-check-commented-authors nil
  "**A list of recipients that don't need to see comments to their texts.

When writing a comment, the client can check that the author of the
commented text is a member of at least one of the recipients. If that
is not the case, the user will be offered to add the author as a
recipient.

This variable lists authors who shold not be checked in this manner.
Typically it will contain a list of import agents.

Values other than a list of integers are reserved for future use."
  server
  inherited
  copy-transition lyskom-copy-indirect-assq)

(def-kom-var kom-smileys t
  "**Controls display of graphical smileys.

When set to `t', display graphical smileys instead of `:-)' and
similar character sequences. When set to `nil', do not display
graphical smileys.

For this to work at all you must have the `smiley' or `smiley-ems'
installed (usually installed with Gnus) and support for graphics (part
of XEmacs and Gnu Emacs 21.x and later).

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-text-properties t
  "**Controls use of text properties in LysKOM (fonts and stuff).

When set to `t', the client will freely use text properties, like
fonts. When set to `nil', the client will not use text properties.
This will disable all font usage and clickable stuff.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-fontify-text t
  "**Controls whether plaintext messages are fontified.

When set to `t', the client will make words and phrases delimited by
asterisks bold, and words and phrases delimited by underscore italic.
Other similar features may be added in the future.

Values other than `t' or `nil' are reserved for future use."
  server)


(def-kom-var kom-use-button-hints t
  "**Controls use of context sensitive actions on clickable areas.

When set to `t', clickable areas of the same type may behave
differently depending on what command created them. For example, in
one case clicking a conference name may show the presentation of that
conference and in another it will go to the conference.

Values other than `t' and `nil' are reserved for future use.")

(def-kom-var kom-autowrap t
  "**Controls automatic text wrapping.

When set to `t', automatically break long lines in texts. A set of
rules attempts to limit line breaking to regular, unformatted text.
This works most of the time, but occasionally fails (in which case
`kom-review-noconversion' comes in handy).

When set to an integer, perform automatic line breaking in text no
longer than that many characters. 

When set to `nil', do not break lines automatically at all.

Values other than `t', `nil' and integers are reserved for future
use."
  server)

(def-kom-var kom-autowrap-timeout 5
  "**Limits how much time automatic text wrapping may take.

When set to an integer, limits the number of seconds automatic line
breaking may take. Without a limitation, breaking lines of a very long
text can take a very, very long time. Note that this setting is
approximate; the actual time spent breaking lines may be several
seconds longer than this value. Furthermore, using this setting slows
down all text display slightly.

When set to `nil', do not limit the amout of time automatic line
breaking may take.

Values other than `nil' and integers are reserved for future use."
  server)

(def-kom-var kom-keep-alive-interval 180
  "**Polling interval for `kom-keep-alive'.

The command `kom-keep-alive' polls the server periodically to keep the
connection active. This variable specifies how many seconds to wait
between these periodic requests to the server.

Values other than integers are reserved for future use."
  server)

(defvar lyskom-transforming-external-text nil
  "Dynamically bound to non-nil when transforming text in which text,
conference and person buttons are not expected.")

(def-kom-var lyskom-url-protocol-regexp
  "\\(file\\|ftp\\|gopher\\|http\\|https\\|news\\|wais\\|mailto\\|telnet\\):"
  "Regexp to match the protocol part of a URL.")

(def-kom-var lyskom-text-buttons
  '(
    ;; Text numbers
    ("\\(\\<[0-9][0-9][0-9][0-9]\\([0-9]\\)?\\([0-9]\\)?\\([0-9]\\)?\\([0-9]\\)?\\([0-9]\\)?\\>\\)" 
                                        ; Match
     text                               ; Button type
     0                                  ; Portion that's a button
     1                                  ; Portion that's the arg
     nil                                ; Face or nil (=default)
     )

    ;; Email

    ("\\(\\b\\|^\\)[^()<>@,;:\"\\\\\000- ]+@[^\000- <>;,.'\"!:?) \t\012\014]+\\(\\.[^\000- <>;,.'\"!:?)]+\\)+"
     email 0 0 kom-url-face)

    ;; URLs

    ("\\b\\(www\\|ftp\\|home\\)\\.[^\t \012\014\"<>|\\]*[^][\t \012\014\"<>|.,!(){}?'`:;]" 
     pseudo-url 0 nil kom-url-face)

    ("\\(google:\\|file://\\|ftp://\\|gopher://\\|rtsp://\\|http://\\|https://\\|news:\\|wais://\\|mailto:\\|telnet:\\)[^\t \012\014\"<>|\\]*[^][\t \012\014\"<>|.,!(){}?'`:;]" 
     url 0 nil kom-url-face)

    ("<URL:\\([^<>]+\\)>"
     pseudo-url 1 1 kom-url-face lyskom-is-url)
    ("<\\([^<>]+\\)>"
     pseudo-url 1 1 kom-url-face lyskom-is-url)

    ;; JySKom enhancements

    ("<(?m[|ö]te[ \t\n\r]*\\([0-9]+\\)\\([^0-9>]?\\|[^0-9>][^>]*\\))?>"
     conf 0 1 nil)
    ("<(?text[ \t\n\r]*\\([0-9]+\\)\\([^0-9>]?\\|[^0-9>][^>]*\\))?>"
     text 0 1 nil)
    ("<(?person[ \t\n\r]*\\([0-9]+\\)\\([^0-9>]?\\|[^0-9>][^>]*\\))?>"
     pers 0 1 nil)

    ;; Info node reference

    ("\\*Note[ \n\t]+\\([^:\n]*\\(\n[^:\n]*\\)?\\):\\s-*\\(\\(([^\)]+)\\)?[^.,\t\n]*\\(\n[^.,\t\n]*\\)?\\)[.,\t]" 
     info-node 1 3 kom-url-face)

    )
  "List of buttons to install in the text mass of LysKOM objects. Each 
element is a list consisting of REGEXP TYPE BUTTON-MATCH BUTTON-ARG-MATCH
FACE &optional PRED.

REGEXP is the regexp to look for in the text.
TYPE is the button type. Valid button types are defined in lyskom-button-actions.
BUTTON-MATCH is the number of the parenthesized expression that is the actual button.
BUTTON-ARG-MATCH is the number of the expression to be used as the button argument.
FACE is the text face to apply to the button, or nil to use the default face.
If PRED is given, it is a function that will be passed the matched string; if 
it returns non-nil, the match is considered valid.")

(def-kom-var kom-url-viewer-preferences '("emacs"
                                          "windows"
                                          "w3")

  "**Specifies application preferences for opening URLs.

This is a list of URL handlers to try when opening a URL. Each handler
is associated with a set of protocols. An URL will be opened by the
first handler in the list that is associated with the URLs protocol.

Value values for elements in the list are:

    Handler          What it does                     Handles protocols
    ------------------------------------------------------------------------
    \"default\"      Use `browse-url' to open URLs    All

    \"windows\"      Microsoft Windows default        All

    \"netscape\"     Opens URLs in Netscape, Mozilla, All
                     Firefox or Opera.

    \"mosaic\"       Opens URLs in NCSA Mosaic        All common

    \"lynx\"         Opens URLs in Lynx               All common

    \"galeon\"       Opens URLs in Galeon             All common

    \"w3\"           Opens URLs in Emacs W3           http,gopher,ftp

    \"emacs\"        Opens URLs in Emacs              ftp,telnet,file,mailto

    \"dired\"        Opens URLs in Emacs              ftp,file

    \"telnet-mode\"  Opens URLs in Emacs              telnet

    \"mail-mode\"    Opens URLs in Emacs              mailto
    ------------------------------------------------------------------------

The variable `kom-url-managers' contains a list of all handlers. 

See `kom-mosaic-command', `kom-netscape-command',
`kom-galeon-command', `kom-lynx-terminal-command',
`kom-lynx-xterm-command', and `kom-windows-browser-command' for
additional settings that affect opening URLs."
  server)


(def-kom-var kom-url-managers '(("default"
                                 ".*"
                                 "Browse-URL"
                                 lyskom-view-url-browse-url)
                                ("w3" 
                                 "\\(google\\|http\\|gopher\\|ftp\\)"
                                 "Emacs W3" 
                                 lyskom-view-url-w3)
                                ("windows"
                                 ".*"
                                 "web browser"
                                 lyskom-view-url-windows)
                                ("netscape"
                                 ".*"
                                 "Netscape Navigator/Mozilla/Firefox/Opera"
                                 lyskom-view-url-netscape)
                                ("\\(emacs\\|dired\\)"
                                 "\\(ftp\\|file\\)"
                                 "dired"
                                 lyskom-view-url-dired)
                                ("\\(emacs\\|telnet-mode\\)"
                                 "telnet"
                                 "emacs telnet"
                                 lyskom-view-url-telnet)
                                ("\\(emacs\\|mail-mode\\)"
                                 "mailto"
                                 "mail-mode"
                                 lyskom-view-url-mailmode)
                                ("mosaic"
                                 "\\(http\\|gopher\\|ftp\\|mailto\\|news\\|wais\\|file\\|telnet\\)"
                                 "NCSA Mosaic"
                                 lyskom-view-url-mosaic)
                                ("lynx"
                                 "\\(http\\|gopher\\|ftp\\|mailto\\|news\\|wais\\|file\\|telnet\\)"
                                 "Lynx"
                                 lyskom-view-url-lynx)
                                ("galeon"
                                 "\\(http\\|gopher\\|ftp\\|mailto\\|news\\|wais\\|file\\|telnet\\)"
                                 "Galeon"
                                 lyskom-view-url-galeon))

  "List of URL managers. Each element is a list consisting of
\(MANAGER-REGEXP PROTOCOLS NAME VIEW-FUNCTION). When LysKOM attempts to
view a URL, kom-url-viewer-preferences is scanned, and the URL
-manager whose MANAGER-REGEXP first matches an element in
kom-url-viewer-preferences and whose PROTOCOLS matches the protocol of
the selected URL is used to view the URL by calling its VIEW-FUNCTION
with the URL and the manager entry as arguments.")

(def-kom-var kom-windows-browser-command ""
  "**Program to open a URL in Windows. 

If it is the empty string, a couple of commands that are likely to
work on Windows will be tried."
  server)

(def-kom-var kom-mosaic-command "/usr/local/bin/mosaic"
  "**Command to run Mosaic.

Note that Mosaic uses its own special conventions to open URLs
remotely that are probably not suitable for other browsers."
  server)

(def-kom-var kom-netscape-command "netscape"
  "**Command to start Firefox, Mozilla, Opera or Netscape.

If set to a string, it should be a command that starts Firefox,
Mozilla, Netscape or Opera with no arguments. If a list, the first
element must be a command that starts the web browser. The remaining 
elements are used as arguments to the browser.

For instance, a value of \"netscape\" is valid, but \"netscape -d
host:0\" is not. Instead, the latter should be \(\"netscape\" \"-d\"
\"host:0\"\)"
  server)

(def-kom-var kom-netscape-variant nil
  "**Netscape-specific options.

When set to `nil', open URLs in whatever window Firefox, Netscape,
Opera or Mozilla chooses. Usually an old window is recycled. When set
to `new-window', open URLs in a new window. When set to `new-tab',
open URLs in a new tab. These options may not work with all versions
of the different browsers, or on all operating systems.

Values other than those listed above are reserved for future use."
  server)


(def-kom-var kom-galeon-command "galeon"
  "**Command used to run to start Galeon.

If a string, it should be a command that starts Galeon with no
arguments. If a list, the first element must be a command that starts
Galeon. The remaining elements are used as arguments to Galeon.

For instance, a value of \"galeon\" is valid, but \"galeon
--display host:0\" is not. Instead, the latter should be
\(\"galeon\" \"--display\" \"host:0\"\)"
  server)

(def-kom-var kom-lynx-terminal 'xterm
  "**Where to start Lynx when opening URLs.

Valid values are `xterm' \(start Lynx in an xterm) and `terminal'
\(start Lynx in Emacs terminal mode)."
  server)

(def-kom-var kom-lynx-xterm-command
  '("xterm" "-geometry" "90x50+100+100" "-e" "lynx")
  "**Command to run to start Lynx in an xterm.

Must be a list of strings, where the first element is the name of the
xterm program, and the remaining elements are arguments to the
xterm. The last elements should be \"-e\" \"lynx\", or something similar,
to start Lynx."
  server)

(def-kom-var kom-lynx-terminal-command "lynx"
  "**Command to run Lynx in Emacs terminal mode.

This can be either a string, to start Lynx with no arguments, or a
list of strings, where the first element is the command, and the rest
are arguments to Lynx."
  server)

(def-kom-var kom-confirm-multiple-recipients 'after
  "**Non-nil means ask the user for confirmation about recipients.

When the user writes a comment to a text with more than one recipient,
ask for confirmation that all recipients are relevant, or one question
for each recipient.

If this variable is set to `before', ask once for each recipient
before opening the edit buffer.

If this variable is set to `after', ask once for all recipients after
editing the text. This is the preferred value since it is difficult to
judge the relevance of a recipient until the text has been written.

Values other than those listed are reserved for future use."
  common confirm-multiple-recipients boolean)

(def-kom-var kom-check-for-new-comments t
  "**Controls check for new comments when writing comments.

When posting a comment, the client can check if the text being
commented has unread comments. This frequently happens when several
people attempt to answer the same question at the same time.

If this variable is set to `t' check for new comments before posting a
comment. If there are new comments, ask for confirmation before
posting.

If this variable is `nil', dont' check.

If this variable is set to a function name, call the function to see
if check should be performed. The function is called with the
commented text's text-stat as its sole argument. If it returns
non-nil, the check is performed.

A list of conference numbers means perform the check for all
conferences other than those listed.

All other values are reserved for future use."
  server
  copy-transition lyskom-copy-indirect-assq)


(def-kom-var kom-check-commented-author-membership t
  "**Controls check that authors of commented texts will see a new comment.

When writing a comment, the client can check that the author of the
commented text is a member of at least one of the recipients. If that
is not the case, the user will be offered to add the author as a
recipient.

If this variable is set to `t', perform the check. If not, don't
perform the check.

See `kom-dont-check-commented-authors' for a way to exclude certain
authors from this check.

Values other than `t' and `nil' are reserved for future use."
  server
  copy-transition lyskom-copy-indirect-assq)


(def-kom-var kom-inhibit-typeahead nil
  "**Controls how command typed while the client is busy are handled.

When this variable is set to `t', input that arrives while a command
is running will be discarded before the next prompt is shown. When set
to `nil', that input will be handled normally.

If you find yourself accidentally executing commands, it may make
sense to set this variable to `t'.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-max-buffer-size nil
  "**Controls the maximum size of the LysKOM buffer.

When this variable is set to an integer, limit the size of the LysKOM
buffer to that many characters. When set to `nil', don't limit the
buffer size.

Before anything is deleted, `lyskom-trim-buffer-hook' (note that this
hook variable will probably be renamed in the future). Also see
`kom-trim-buffer-minimum' for more information.

Values other than `nil' and positive integers are reserved for future
use."
  server)

(def-kom-var kom-trim-buffer-minimum 4096
  "**The amount of text to trim from the buffer when limiting its size.

To avoid cutting tiny bits from the top of the buffer all the time,
delete this many bytes (rounded to a whole line) from the buffer at a
time.

See `kom-max-buffer-size' for information on how to limit the buffer
size.

Values other than positive integers are reserved for future use."
  server)


(def-kom-var kom-print-relative-dates t
  "**Controls display of relative dates.

When set to `t', print today's date as \"today\" and yesterday's as
\"yesterday\" in most places. When set to `nil', print all dates using
the default numeric format.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-print-seconds-in-time-strings nil
  "**Controls display of seconds in timestamps.

When set to `t', display many timestamps with seconds. When set to
`nil', only show hours and minutes.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-show-namedays nil
  "**Controls display of namedays.

When this variable is set to a non-nil value,`kom-display-time' can
display names of the day.

If set to `t', show the namedays for the currently selected languag
\(if there are any). If set to a symbol, that symbol should indicate a
list of names (use `kom-list-nameday-lists' to see a list of all
possible lists). If set to a list, each element should be a symbol
indicating a list of names; display namedays from all indicated lists.

All values other than those listed are reserved for future use."
  server)

(def-kom-var kom-ssh-relay-host nil
  "**Controls relay of LysKOM sessions through ssh.

It is possible to automatically tunnel LysKOM sessions through ssh.
For this to work you need to have command-line ssh installed, and
connecting to the remote host indicated by this variable must succeed
without asking for a password or passphrase.

This variable, when set to a string, enables tunneling ssh to the host
indicated by the value of this variable.

Note that storing this variable in the LysKOM server makes no sense.

See `kom-ssh-command' for additional configuration options.

Values other than strings are reserved for future use.")

(def-kom-var kom-ssh-command "ssh"
  "**Command to start ssh.

This variable should be set to a command that runs ssh. Note that it
is not possible to specify command-line arguments using this variable.

See `kom-ssh-relay-host' for more information.

Non-string values are reserved for future use.")

(def-kom-var lyskom-ssh-proxy nil
  "When non-nil, the ssh proxy used for this buffer."
  local
  protected)

(def-kom-var kom-ssh-general-errors
  "\\<\\(Enter passphrase.*$\\|^.*password.*$\\)\\|refused\\|disconnect\\|denied\\|error\\|key not found\\|cannot listen\\|[Aa]ddress already in use\\>"
  "**Regexp for messages from ssh that indicate that ssh has failed.")

(def-kom-var kom-ssh-forwarding-errors
  "\\<cannot listen\\|[Aa]ddress already in use\\>"
  "**Regexp for messages from ssh that indicate that forwarding failed.")


(def-kom-var kom-www-proxy nil
  "**Controls use of an HTTP proxy for the LysKOM session.

When this variable is set to a string, it should be the host name of
an HTTP proxy that supports the CONNECT method. All LysKOM sessions
will be transparently tunneled through this proxy.

When set to a list, each element must be a pair \(`SERVER' . `PROXY'),
where `SERVER' is a LysKOM server and `PROXY' is the proxy to use for
that server (or `nil' to not use a proxy). The special value `t' for
`SERVER' indicates the proxy to use for unlisted servers.

The proxy string has the form \"`HOST':`PORT'\", where `HOST' is the
proxy host and `PORT' is the port on which the proxy is running. The
port part is optional. If it is not specified, port 80 is assumed.

Values other than those described are reserved for future use.")

(def-kom-var kom-www-proxy-headers
  "User-Agent: Mozilla/4.7C-CCK-MCD  [en] (X11; I; SunOS 5.6 sun4u)"
  "**Extra HTTP headers to use when connecting through a Proxy.

The value of this variable should either be a single string, which is
sent verbatim to the proxy, or a list of strings which will be sent to
the proxy separated by CRLF, or a list of elements like \(`NAME' `H1'
`H2' ... `Hn') where `NAME' is the name of a proxy and the remaining
elements are headers to send when connecting through that proxy.

Do not use this variable for proxy authentication.

Values other than those listed are reserved for future use.")


(def-kom-var kom-server-aliases nil
  "**An alist mapping server names to shorter identification strings.

Each value in this string should be of the form \(`SERVER' . `NICKNAME'),
where `NICKNAME' is the short name for the server `SERVER'. You can
set this in init files before loading LysKOM.

See `kom-builtin-server-aliases' for more information.

Values other than those described are reserved for future use.")

(def-kom-var kom-builtin-server-aliases
  '(("kom.lysator.liu.se" . "LysKOM")
    ("com.lysator.liu.se" . "LysCOM (LysKOM in English)")
    ("kom.ludd.luth.se" . "LuddKOM")
    ("kom.hem.liu.se" . "RydKOM")
    ("kom.update.uu.se" . "UppKOM")
    ("kom.mds.mdh.se" . "MdS-KOM")
    ("kom.stacken.kth.se" . "TokKOM")
    ("com.helsinki.fi" . "HesaKOM")
    ("kom.cd.chalmers.se" . "CD-KOM")
    ("community.roxen.com" . "Roxen Community KOM")
    ("kom.ds.hj.se" . "DSKOM")
    ("kom.sno.pp.se" . "SnoppKOM")
    ("myskom.kfib.org" . "MysKOM"))
  "**An alist mapping server names to shorter identification strings.

Each value in this string should be of the form (`SERVER' . `NICKNAME'),
where `NICKNAME' is the short name for the server `SERVER'. Avoid
setting this variable since that will override the list compiled into
the client. Use `kom-server-aliases' instead.

Values other than those described are reserved for future use.")

(def-kom-var kom-ansaphone-on nil
  "*'Controls automatic replies to personal messages.

When set to `t', send automatically send replies to personal messages.
See `kom-ansaphone-replies' and `kom-ansaphone-default-reply' for ways
to configure the replies to send.

When set to `nil', don't send automatic replies.

This can be set temporarily with `kom-toggle-auto-reply'. The reply
message can be changed temporarily with `kom-change-auto-reply'.

Values other than `t' and `nil' are reserved for future use."
  local)

(def-kom-var kom-silent-ansaphone nil
  "**Controls beeps when automatic replies are enabled.

When set to `t', don't beep or otherwise signal receipt of personal
messages while automatic replies are enabled (see `kom-ansaphone-on').
When set to `nil', signal receipt of personal messages as usual.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-ansaphone-record-messages t
  "**Controls recording of personal messages while automatic replies are on.

When set to `t', record personal messages that are received while
automatic replies are on. Recorded messages can be listed with
`kom-list-messages' and erased with `kom-erase-messages'.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-ansaphone-show-messages t
  "**Controls display or personal messages while automatic replies are on.

When set to `t', personal messages received while automatic replies
are enabled will be shown as usual. When set to `nil' the will not be
shown. Note that if this variable is set to `nil' and
`kom-ansaphone-record-messages' is also set to `nil', messages are
simply discarded.

Values other than `t' and `nil' are reserved for future use."
  server)


(def-kom-var lyskom-ansaphone-messages nil
  "Messages collected by the automatic reply facility.
The most recent message is the first message in the list."
  local)

(def-kom-var lyskom-ansaphone-when-set (current-time-string)
  "Time when the auto-reply facility was enabled."
  local)

(def-kom-var kom-remote-control t
  "**Enables and disable remote control.

When this is set to `t', it will be possible to control the client
from other sessions using `kom-remote-autoreply',
`kom-remote-list-messages', `kom-remote-set-message',
`kom-remote-erase-messages' and `kom-remote-quit' (and possibly others
in the future). When set to `nil', remote control is disabled.

See `kom-remote-controllers' and `kom-self-control' for ways to
control access through remote control.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-remote-controllers nil 
  "**List of people who may use remote control.

This should be `nil' or a list of person numbers. The persons listed
may control the session using remote control commands (see
`kom-remote-control' for more information).

See `kom-self-control' for another variable that affects remote
control.

Values other than `nil' and a list of persons are reserved for future
use."
  server
  non-portable)

(def-kom-var kom-self-control t
  "**Enable or disable remote control for the logged-in user.

When set to `t', the user who is currently logged in may control the
session remotely from sessions that where the same user is logged in.
When set to `nil', only permit users listed in
`kom-remote-controllers'.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-ansaphone-replies
  '((group nil nil nil nil)
    (common nil nil nil nil))
  "**List of automatic replies to various messages.

This variable determines non-default automatic replies to personal
messages. Automatic replies are only send when `kom-ansaphone-on' is
set to `t'.

The value of this variable is a list of rules. Each rule is a list
with five elements: `MESSAGE-TYPE', `SENDER', `RECIPIENT', `TEXT', and
`REPLY'. Incoming messages are compared against these rules in order.
A message matches a rule if it matches all non-nil elements of the
rule.

If `MESSAGE-TYPE' is non-nil, match against the message type. Valid
values are `personal', `group' and `common' (for alarm messages).

If `SENDER' is non-nil, match against the message sender. The value
is either an integer (a person number) or a list of person numbers. A
message matches if its sender is any of the listed persons.

If `RECIPIENT' is non-nil, match against the message recipient. The
value is either an integer (a conference number) or a list of
conference numbers. A message matches if its recipient is any of the
listed conferences.

If `TEXT' is non-nil, match against the message text. The value is
a regular expression. A message matches if it contains a match for the
regular expression.

If all non-nil components of a rule match, rule processing is
terminated. If `REPLY' is a string, send that string as the reply. If
`REPLY' is `nil', don't send a reply.

If all rules are processed without finding a match, the value of
`kom-ansaphone-default-reply' is sent.

The default value for this variable disables automatic replies to all
group and alarm messages."
  server)

(def-kom-var kom-agree-text nil
  "**Determines the text used in `kom-agree'.

When non-nil, this is the default text for `kom-agree'. The value
may be a string, which is used verbatim as the default text; a
function, which is called and should return a text to use; or a list
whose elements must be strings, functions or lists, and from which an
element will be chosen at random and used in the same manner as
`kom-agree-text' itself.

Values other than those described are reserved for future use."
  server)

(def-kom-var kom-default-language nil
  "**The default language for LysKOM.

When set to non-nil, this variable should be set to a list of
symbols or a symbol indicating the prefered language(s) for LysKOM.
Each symbol name must be the ISO 630 code for a desired language (e.g.
`sv' for Swedish and `en' for English). Available languages depend on
how LysKOM was built.

All valid choices are listed in `lyskom-languages'."
  common language symbol-list
  transition (lambda (x) (cond ((listp x) x) (t (list x))))
  inherited
  protected)

(def-kom-var lyskom-language kom-default-language
  local
  inherited
  minibuffer
  protected
  "The language currently in use for messages.")

(def-kom-var lyskom-global-language kom-default-language
  "The language for language-specific things that affect multiple sessions.")

(def-kom-var lyskom-edit-mode-map nil
  "Mode map for LysKOM edit."
  local)

(def-kom-var lyskom-edit-prefix nil
  "Mode map for LysKOM edit mode.")

(def-kom-var lyskom-customize-map nil
  "Keymap for the customize buffer"
  local)

(def-kom-var lyskom-command-alternatives nil
  "Possible command completions."
  local
  minibuffer)


;;; =================================================================
;;;
;;; Language-dependent variables
;;;

(def-kom-var lyskom-month-names nil
  "A list of month names.
Each element is a cons cell consisting of the name of the month
\(a symbol) and the number of the month (1-12). Each month may
appear more than once, but the first occurence should be the
preferred name of the month."
  local
  inherited
  language-force)

(def-kom-var lyskom-help-data nil
  "Help strings."
  local
  language-force)

(def-kom-var lyskom-onoff-table nil
  "A completion table for on and off selections."
  local
  language-force)

(def-kom-var lyskom-move-tree-actions nil
  "A completion table for actions in kom-move-text-tree"
  local
  language-force)

(def-kom-var lyskom-language-codes nil
  "A list of ISO 639 language codes"
  local
  language-force)

(def-kom-var lyskom-filter-predicate-list nil
      "A list of legal filter comparison predicates."
      local
      language-force)

(def-kom-var lyskom-filter-what nil
      "A list of legal filter conditions and their textual representation."
      local
      language-force)

(def-kom-var lyskom-filter-actions nil
      "A list of legal filter actions an their textual representation."
      local
      language-force)

(def-kom-var lyskom-filter-edit-map nil
  "Keymap for LysKOM filter edit."
  local)

(def-kom-var lyskom-prioritize-mode-map nil
  "Keymap used in lyskom-prioritize-mode."
  local)

(def-kom-var kom-ansaphone-default-reply nil
  "**Default message to send when automatic replies are on.

The value of this variable must be a string or `nil'. When set to
`nil', no automatic replies are generated. See `kom-ansaphone-replies'
for a more flexible way of specifying automatic replies.

Values other than strings ans `nil' are reserved for future use."
  server)

(def-kom-var kom-ispell-dictionary nil
  "**Dictionary to use for spell checking.

When spell checking is used when writing texts, this variable can be
used to specify an alternate dictionary. When set to `nil', use the
default dictionary. When set, it should be set to a string indicating
the alternate dictionary (see `ispell-dictionary').

Values other than `nil' and strings are reserved for future use."
  server
  inherited)

(def-kom-var lyskom-unread-mode-line nil
  "This variable will become part of mode-line-format"
  language-force)

(def-kom-var lyskom-unread-title-format nil
  "This variable will become part of frame-title-format"
  language-force)

(def-kom-var lyskom-button-actions
  '((text
     text-popup-title
     lyskom-button-view-text
     ((lyskom-button-view-text-action . lyskom-button-view-text)
      (lyskom-button-unread-text-action . lyskom-button-unread-text)
      (lyskom-button-copy-text-no-action . lyskom-button-copy-text-no)
      (lyskom-button-review-noconversion-action . lyskom-button-review-noconversion)
      (lyskom-button-review-converted-action . lyskom-button-review-converted)
      (lyskom-button-review-rot13-action . lyskom-button-review-rot13)
      (lyskom-button-find-root-review-action . lyskom-button-find-root-review)
      (lyskom-button-find-root-action . lyskom-button-find-root)
      (lyskom-button-review-comments-action . lyskom-button-review-comments)
      (lyskom-button-review-tree-action . lyskom-button-review-tree)
      (lyskom-button-comment-text-action . lyskom-button-comment-text)
      (lyskom-button-private-comment-text-action . lyskom-button-private-comment-text)
      (lyskom-button-write-footnote-action . lyskom-button-write-footnote)
      (lyskom-button-fast-reply-action . lyskom-button-fast-reply)
      (lyskom-button-mark-text-action . lyskom-button-mark-text)
      (lyskom-button-unmark-text-action . lyskom-button-unmark-text)
      (lyskom-button-save-text-action . lyskom-button-save-text)
      (lyskom-button-save-text-body-action . lyskom-button-save-text-body)
      )
     nil
     ;; ((nil lyskom-print-text footer lyskom-button-comment-text))
     )
    (conf 
     conf-popup-title
     lyskom-button-view-conf-presentation
     ((lyskom-button-view-conf-presentation-action . lyskom-button-view-conf-presentation)
      (lyskom-button-view-conf-status-action . lyskom-button-view-conf-status)
      (lyskom-button-goto-conf-action . lyskom-button-goto-conf)
      (lyskom-button-send-message-action . lyskom-button-send-message)
      (lyskom-button-add-self-action . lyskom-button-add-self)
      (lyskom-button-sub-self-action . lyskom-button-sub-self))
     ((kom-list-news . lyskom-button-goto-conf)
      (kom-membership . lyskom-button-goto-conf)))
    (pers 
     pers-popup-title
     lyskom-button-view-pers-presentation
     ((lyskom-button-view-pers-presentation-action . lyskom-button-view-pers-presentation)
      (lyskom-button-view-pers-status-action . lyskom-button-view-pers-status)
      (lyskom-button-goto-conf-action . lyskom-button-goto-conf)
      (lyskom-button-view-session-status-action . lyskom-button-view-session-status)
      (lyskom-button-mail-action . lyskom-button-mail)
      (lyskom-button-send-message-action . lyskom-button-send-message)
      (lyskom-button-moronify-action . lyskom-button-moronify)
      (lyskom-button-befriend-action . lyskom-button-befriend))
     ; Hints
     ((kom-list-news . lyskom-button-goto-conf)
      (kom-membership . lyskom-button-goto-conf))
     ; Filter
     (lambda (item arg) 
       (cond ((not (numberp arg)) t)
             ((eq 'lyskom-button-goto-conf item)
              (or (eq arg lyskom-pers-no) (lyskom-is-supervisor arg lyskom-pers-no)))
             (t t)))
     )
    (url 
     url-popup-title
     lyskom-button-open-url
     ((lyskom-button-open-url-action . lyskom-button-open-url)
      (lyskom-button-copy-url-action . lyskom-button-copy-url))
     nil)
    (info-node 
     generic-popup-title
     lyskom-button-goto-info-node
     ((lyskom-button-goto-info-node-action . lyskom-button-goto-info-node))
     nil)
    (email
     generic-popup-title
     lyskom-button-open-email
     ((lyskom-button-open-email-action . lyskom-button-open-email)
      (lyskom-button-copy-email-action . lyskom-button-copy-email))
     nil)
    (aux
     aux-popup-title
     lyskom-button-info-aux
     ((lyskom-button-info-aux-action . lyskom-button-info-aux)
      (lyskom-button-delete-aux-action . lyskom-button-delete-aux))
     nil)
    (aux-edit-menu
     nil
     nil
     ((lyskom-edit-toggle-secret-aux-action . lyskom-edit-toggle-secret-aux)
      (lyskom-edit-toggle-anonymous-aux-action . lyskom-edit-toggle-anonymous-aux)
      (lyskom-edit-toggle-inherit-aux-action . lyskom-edit-toggle-inherit-aux)
      (lyskom-edit-delete-aux-action . lyskom-edit-delete-aux))
     nil)
    (prioritize-flag-menu
     nil
     lyskom-prioritize-flag-toggle
     ((lyskom-prioritize-flag-toggle-action . lyskom-prioritize-flag-toggle)
      (lyskom-prioritize-flag-set-action . lyskom-prioritize-flag-set)
      (lyskom-prioritize-flag-clear-action . lyskom-prioritize-flag-clear))
     nil)
    (func
     nil
     lyskom-button-apply
     nil
     nil)
    (timestamp
     timestamp-popup-title
     (lambda (buffer argument text) nil)
     ((lyskom-button-copy-timestamp-action . lyskom-button-copy-timestamp))
     nil)
    (recpt-type
     recpt-type-popup-title
     (lambda (buffer argument text) nil)
     ((lyskom-button-recpt-type-recipient
       . (lambda (buffer recpt-and-buffer text)
	   (lyskom-edit-do-add-recipient/copy
	    'RECPT (car recpt-and-buffer)
	    (car (cdr recpt-and-buffer)))))
      (lyskom-button-recpt-type-copy
       . (lambda (buffer recpt-and-buffer text)
	   (lyskom-edit-do-add-recipient/copy
	    'CC-RECPT (car recpt-and-buffer)
	    (car (cdr recpt-and-buffer)))))
      (lyskom-button-recpt-type-bcc
       . (lambda (buffer recpt-and-buffer text)
	   (lyskom-edit-do-add-recipient/copy
	    'BCC-RECPT (car recpt-and-buffer)
	    (car (cdr recpt-and-buffer)))))
      (lyskom-button-recpt-type-sub
       . (lambda (buffer recpt-and-buffer text)
	   (lyskom-edit-sub-recipient/copy
	    (car recpt-and-buffer)
	    (car (cdr recpt-and-buffer)))))))
    (add-recipient-or-xref
     add-recipient-or-xref
     nil
     ((lyskom-button-recpt-add-recipient
       . (lambda (buffer buffer text)
           (set-buffer buffer)
	   (kom-edit-add-recipient)))
      (lyskom-button-recpt-add-copy
       . (lambda (buffer buffer text)
           (set-buffer buffer)
	   (kom-edit-add-copy)))
      (lyskom-button-recpt-add-bcc
       . (lambda (buffer buffer text)
           (set-buffer buffer)
	   (kom-edit-add-bcc)))
      (lyskom-button-aux-type-xref
       . (lambda (buffer recpt-and-buffer text)
           (save-excursion
             (set-buffer recpt-and-buffer)
             (kom-edit-add-cross-reference))))
      (lyskom-button-aux-type-no-comments
       . (lambda (buffer recpt-and-buffer text)
           (save-excursion
             (set-buffer recpt-and-buffer)
             (kom-edit-add-no-comments))))
      (lyskom-button-aux-type-personal-comments
       . (lambda (buffer recpt-and-buffer text)
           (save-excursion
             (set-buffer recpt-and-buffer)
             (kom-edit-add-personal-comments))))))
    (view-pdf
     nil
     (lambda (buffer argument text) (lyskom-doc-view-text argument))
     nil
     nil)
    )
  "This variable defines valid button types in LysKOM. Each element is a
list consisting of (TYPE LABEL DEFAULT ACTIONS HINTS).
TYPE is the button type the entry defines
LABEL is a textual representation for the button type, used in menu titles. If
      it is a symbol, that symbol will be looked up using lyskom-get-string.
DEFAULT is the default action to take on a click. It must be a function.
ACTIONS are other possible actions. The format of this entry is described 
        below.
HINTS is a list of hints to override the default action. This is described 
      below.

The ACTIONS entry is used to construct a pop-up menu. It is a list consisting 
of lists with the format (STRING . FUNCTION). STRING is the menu label and
FUNCTION is the function to call when the menu item is selected.

The HINTS entry is used to generate hints that the default action should be 
overridden. It is a list containing elements (COMMAND . HINT) where COMMAND is
as interactive LysKOM command and HINT is a function to call. When a button
is generated while the command COMMAND is being executed, HINT is used as a 
hint for a new default action. The user has the option to ignore or used the 
hint.

Also see the function \"lyskom-add-button-action\"."
  local
  inherited)

(def-kom-var kom-show-imported-envelope-sender t
  "**Controls display of envelope sender of imported e-mails.

When set to `t', dispaly the envelope sender of imported e-mails (if
the importer has recorded such information).

`kom-show-imported-envelope-sender' set to `t':

<
  2912231 today 10:07 +0100 /41 lines/  Super-User <root@spareribs.meat.com>
  Sent by: root@meat.spareribs.com
  Imported: today 00:07 by Mailman
  External recipient: BBQ <531@kom.meat.com>
>

`kom-show-imported-envelope-sender' set to `nil':

<
  2912231 today 10:07 +0100 /41 lines/  Super-User <root@spareribs.meat.com>
  Imported: today 00:07 by Mailman
  External recipient: BBQ <531@kom.meat.com>
>

Other variables that control display of imported e-mail include
`kom-show-imported-importer' and
`kom-show-imported-external-recipients'.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-show-imported-importer t
  "**Controls display of the importer name in imported mail.

When set to `t', display information about what imported the e-mail
\(if the importer has recorded such information). When set to `nil',
don't.

`kom-show-imported-importer' set to `t':

<
  2912231 today 10:07 +0100 /41 lines/  Super-User <root@spareribs.meat.com>
  Imported: today 00:07 by Mailman
  External recipient: BBQ <531@kom.meat.com>
>

`kom-show-imported-importer' set to `nil':

<
  2912231 today 10:07 +0100 /41 lines/  Super-User <root@spareribs.meat.com>
  External recipient: BBQ <531@kom.meat.com>
>

Other variables that control display of imported e-mail include
`kom-show-imported-importer' and
`kom-show-imported-external-recipients'.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-show-imported-message-id nil
  "**Controls display of the message id in imported mail.

When set to `t', display the message id of the imported e-mail. When
set to `nil', don't.

`kom-show-imported-message-id' set to `t':

<
  2912231 today 10:07 +0100 /41 lines/  Super-User <root@spareribs.meat.com>
  Message-ID: <D1696C471C6CD511A0BE00D0B7A932DE01BBB4FA@spareribs.meat.com>
  External recipient: BBQ <531@kom.meat.com>
>

`kom-show-imported-message-id' set to `nil':

<
  2912231 today 10:07 +0100 /41 lines/  Super-User <root@spareribs.meat.com>
  External recipient: BBQ <531@kom.meat.com>
>

Other variables that control display of imported e-mail include
`kom-show-imported-envelope-sender', `kom-show-imported-importer' and
`kom-show-imported-external-recipients'.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-show-imported-external-recipients t
  "**Controls display of external recipients of imported e-mail.

When set to `t', display all external recipients of each imported
e-mail (the to and cc headers). When set to `nil', don't display these
headers.

`kom-show-imported-external-recipients' set to `t':

<
  2912231 today 10:07 +0100 /41 lines/  Super-User <root@spareribs.meat.com>
  Imported: today 00:07 by Mailman
  External recipient: BBQ <531@kom.meat.com>
  External recipient: \"Mac the Hack\" <mac.hack@steak.meat.com>
>

`kom-show-imported-external-recipients' set to `nil':

<
  2912231 today 10:07 +0100 /41 lines/  Super-User <root@spareribs.meat.com>
  Imported: today 00:07 by Mailman
>

Other variables that control display of imported e-mail include
`kom-show-imported-envelope-sender', `kom-show-imported-importer' and
`kom-show-imported-message-id'.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-complete-numbers-before-names t
  "**Controls completion of numeric person and conference names.

When set to `t', reading conference and user names accepts the special
forms \"m 4711\" or \"p 42\" as numeric references to conference 4711 and
person 42 instead of trying to look for an object with a matching
name. If `nil', any name matching the input will be preferred to a
numeric reference.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-preferred-charsets '(iso-8859-1 utf-8)
  "**Character sets to try encoding texts with.

Thie setting may contain a list of valid MIME character sets. When
encoding a text to send to the server, the client will use the default
server character set whenever possible. If it is insufficient, it will
try the character sets listed in this setting, in order. If they are
also insufficient, it will attempt to find some other character set
that works.

WARNING: this setting only applies to Gnu Emacs. In XEmacs, customize
the variable mm-coding-system-priorities instead; you may have to load
Gnus in order to do so."
  server)


(def-kom-var kom-mercial nil
  "**What you are doing when you're done reading.

The value of this variable should be a string. This string is sent to
the server as the user's current activity when everything is read.

Users are encouraged to use their best sense of humor."
  server)


(defconst lyskom-commands-not-in-menu
  '(
    kom-unread-previous-commented-text
    kom-create-aux-item
    kom-become-nonanonymous
    kom-become-anonymous
    kom-send-alarm
    kom-get-abuse
    kom-get-appreciation
    kom-list-clients
    kom-view-previous-commented-text
    kom-membership
    kom-delete-text
    kom-quick-mode
    kom-slow-mode
    kom-busy-wait
    kom-comment-previous
    kom-private-answer-previous
    kom-change-language
    kom-delete-user-area
    )
  "Commands that are not supposed to appear in menus.")

(defconst lyskom-commands
  '(
    kom-help
    kom-slow-mode
    kom-quick-mode
    kom-send-message
    kom-moronify
    kom-befriend
    kom-create-conf
    kom-delete-conf
    kom-delete-text
    kom-display-time
    kom-go-to-conf
    kom-go-to-next-conf
    kom-jump
    kom-list-created-conferences
    kom-list-conferences 
    kom-list-persons
    kom-list-news
    kom-list-re
    kom-list-sessions
    kom-membership
    kom-list-marks
    kom-postpone
    kom-set-session-priority
    kom-prioritize
    kom-status-person
    kom-status-conf
    kom-add-self
    kom-change-priority
    kom-list-summary
    kom-sub-self
    kom-quit
    kom-recover 
    kom-start-anew
    kom-view
    kom-find-root-review
    kom-review-comments
    kom-review-tree
    kom-review-cross-references
    kom-review-clear
    kom-review-last-normally-read
    kom-review-noconversion
    kom-review-converted
    kom-review-rot13
    kom-review-next
    kom-find-root
    kom-review-by-to
    kom-review-roots-by-to
    kom-review-more
    kom-review-first
    kom-review-first-roots
    kom-review-all
    kom-review-all-roots
    kom-view-commented-text
    kom-view-previous-commented-text
    kom-review-stack
    kom-review-presentation
    kom-review-backward
    kom-view-next-text
    kom-who-is-on
    kom-who-is-on-in-conference
    kom-who-is-on-and-friend
    kom-who-am-i
    ;;   kom-display-who-buffer
    kom-list-clients
    kom-busy-wait
    kom-write-comment
    kom-comment-previous
    kom-write-footnote
    kom-private-answer
    kom-private-answer-previous
    kom-set-unread
    kom-write-text
    kom-send-letter
    kom-change-name
    kom-change-parenthesis
    kom-change-password
    kom-change-supervisor
    kom-change-presentation
    kom-get-appreciation
    kom-get-abuse
    kom-mark-text
    kom-unmark-text
    kom-review-marked-texts
    kom-review-all-marked-texts
    kom-add-recipient
    kom-add-copy
    kom-add-bcc
    kom-sub-recipient
    kom-move-text
    kom-move-text-tree
    kom-add-comment
    kom-sub-comment
    kom-move-comment
    kom-add-cross-reference
    kom-add-member
    kom-sub-member
    kom-change-conf-motd
    kom-set-garb-nice
    kom-set-super-conf
    kom-set-permitted-submitters
    kom-unset-conf-motd
    kom-save-text
    kom-save-text-body
    kom-save-options
    kom-shutdown-server
    kom-sync-database
    kom-enable-adm-caps
    kom-disable-adm-caps
    kom-set-motd
    kom-remove-motd
    kom-force-logout
    kom-filter-author
    kom-filter-subject
    kom-filter-text
    kom-filter-recipient
    kom-super-jump
    kom-filter-edit
    kom-list-filters
    kom-show-user-area
    kom-delete-user-area
    kom-change-conf-type

    kom-change-auto-reply
    kom-toggle-auto-reply
    kom-list-messages
    kom-erase-messages

    kom-remote-autoreply
    kom-remote-set-message
    kom-remote-list-messages
    kom-remote-erase-messages
    kom-remote-quit

    kom-status-session
    kom-customize
    kom-change-language
    kom-calculate

    kom-where-is

    kom-next-kom
    kom-previous-kom
    kom-next-unread-kom

    kom-send-alarm
    kom-agree
    kom-fast-reply
    kom-add-faq
    kom-del-faq
    kom-review-faq

    kom-add-footnote
    kom-sub-footnote

    kom-add-private-answer
    kom-add-no-comments
    kom-add-request-confirm

    kom-review-mail-headers

    kom-compare-texts
    kom-diff-texts

    kom-view-url-in-text

    kom-become-anonymous
    kom-become-nonanonymous

    kom-keep-alive
    kom-stop-keep-alive

    kom-who-is-present-in-conference
    kom-is-person-member-of-conference
    kom-change-conf-faq

    kom-make-review-mark-as-read
    kom-make-review-not-mark-as-read
    kom-set-presentation
    kom-set-motd-text
    kom-remove-presentation
    kom-create-aux-item
    kom-status-server
    kom-add-server-faq
    kom-del-server-faq
    kom-change-server-faq
    kom-review-server-faq
    kom-recommend-conference
    kom-redirect-comments
    kom-copy-options
    kom-mark-unread
    kom-unread-by-to
    kom-unread-roots-by-to
    kom-unread-last-normally-read
    kom-unread-root-review
    kom-unread-root
    kom-unread-tree
    kom-unread-comments
    kom-unread-previous-commented-text
    kom-unread-commented-text
    kom-unread-more
    kom-unread-all
    kom-unread-all-roots
    kom-unread-first
    kom-unread-first-roots
    kom-unread-all-marked-texts
    kom-unread-marked-texts
    kom-unread-faq
    kom-unread-server-faq
    kom-unread-presentation
    kom-join-all-conferences
    kom-leave-all-conferences
    kom-will-person-read-text
    kom-limit-import
    kom-change-message-flag
    kom-list-faqs
    kom-list-server-faqs
    kom-list-new-conferences
    kom-list-new-persons
    kom-change-privileges

    kom-review-more-comments
    ))

;;; ================================================================
;;;                  Internal variables and constants


(def-kom-var lyskom-settings-version lyskom-clientversion
  "Version of saved settings."
  server)

(defvar lyskom-max-int 8388607
  "The largest int Emacs, and thus this LysKOM client, can handle.")

(def-kom-var lyskom-server-uses-utc nil
  "When non-nil, assume that the server uses UTC timestamps."
  inherited
  local)

(defconst lyskom-server-features
  '((10 lyskom-bcc-flag
        lyskom-extended-types-flag)
    (9  lyskom-accept-async-flag
        lyskom-dynamic-session-info-flag
        lyskom-idle-time-flag)
    (8  lyskom-long-conf-types-flag 
        lyskom-set-last-read-flag 
        lyskom-uconf-stats-flag
        lyskom-set-last-read-flag)
    (7  lyskom-z-lookup-flag))
  "List describing which features a certain server has. Each
element is a list containing the protocol version and what
it supports. The format of each element is:

\(VERSION . SUPPORTS\)

Version is simply a protocol version. Protocol equal to or above the
version support the supports list.

SUPPORTS is a list of pairs and symbols. Cons pairs are treated as
arguments to setq, symbols are interpreted as variable names set
 to 't'.")

;;;(defconst lyskom-server-features
;;;  '(((>= 2 0 0) (lyskom-bcc-flag
;;;                 lyskom-aux-items-flag))
;;;    ((>= 1 9 0) (lyskom-accept-async-flag
;;;                 lyskom-dynamic-session-info-flag
;;;                 lyskom-idle-time-flag))
;;;    ((>= 1 8 0) (lyskom-long-conf-types-flag 
;;;                 lyskom-set-last-read-flag
;;;                 lyskom-uconf-stats-flag))
;;;    ((>= 1 7 0) (lyskom-z-lookup-flag))
;;;    ((= 2 0 0) ((protocol-version 10)))
;;;    ((= 1 9 0) ((protocol-version 9)))
;;;    ((= 1 8 0) ((protocol-version 8)))
;;;    ((= 1 7 0) ((protocol-version 7)))
;;;    ((= 1 7 1) ((protocol-version 7)))
;;;    ((< 1 7 0) ((protocol-version 6))))
;;;  "List describing which features a certain server version has.
;;;Each element is a list containing the server version and what it
;;;supports:
;;;")


(def-kom-var lyskom-server-version '(0 0 0)
  "The version of the server.  A list of three integers: major
version, minor version and revision."
  local)

(def-kom-var lyskom-server-coding-system 'iso-8859-1
  "The default coding system used by the server for all strings."
  inherited)

(def-kom-var lyskom-current-user-area 0
  "Text-no of the user area last saved or read for lyskom-pers-no."
  local)

(def-kom-var lyskom-max-packet-size lyskom-max-int
  "The largest possible packet size that can be transmitted to a
TCP/IP connection. This should be unlimited, but in practise there
are systems that limits this. This variable is automatically adjusted
if any problems are detected.")

(def-kom-var lyskom-pending-commands nil
  "Commands pending to be executed.
When a command finishes, it checks this variable to see if another command
should be run.

It should be a list where each element should be either a symbol or an
expression. If it is a symbol, it is invoked with `call-interactively', and
an expression is evaluated with `eval'."
  local)

(def-kom-var lyskom-do-when-done nil
  "Internal of kom-do-when-done."
  local)

(def-kom-var lyskom-do-when-starting nil
  "Internal of kom-do-when-starting. Obsolete.")

(def-kom-var lyskom-sessions-with-unread nil
  "List of LysKOM sessions with unread texts.
This is not buffer-local.")

(def-kom-var lyskom-sessions-with-unread-letters nil
  "List of LysKOM sessions with unread letters.
This is not buffer-local.")

(def-kom-var lyskom-session-has-unread-letters nil
  "Non-nil if this session has unread letters."
  local
  protected
  inherited)

(def-kom-var lyskom-session-has-unreads nil
  "Non-nil if this session has unread texts."
  local
  protected
  inherited)

(def-kom-var lyskom-buffer nil
  "The LysKOM buffer we are connected to."
  inherited
  minibuffer)

(def-kom-var lyskom-buffer-type nil
  "Type of the current buffer."
  local
  protected)

(def-kom-var lyskom-errno nil
  "Errno of last lyskom error."
  local)

(def-kom-var lyskom-err-stat nil
  "Err-stat of last lyskom error."
  local)

(def-kom-var lyskom-parser-recovering nil
  "Non-nil if the parser is recovering from an error."
  local)

(def-kom-var lyskom-parse-pos nil
  "Position of parsing.")

(def-kom-var lyskom-unparsed-buffer nil
  "Buffer containing unparsed information from the server."
  local)

(def-kom-var lyskom-unparsed-marker nil
  "Where we now are inserting."
  local)

(def-kom-var lyskom-to-be-printed-before-prompt nil
  "Contains the strings to be printed before the next prompt."
  local)

(def-kom-var lyskom-other-clients-user-areas nil
  "Contains the parts of the user areas of unknown clients.
The area is a pair: name . info (both strings)."
  local)

(def-kom-var lyskom-saved-unknown-variables nil
  "Variables read from the user area that this client version
knows nothing about and that will be saved back unaltered."
  local)

(def-kom-var lyskom-pending-calls nil
  "Assoc list of calls to LysKOM server that have not yet completed.
Each element on the list has the format

	(REF-NO . KOM-QUEUE)
REF-NO	   unique number assigned by lyskom-send-packet.
KOM-QUEUE is a kom-queue. (See lyskom-call-data.)"
  local)

(def-kom-var lyskom-output-queues nil
  "Pending output to the server.
This is a vector of ten elements, each of which is a kom-queue. Calls from
queues with a higher index (priority) are always sent first.

At most lyskom-max-pending-calls calls are sent at once."
  local)

(def-kom-var lyskom-max-pending-calls 20
  "Max number of calls that are transmitted to the server at once.
Extra calls are queued in lyskom-output-queue and sent when the replies
returns.

This variable is not saved in the LysKOM server.")

(def-kom-var lyskom-number-of-pending-calls 0
  "Number of pending calls that are transmitted to the server."
  local)

;; This variable is used to prevent "starvation" of the blocking-do call.
;; When there is heavy prefetch going on in the background and a
;; blocking-do call is made there is a good chance that the
;; accept-process-output call will not return within a reasonable
;; time, because there will always be data to read from the server,
;; which means that Emacs will call lyskom-filter instead of returning
;; from accept-process-output.
(defvar lyskom-ok-to-send-new-calls t
  "This variable controls whether calls are passed to the server.
If it is nil, all outgoing calls are inhibited.")

(def-kom-var lyskom-ref-no 0
  "Next ref-no to use. These ref-nos are used to keep track of the
different packets.")

(def-kom-var lyskom-pers-no 0
  "The pers-no of the current user."
  inherited)

(def-kom-var lyskom-session-no 0
  "Session number in the server for this connection."
  local)

(def-kom-var kom-default-session-priority 0
  "**The default session priority.

Tha value of this variable must be an integer. Only texts in
conferences with a priority equal to or higher than this will be shown
by default.

To set the session priority in a running session, use
`kom-set-session-priority'. Setting this value directly will have no
effect on the actual session priority.

Non-integer values are reserved for future use."
  local
  server)

(def-kom-var kom-server-priority -1
  "**The default server priority.

The value of this variable must be an integer.

When `kom-server-priority-breaks' is set to a non-nil value, this
priority is used to decide when to go to a prioritized session. For a
session to be prioritized its priority must be higher than the current
session's `kom-server-priority' and higher than the priority of
whatever conference is currently being read.

Non-integer values are reserved for future use."
  server)


(def-kom-var lyskom-session-priority 0
  "This sessions priority.
Only texts in conferences with a priority equal to or higher than this
will be shown."
  local)

(def-kom-var lyskom-proc nil
  "The process (network connection) that is associated with this buffer."
  inherited
  minibuffer)

(def-kom-var lyskom-server-info nil
  "Info about the server."
  local)

(def-kom-var lyskom-server-version-info nil
  "Version information about the client."
  local)

(def-kom-var lyskom-server-name ""
  "The name of the server."
  inherited)

(def-kom-var lyskom-server-port nil
  "The port we are connected to."
  local)

(def-kom-var lyskom-buffer-list nil
  "List of all LysKOM buffers.")

(def-kom-var lyskom-static-session-info-cache nil
  "Cache of session."
  local)

(def-kom-var lyskom-conf-cache nil
  "Cache of conference statuses."
  local)

(def-kom-var lyskom-static-server-info nil
  "Cache of static-server-info"
  local)

(def-kom-var lyskom-stats-description nil
  "Cache of get-stats-description"
  local)

(def-kom-var lyskom-uconf-cache nil
  "Cache of small conference statuses."
  local)

(def-kom-var lyskom-pers-cache nil
  "Cache of person statuses."
  local)

(def-kom-var lyskom-text-cache nil
  "Cache of text statuses."
  local)

(def-kom-var lyskom-text-mass-cache nil
  "Cache of texts."
  local)

(def-kom-var lyskom-marked-text-cache nil
  "Cache of marks of all texts the current user has marked. "
  local)

(def-kom-var lyskom-is-parsing t
  "True when parsing a result.
This is used to prevent parallel parsing since the parser is not reentrant."
  local)

(def-kom-var lyskom-string-bytes-missing 0
  "Number of bytes missing in the unparsed buffer when parsing a string.
Set when parsing a string and there were not enough bytes in the buffer
with the unparsed bytes. This variable is used to prevent reparsing before 
the string is complete.
This variable is buffer-local in the unparsed-buffer."
  local
  inherited)

(def-kom-var lyskom-last-viewed 0       ;
  "Position of the first char of the last line that the user has had
time to view. This is normally the pos of the first char of the prompt."
  local)

(def-kom-var lyskom-mode-map nil
  "Keymap used in LysKOM mode."
  local)


(def-kom-var lyskom-reading-list nil
  "List of articles to read in the current conference.
Each element is a read-info. Only one of the elements is of the type CONF.
This one is located last in the list (except for the elements of the type 
REVIEW, REVIEW-TREE or REVIEW-MARK).
When reading an article with comments a list of the comments is built
recursively if the flag kom-read-depth-first is non-nil.
This is to keep track of the reading order.
Articles can exist in several of the read-info elements. All unread articles
in the conference are always present in the CONF type entry in this list even
if also in other entries. (COMM-IN, FOOTN-IN)

Some powerful reviewing commands requires to construct a list of articles that
should be read. These use the type REVIEW. When reviewing trees and when
every viewed article is supposed to be followed by all its comments then the
type REVIEW-TREE is used.

The first element is a dummy."
  local)

(def-kom-var lyskom-to-do-list nil
  "List of conferences with unread texts.
Each element is a read-info. All have the type 'CONF and there is one for
every conference with unread articles that have already been prefetched.
The list is sorted in falling priority. 
When going to a conference the first element (the one with the highest
priority) is copied from this list to lyskom-reading-list.

The first element is a dummy."
  local)

(def-kom-var lyskom-quit-flag nil
  "A flag indicating if the filter was interrupted by C-g.
 It is set to the same value as quit-flag on filter exit.")

(def-kom-var lyskom-ignoring-async-list nil
  "A list of async messages we are currently ignoring.
Each element is a list. The car of the list is the message and the
remaining elements are whatever is suitable for that type of message.
See the checks in lyskom-parse-async for details."
  local)

(def-kom-var lyskom-inhibit-minibuffer-messages nil
  "A flag indicating whether asynchronous minibuffer messages are allowed.
If this variable is non-nil, no asynchronous messages will appear.")

(def-kom-var lyskom-is-saving nil
  "A flag indicating whether the server is saving at the moment.")

;;; These variables control prefetch of conf-stats, text-stats and texts:

(def-kom-var lyskom-prefetch-conf-tresh 50
  "If fewer than lyskom-prefetch-conf-tresh texts are known, ask for more
conf-stats from server.

This is currently not used."
  local)

(def-kom-var lyskom-prefetch-confs 10
  "Number of confs to ask about at once when checking for unread texts.

This is currently not used."
  local)

(def-kom-var lyskom-fetch-map-nos 100
  "Number of text-nos lyskom will fetch when fetching maps."
  local)

(def-kom-var lyskom-fetch-membership-length 100
  "Number of entries in the membership-list that is fetched at a time.")

(def-kom-var lyskom-prefetch-limit 10
  "Number of prefetch requests the client will try to keep going
at a time.")


;;;

(def-kom-var lyskom-unread-confs nil
  "List containing all unread confs."
  local)

(def-kom-var lyskom-dont-change-prompt nil
  "Non-nil during the entry of a text."
  local)

(def-kom-var lyskom-command-to-do 'unknown
  "Atom describing what command to do. See the function lyskom-what-to-do."
  local)

(def-kom-var lyskom-is-waiting nil
  "If non-nil, this is the condition for the waiting to be stopped.
If t, however, it means that the user is waiting for a text with a prompt.
It is a form that will be evaluated (using eval) every time the asynchronous
message \"new text\" is received.

This is used by the command kom-busy-wait."
  local)

(def-kom-var lyskom-current-conf 0
  "Current conference. 0 means user is not reading any conf."
  local)

(def-kom-var lyskom-current-text nil
  "Text-no of current text. nil means no text is current."
  local)

(def-kom-var lyskom-last-written nil
  "Text-no of last text written. nil means no text written."
  local)

(def-kom-var lyskom-last-seen-written nil
  "Text-no of last text read or written by the current user.
When a new text is written, this is set to the text number of that text.
When a text is read that was written by the current user, this is
set to that text."
  local)

(def-kom-var lyskom-previous-text nil
  "Text-no of previous text. Nil means no text."
  local)

(def-kom-var lyskom-normally-read-texts nil
  "Stack of texts that are read normally. Used for kom-review-last-normally-read."
  local)

(def-kom-var lyskom-current-subject ""
  "Current subject."
  local)

(def-kom-var lyskom-current-user-area nil
  "Text-no of user area that current settings come from."
  local)

(def-kom-var kom-saved-file-name (concat default-directory "kom-text")
  "**The default filename when saving a LysKOM text.

This file name is used by `kom-save-text' as the default filename
until a new filename has been chosen."
  server)

(def-kom-var lyskom-saved-file-name nil
  "After saving once, the default file name when saving a LysKOM text."
  local)

(def-kom-var lyskom-mode-hook nil
  "*'Hook to run when `lyskom-mode' is entered.")

(def-kom-var kom-quit-hook nil
  "**Hook to run when the LysKOM session is ended.

This hook is only run when the session ends by using `kom-quit' or
`kom-remote-quit'. The hook is not run is the session is forecfully
terminated."
  server-hook)

(def-kom-var kom-quit-when-idle t
  "Controls automatic quitting when LysKOM is full.

When set to `t', automatically end the session when LysKOM is full and
the session has been idle for a predetermined amount of time.

This is currently not implemented.")

(def-kom-var kom-permanent-filter-list nil
  "List of patterns to filter permanently."
  server
  non-portable)

(def-kom-var kom-session-filter-list nil
  "List of patterns to filter during this session."
  local)

(def-kom-var lyskom-text-no-prompts-defaults
  '(kom-delete-text
    kom-view
    kom-write-footnote
    kom-mark-text
    kom-unmark-text
    kom-add-recipient
    kom-add-copy
    kom-add-bcc
    kom-sub-recipient
    kom-move-text
    kom-move-text-tree
    kom-add-comment
    kom-sub-comment
    kom-add-cross-reference
    kom-save-text-body
    kom-add-footnote
    kom-sub-footnote
    kom-add-faq
    kom-add-no-comments
    kom-add-private-answer
    kom-add-request-confirm
    kom-add-server-faq
    kom-mark-unread
    kom-set-presentation
    kom-set-motd-text
    kom-will-person-read-text
    kom-compare-texts
    kom-diff-texts
    )
  "Commands that prompt for a text number rather than assume a default."
  inherited)

(def-kom-var kom-text-no-prompts nil
  "**Determines which commands ask for text numbers.

The value of this variable must be a list containing pairs of 
(`COMMAND' . `VALUE'). If `VALUE' is `t', `COMMAND' will prompt for text
numbers. If `VALUE' is nil, `COMMAND' will use a suitable default value."
  server
  inherited)


(def-kom-var lyskom-filter-list nil
  "List of patterns that are filtered."
  local)

(def-kom-var kom-create-text-hook nil
  "**Hook to run before creating a new text.
This hook is run just before the server call to create the text is made.

The hook is currently called with the following arguments:

  Argument          Contents
  ----------------------------------------------------------------
  `MESSAGE'         The message text

  `MISC-LIST'       The misc-info list

  `AUX-LIST'        The aux-item list

  `BUFFER'          The edit buffer

  `IS-ANONYMOUS'    Non-nil if the user is currently anonymous.
  ----------------------------------------------------------------

Additional arguments may be added in the future, so include 
`&rest reserved' as the last element of the parameter list.

The hook can change the message by modifying the variable
`full-message', the misc-info list by modifying `misc-list' and the
aux-item list by modifying `aux-list'. This is not encouraged and may
break in the future."
  local-hook)

(def-kom-var kom-new-text-hook nil
  "**Hook to run when a new text is created.

This hook is run after the prompt is removed if it shall be changed
but before the text \"Text 4711 created\" is printed in the message
area and before the new prompt is printed."
  local-hook)

(def-kom-var kom-deleted-text-hook nil
  "**Hook to run when a text is deleted.

This hook is run after the prompt is removed if it shall be changed but 
before the new prompt is printed."
  local-hook)

(def-kom-var kom-new-recipient-hook nil
  "**Hook to run when a text receives a new recipient.

This hook is run after the prompt is removed if it shall be changed
but before the new prompt is printed. It may not run if the text has
been marked as read in any conference other than the person's
letterbox."
  local-hook)

(def-kom-var kom-personal-message-hook nil
  "**Hook to run when a personal message is received.

When the hook is run, `sender' is bound to the conf-stat of the sender
of the message (or possibly `nil'), `recipient' is 0 if the message is
an alarm message and otherwise the conf-stat of the recipient, and
`message' is a string that holds the message."
  local-hook)

(def-kom-var lyskom-executing-command t
  "Non-nil means the client is executing a command.
Most commands can't be interrupted by another command."
  local)

(def-kom-var lyskom-current-command nil
  "The command currently being executed."
  local)

(def-kom-var lyskom-command-point nil
  "The point where the most recent command was started."
  local)

(def-kom-var lyskom-current-function nil
  "Sometimes set to the current high-level function being executed."
  local)

(def-kom-var lyskom-current-function-phase nil
  "Sometimes set to the phase of the current high-level function being
executed."
  local)

(def-kom-var lyskom-membership-is-read nil
  "t when the membership has been read."
  local)

(def-kom-var lyskom-is-writing nil
  "t when the user is writing a text."
  local)

(def-kom-var lyskom-debug-communications-to-buffer nil
  "Non-nil means all communications with the server is stored in a buffer.
The name is stored in lyskom-debug-communications-to-buffer-buffer.")

(def-kom-var lyskom-debug-communications-limit 60000
  "When set to an integer limits the communications log to that many
bytes. When set to a non-integer, the communications log is unlimited.
If a protocol error is detected, this limit will be reset to nil.

See lyskom-debug-communications-to-buffer")

(def-kom-var lyskom-backtrace-list nil
  "List containing debugging information.")
  
(def-kom-var lyskom-debug-what-i-am-doing t
  "Non-nil means asynchronous message 5 will be logged to the debug
  buffer. ")

(def-kom-var lyskom-is-anonymous nil
  "Non-nil means be a bit secretive about things. Not totally
  secretive of course, since the server doesn't allow that yet."
  local)

(def-kom-var lyskom-debug-communications-to-buffer-buffer "*kom*-debugs"
  "Name of the buffer to insert the communications with the server into if
lyskom-debug-communications-to-buffer is non-nil.")

(def-kom-var lyskom-doing-default-command nil
  "Non-nil if LysKOM is executing the default command."
  local)

(def-kom-var lyskom-first-time-around nil
  "Non-nil if LysKOM is being entered for the first time."
  local)

(def-kom-var lyskom-experimental-features nil
  "If non-nil, LysKOM is likely to blow up in your face."
  local)

(def-kom-var lyskom-format-experimental nil
  "If non-nil, LysKOM is likely to make a fool out of you."
  local)

(def-kom-var lyskom-count-var 0
  "This variable is used for counting things in the client, such as
unread texts in list-unread."
  local)

(def-kom-var lyskom-default-conf-string nil
  "The default string to use for an unknown conference.
Set this locally when inserting a conference name using
lyskom-format-insert if you want to replace the usual description of
an unknown conference.")

(def-kom-var lyskom-default-pers-string nil
  "The default string to use for an unknown person.
Set this locally when inserting a conference name using
lyskom-format-insert if you want to replace the usual description of
an unknown person.")


(def-kom-var lyskom-is-administrator nil
  "This variable is t if the user is in administrator mode and nil otherwise."
  local
  minibuffer)

(def-kom-var lyskom-last-personal-message-sender nil
  "Name of sender of last personal message received."
  local)

(def-kom-var lyskom-last-group-message-recipient nil
  "Name of target for last group message received."
  local)

(def-kom-var lyskom-last-message-recipient nil
  "Number of last async message recipient sent to."
  local)

(def-kom-var lyskom-is-new-user nil
  "An internal variable used in kom-start-anew.")

(def-kom-var lyskom-apo-timeout-s 1
  "Seconds timeout for accept-process-output.")

(def-kom-var lyskom-apo-timeout-ms nil
  "Microseconds timeout for accept-process-output.")

(def-kom-var lyskom-collate-table nil
  "Table mapping characters to an equivalence class."
  inherited)

(def-kom-var lyskom-char-classes nil
  "An assoc list from character to a list of equivalent strings.
See lyskom-compute-char-classes."
  inherited)

(def-kom-var lyskom-dont-read-user-area nil
  "If non-nil the user area will not be read on login."
  local)

(def-kom-var lyskom-allow-missing-subject nil
  "If non-nil allow texts without subjects.")

(def-kom-var kom-w3-simplify-body t
  "**Control stripping of color information from HTML.

When this variable is set to `t', strip color information from body
tags in HTML passed to w3. This is generally a good idea. When `nil',
don't strip color information from body tags. This is generally a bad
idea.

Values other than `t' and `nil' are reserved for future use."
  server)

(def-kom-var kom-format-show-images t 
  "**Controls in-line display of images.

When set to `t', attempt to display images in-line. When set to `nil', 
images are handled as normal texts.

Values other than `t' and `nil' are reserved for future use."
  server)


(def-kom-var kom-format-html-authors '((t . t))
  "**Determines from which authors we accept HTML.

If non-nil, LysKOM will attempt to format texts marked as HTML.

The value is treated as an `indirect association list'. When
reading a text he author of a text is matched against the list,
and if the value of the match is non-nil, the HTML formatting may
be attempted. The matching rules are as follows:

  Element           Meaning
  ------------------------------------------------------------------
  `ATOM'            Matches with the default value if ATOM equals 
                    the author number (this means that you can set 
                    this variable to a list of person numbers).

  `(t . VAL)'       Matches with value VAL if no other match is 
                    found (i.e. sets the default value).

  `(FN . VAL)'      Matches with value VAL if FN is a function that
                    returns non-nil when called with the author
                    number as its sole argument.

  `(SYM . VAL)'     If SYM is bound to a list, match the author
                    number against that list using these rules. 
                    VAL is used as the default value for the 
                    matching process.

  `(SYM . VAL)'     If SYM is not bound to a list, matches with 
                    the value VAL if the value of SYM equals the
                    author number.

  `(LIST . VAL)'    Match the person number against LIST using
                    these rules. VAL is used as the default value.

  `(ATOM . VAL)'    Match with value VAL if ATOM equals the 
                    author number.
  ------------------------------------------------------------------

For example, the value `((5 . nil) (kom-friends . t))' will
prevent texts by person 5 from being shown as formatted HTML,
while texts by persons listed in the variable `kom-friends' can
be shown as formatted HTML.

Values other than those listed are reserved for future use." 
  server
  copy-transition lyskom-copy-indirect-assq)

(def-kom-var lyskom-format-special 
  '(("html"                . (lyskom-format-html-w3m lyskom-format-html-w3 lyskom-format-html-plaintext))
    ("enriched"            . lyskom-format-enriched)
    ("^image/"             . lyskom-format-image)
    ("^text/html"          . (lyskom-format-html-w3m lyskom-format-html-w3 lyskom-format-html-plaintext))
    ("^text/enriched"      . lyskom-format-enriched)
    ("^text/"              . lyskom-format-plaintext)
    ("^x-kom/text"         . lyskom-format-plaintext)  ;Archaic alias for text/x-kom-basic.
    ("^x-kom/basic"        . lyskom-format-plaintext)  ;Archaic alias for text/x-kom-basic.
    ("^application/pdf"    . lyskom-format-pdf)
    ("^x-kom/user-area"    . lyskom-format-x-kom/user-area)
    ("^x-kom/ö\\."      . lyskom-format-ö)
    ("^audio/"		   . lyskom-format-audio)
    ("^video/"		   . lyskom-format-video)
    ("^multipart/"	   . lyskom-format-multipart)
    ("^message/rfc822"	   . lyskom-format-plaintext)
    ("^message/delivery-status" . lyskom-format-plaintext)
    ("^message/disposition-notification" . lyskom-format-plaintext)
    ("^message/news"       . lyskom-format-plaintext)
    ("^message/"	   . lyskom-format-message)
    ("^model/"		   . lyskom-format-model)
    ("^application/"	   . lyskom-format-application)
    (""	                   . lyskom-format-unknown)
    )
  
  "AList of (FORMAT . FUNCTION) specifying functions that format texts
of that type. FORMAT is a symbol and FUNCTION is a function taking one
argument and returning a formatted string.")

(def-kom-var kom-respect-ancient-content-types nil
  "**When set to non-nil, LysKOM respects an ancient method for specifying
the content type of texts (in the first line of the text body). Should be
set to nil for most users."
  server)

(def-kom-var lyskom-send-text-transform-hook nil
  "Functions to call to transform text before sending it to the server.

When each function is run, lyskom-edit-text is bound to the text mass,
lyskom-edit-subject is bound to the subject, lyskom-edit-aux-list is
bound to the list of aux-items, and lyskom-edit-misc-list is bound to
the list of misc items. Functions may change these variables.

The first function to return a non-nil value will be the last function
to run.")

(def-kom-var lyskom-slow-mode nil
  "Non-nil when in slow mode."
  local)

(def-kom-var lyskom-saved-read-only nil
  "Saved value of buffer-read-only when in slow mode."
  local)

(def-kom-var lyskom-read-conf-saved-inputs nil
  "Saved inputs from lyskom-read-conf."
  local)


(defvar lyskom-line-start-chars-string
  "\"$&'()*+-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]_`abcdefghijklmnopqrstuvwxyz¡£¤¥§©ª«­®±²³µ¶¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ"
  "Characters that may start a line in a paragraph to be broken.")

(def-kom-var lyskom-line-start-chars nil
  "Computer-friendly version of lyskom-line-start-string.")


(def-kom-var lyskom-last-text-format-flags nil
  "List of flags specifying how the last text was reformatted. This variable
should be dynamically bound whenever it needs to be used.")

(def-kom-var lyskom-read-faqs nil
  "List of FAQs that have been read."
  inherited)

(def-kom-var lyskom-rejected-recommendations nil
  "List of invitations that have been rejected."
  inherited)

(def-kom-var lyskom-last-known-conf-no nil
  "Last known conference number."
  server
  non-portable)

(def-kom-var lyskom-last-known-pers-no nil
  "Last known conference number."
  server
  non-portable)

(defvar lyskom-xface-cache (make-vector 29 0))


;;; ======================================================================
;;; Event hooks
;;;

(def-kom-var lyskom-add-membership-hook nil
  "Functions to call when a membership is added."
  local-hook)

(def-kom-var lyskom-replace-membership-hook nil
  "Functions to call when a membership is replaced."
  local-hook)

(def-kom-var lyskom-remove-membership-hook nil
  "Functions to call when a membership is removed."
  local-hook)

(def-kom-var lyskom-new-membership-list-hook nil
  "Functions to call when the entire membership list is replaced."
  local-hook)


;;; ======================================================================
;;;; lyskom-tell-phrases-validation-keyword-list
;;; This is a list of keywords for kom-tell-phrases.
;;; These are the only keywords that are allowed in kom-tell-phrases.

;;; To coders of the elisp-client:
;;; If you add/delete a reference to any of these keywords make sure
;;; you update these changes.

;;; To everyone:
;;; The kom-tell-phrases list is checked against this list when the
;;; client is loaded, i.e. by lyskom-tell-phrases-validate that causes
;;; an error if any keyword is not present or any non-keyword is
;;; present.

(defvar kom-tell-phrases nil
  "**A list of phrases describing what the client is doing. 

Each element in the list is a pair \(`KEY' . `PHRASE') where KEY is one
of the keywords in `lyskom-tell-phrases-validation-keyword-list' and
`PHRASE' is the phrase to sent to the server then the client is doing
what `KEY' describes.

If the value of this variable is `nil', suitable defaults for the
currently selected language will be selected.")

(defconst lyskom-tell-phrases-validation-keyword-list
  '(
    (kom-tell-silence)
    (kom-tell-send)
    (kom-tell-login)
    (kom-tell-read)
    (kom-tell-1st-pres)
    (kom-tell-write-comment)
    (kom-tell-write-footnote)
    (kom-tell-write-letter)
    (kom-tell-write-reply)
    (kom-tell-write-text)
    (kom-tell-conf-pres)
    (kom-tell-recover)
    (kom-tell-wait)
    (kom-tell-regret)
    (kom-tell-review)
    (kom-tell-change-name)
    (kom-tell-change-supervisor)
    (kom-tell-next-lyskom)
    )
  "Users must not change this constant, but are encouraged to change
the value of  kom-tell-phrases for fun.")

;;; ================================================================
;;; Commands lists that are removed from extended command depending on
;;; administrator status.

(defconst lyskom-admin-removed-commands
  '(kom-enable-adm-caps))
(defconst lyskom-noadmin-removed-commands
  '(kom-disable-adm-caps
    kom-remove-motd
    kom-set-motd
    kom-shutdown-server
    kom-sync-database
    kom-add-server-faq 
    kom-del-server-faq
    kom-change-server-faq
    kom-recommend-conference
    kom-change-privileges))


;;; ================================================================
;;; Symbolic error codes

(defvar lyskom-error-codes
  '((no-error . 0)
    (not-implemented . 2)
    (obsolete-call . 3)
    (invalid-password . 4)
    (string-too-long . 5)
    (login-first . 6)
    (login-disallowed . 7)
    (conference-zero . 8)
    (undefined-conference . 9)
    (undefined-person . 10)
    (access-denied . 11)
    (permission-denied . 12)
    (not-member . 13)
    (no-such-text . 14)
    (text-zero . 15)
    (no-such-local-text . 16)
    (local-text-zero . 17)
    (bad-name . 18)
    (index-out-of-range . 19)
    (conference-exists . 20)
    (person-exists . 21)
    (secret-public . 22)
    (letterbox . 23)
    (ldb-error . 24)
    (illegal-misc . 25)
    (illegal-info-type . 26)
    (already-recipient . 27)
    (already-comment . 28)
    (already-footnote . 29)
    (not-recipient . 30)
    (not-comment . 31)
    (not-footnote . 32)
    (recipient-limit . 33)
    (comment-limit . 34)
    (footnote-limit . 35)
    (mark-limit . 36)
    (not-author . 37)
    (no-connect . 38)
    (out-of-memory . 39)
    (server-is-crazy . 40)
    (client-is-crazy . 41)
    (undefined-session . 42)
    (regexp-error . 43)
    (not-marked . 44)
    (temporary-failure . 45)
    (long-array . 46)
    (anonymous-rejected . 47)
    (illegal-aux-item . 48)
    (aux-item-permission . 49)
    (unknown-async . 50)
    (internal-error . 51)
    (feature-disabled . 52)
    (message-not-sent . 53)
    (invalid-membership-type . 54)
    (invalid-range . 55)
    (invalid-range-list . 56)
    (undefined-measurement . 57)
    (priority-denied . 59)
    (weight-zero . 60)
    (bad-bool . 61)
    )
  )

;;; ================================================================
;;;          Externally defined variables (environment)


(def-kom-var kom-default-server nil
  "**Default LysKOM server.

Use this server to log in to LysKOM. Can be overridden by the
`KOMSERVER' environment variable.

The value of this variable must be a string or `nil'."
  local)

(def-kom-var lyskom-default-server nil
  "**This variable is reserved for internal use. See 
`kom-default-server' instead." 
  local)

(def-kom-var kom-default-user-name nil
  "**Default LysKOM user name.

The value of this variable must be a string, indicating the
default user name, an integer indicating the conference number or 
`nil' to indicate no default. If set to an integer, log in as the
user with that conference number. If set to a string, log in as the 
user with that name.

Overrides the environment variable `KOMNAME'."
  local)

(def-kom-var kom-default-password nil
  "**Default LysKOM password.

The value of this variable must be a string or `nil'. Overrides the 
environment variable `KOMPASSWORD' \(which should never be used)."
  local)

(def-kom-var lyskom-default-password nil
  "**This variable is reserved for internal use. See 
`kom-default-password' instead."
  local)

(def-kom-var mode-line-conf-name nil
  "Conf name that is present on the mode-line."
  local)

(def-kom-var mode-line-server-name nil
  "Server name that is present on the mode-line."
  local)


;;; ============================================================
;;; History lists
;;;

(defvar lyskom-command-history nil)
(defvar lyskom-expression-history nil)
(defvar lyskom-message-history nil)
(defvar lyskom-language-history nil)
(defvar lyskom-fast-reply-history nil)
(defvar lyskom-help-history nil)


;;; ============================================================

(defconst lyskom-comment-types-list '(COMM-IN FOOTN-IN))
(defconst lyskom-recpt-types-list '(RECPT CC-RECPT BCC-RECPT)
  "Types of recipients in LysKOM.
This variable must be sorted in order of importance, with full recipients
first, followed by successively more and more minor recipients. The reason
for this is in kom-sub-recipient, which sorts the recipient list using
the order of this list.")
(defconst lyskom-review-types-list '(REVIEW REVIEW-TREE
                                            REVIEW-MARK REVIEW-FAQ
                                            REVIEW-FAQ-TREE))

;;; ============================================================


(eval-and-compile (provide 'lyskom-vars))

;;; vars.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: macros.el
;;;;
;;;; This file contains the macros which must be loaded before lyskom can
;;;; be compiled.
;;;;

;;;
;;; Require parts of the widget package. We do this to avoid generating
;;; errors later on. This sucks. 
;;;

(require 'custom)
(require 'widget)

;;; ======================================================================
;;; lyskom-traverse - traverse a sequence.
;;;

(defmacro lyskom-traverse (atom sequence &rest body)
  "Bind ATOM to each element in SEQUENCE and execute BODY.
Value returned is always nil."
  (let ((seq-sym (make-symbol "sequence"))
        (len-sym (make-symbol "len"))
        (idx-sym (make-symbol "index")))
    `(catch 'lyskom-traverse
       (let* ((,seq-sym ,sequence)
              (,idx-sym 0)
              (,len-sym (or (listp ,seq-sym)
                            (length ,seq-sym)))
              (,atom nil))
         (if (listp ,seq-sym)
             (while ,seq-sym
               (setq ,atom (car ,seq-sym))
               ,@body
               (setq ,seq-sym (cdr ,seq-sym)))
           (while (< ,idx-sym ,len-sym)
             (setq ,atom (aref ,seq-sym ,idx-sym))
             ,@body
             (setq ,idx-sym (1+ ,idx-sym))))))))


(defmacro lyskom-traverse-membership (var &rest forms)
  "Traverse the membership list.
Variable VAR is bound to each membership, in turn, and FORMS are evaluated."
  (let ((el-sym (make-symbol "el")))
    `(catch 'lyskom-traverse
       (let ((,el-sym (membership-list->head
                       (lyskom-with-lyskom-buffer (lyskom-mship-cache-data))))
             (,var nil))
         (while ,el-sym
           (setq ,var (mship-list-node->data ,el-sym))
           ,@forms
           (setq ,el-sym (mship-list-node->next ,el-sym)))))))


(defmacro lyskom-traverse-aux (atom sequence &rest body)
  "Bind ATOM to each element in SEQUENCE and execute BODY.
Value returned is always nil."
  (let ((seq-sym (make-symbol "aux-items")))
    `(catch 'lyskom-traverse
       (let ((,seq-sym ,sequence)
             (,atom nil))
         (while ,seq-sym
           (setq ,atom (car ,seq-sym))
           (if (not (aux-item-flags->deleted
                     (aux-item->flags ,atom)))
               (progn ,@body))
           (setq ,seq-sym (cdr ,seq-sym)))))))


(defmacro lyskom-traverse-break (&optional result)
  "Break a current lyskom-traverse"
  `(throw 'lyskom-traverse ,result))



(put 'lyskom-traverse-aux 'edebug-form-spec '(sexp form body))
(put 'lyskom-traverse-aux 'lisp-indent-hook 2)
(put 'lyskom-traverse-membership 'edebug-form-spec '(sexp body))
(put 'lyskom-traverse-membership 'lisp-indent-hook 1)
(put 'lyskom-traverse 'lisp-indent-hook 2)
(put 'lyskom-traverse 'edebug-form-spec '(sexp form body))


;;; ======================================================================
;;; lyskom-save-excursion Does not save point and mark.
;;;

(defmacro lyskom-save-excursion (&rest forms)
  "Save-excursion without saving point and mark."
  (list 'let (list '(__buffer__ (current-buffer)))
	(list 'unwind-protect
	      (cons 'progn
		    forms)
	      '(set-buffer __buffer__))))

(put 'lyskom-save-excursion 'edebug-form-spec t)
(put 'lyskom-save-excursion 'lisp-indent-hook 0)


(defmacro lyskom-with-lyskom-buffer (&rest forms)
  "Evaluate FORMS in the current LysKOM buffer."
  `(save-current-buffer
     (set-buffer lyskom-buffer)
     ,@forms))

(put 'lyskom-with-lyskom-buffer 'edebug-form-spec t)
(put 'lyskom-with-lyskom-buffer 'lisp-indent-hook 0)

;;; ======================================================================
;;; Some useful macros to make the code more readable.
;;;

(defmacro char-in-string (char string)
  "Return t if the character CHAR is member of STRING. Otherwise return nil."
  (list 'null
	(list 'not
	      (list 'string-match
		    (list 'regexp-quote
			  (list 'char-to-string char))
		    string))))

(defmacro ++ (var)
  "Increment the variable VAR and return the value."
  (list 'setq var (list '1+ var)))

(defmacro -- (var)
  "Decrement the variable VAR and return the value."
  (list 'setq var (list '1- var)))

(eval-and-compile
  (if (fboundp 'when)
      nil
    (defmacro when (expr &rest body)
      "Execute BODY if EXPR evaluates to non-nil"
      (list 'if expr (cons 'progn body)))
    (put 'when lisp-indent-function 1)
    (put 'when 'edebug-form-spec t)))

(eval-and-compile
  (if (fboundp 'unless)
      nil
    (defmacro unless (expr &rest body)
      "Execute BODY if EXPR evaluates to non-nil"
      (append (list 'if expr nil) body))
    (put 'unless lisp-indent-function 1)
    (put 'unless 'edebug-form-spec t)))


;;; ======================================================================
;;; Multiple blocking read from server
;;;

(defmacro blocking-do-multiple (bind-list &rest body)
  "Bind variables according to BIND-LIST and then eval BODY.
The value of the last form in BODY is returned.
Each element in BIND-LIST is a list (SYMBOL FORM) which binds SYMBOL to
the result of the server call FORM, which is the same as used in blocking-do.
All the forms in BIND-LIST are evaluated before any symbols are bound."
  (let ((index 0))
    `(let ((lyskom-multiple-bind-sym
            (lyskom-blocking-do-multiple
             (list ,@(mapcar (lambda (x) 
                               `(list ',(car (car (cdr x)))
                                      ,@(cdr (car (cdr x)))))
                             bind-list)))))
       (let (,@(mapcar (lambda (bpat)
                         (prog1
                             `(,(car bpat) (elt lyskom-multiple-bind-sym
                                                ,index))
                           (setq index (1+ index))))
                       bind-list))
         ,@body))))


(put 'blocking-do-multiple 'edebug-form-spec '(sexp body))
(put 'blocking-do-multiple 'lisp-indent-function 1)


;;; ======================================================================
;;; Some commands generat async messages we don't really want
;;;

(defmacro lyskom-ignoring-async (async &rest body)
  `(let ((lyskom-ignoring-async-list 
          (cons (list ,@async) lyskom-ignoring-async-list)))
     ,@body))

(put 'lyskom-ignoring-async 'edebug-form-spec
     '(sexp body))

(put 'lyskom-ignoring-async 'lisp-indent-function 1)


;;; ======================================================================
;;; These macros do magic things to the compiler to avoid gratuitous
;;; compiler warnings.
;;;

(eval-and-compile (defvar lyskom-expected-unresolved-functions nil))
(eval-and-compile (defvar lyskom-expected-unresolved-functions-temp nil))

(defmacro lyskom-external-function (fn)
  `(eval-when-compile
       (setq lyskom-expected-unresolved-functions
             (cons ',fn lyskom-expected-unresolved-functions))))

(defmacro lyskom-with-external-functions (fns &rest body)
  "Ignore warnings about unresolved functions FNS when compiling BODY."
  `(progn (eval-when-compile
            (and (boundp 'byte-compile-unresolved-functions)
                 (setq lyskom-expected-unresolved-functions-temp
                       (cons (delq nil
                                   (mapcar (lambda (fn)
                                             (if (assq fn byte-compile-unresolved-functions)
                                                 nil
                                               fn))
                                           ',fns))
                             lyskom-expected-unresolved-functions-temp))))
          ,@body
          (eval-when-compile
            (when (boundp 'byte-compile-unresolved-functions)
              (mapc (lambda (fn) (and fn (setq byte-compile-unresolved-functions
                                               (delq (assq fn byte-compile-unresolved-functions)
                                                     byte-compile-unresolved-functions))))
                    (car lyskom-expected-unresolved-functions-temp)))
            (setq lyskom-expected-unresolved-functions-temp
                  (cdr lyskom-expected-unresolved-functions-temp)))))


(defmacro lyskom-end-of-compilation ()
  `(progn 
     (eval-when-compile
       (progn
         (if (and (boundp 'byte-compile-unresolved-functions)
                  (consp (car-safe byte-compile-unresolved-functions))
                  (symbolp (car-safe (car-safe 
                                      byte-compile-unresolved-functions))))
             (progn
               (mapc (lambda (x)
                       (setq byte-compile-unresolved-functions
                             (delq
                              (assq x
                                    byte-compile-unresolved-functions)
                              byte-compile-unresolved-functions)))
                       lyskom-expected-unresolved-functions)
               ))

         (if lyskom-compatibility-definitions
             (message "Compatibility definitions: %s"
                      (mapconcat (function (lambda (sym)
					     (symbol-name sym)))
				 lyskom-compatibility-definitions
				 ", ")))))))

;;; ============================================================
;;; Keymap handling
;;;

(defmacro lyskom-use-local-map (keymap)
  "Use keymap KEYMAP as local map in this buffer. KEYMAP is made local in
the current buffer, and its value is copied from the LysKOM buffer."
  ` (progn (make-local-variable (quote ,keymap))
            (setq ,keymap (lyskom-default-value (quote ,keymap)))
            (use-local-map ,keymap)))

(defmacro lyskom-try-define-key (map seq fn)
  `(condition-case nil
       (define-key ,map ,seq ,fn)
     (error nil)))


;;; ============================================================
;;; Widget gunk
;;;

(defmacro lyskom-widget-wrapper (fn file)
  `(eval-and-compile
     (if (not (fboundp ',fn))
         (autoload ',fn ,file))))

(lyskom-widget-wrapper define-widget "widget")
(lyskom-widget-wrapper widget-at "wid-edit")
(lyskom-widget-wrapper widget-value "wid-edit")
(lyskom-widget-wrapper widget-button-click "wid-edit")
(lyskom-widget-wrapper widget-setup "wid-edit")
(lyskom-widget-wrapper widget-value-set "wid-edit")
(lyskom-widget-wrapper widget-insert "wid-edit")
(lyskom-widget-wrapper widget-create "wid-edit")
(lyskom-widget-wrapper widget-get "wid-edit")
(lyskom-widget-wrapper widget-put "wid-edit")

;;; ============================================================
;;; Signal gunk
;;;

(defmacro lyskom-ignore-errors (&rest forms)
  ` (condition-case nil
         (progn ,@forms)
       (error nil)))

(put 'ignore-errors 'edebug-form-spec
     '(sexp form body))

(defun lyskom-assert-error (&rest args)
  (save-excursion
    (save-match-data
      (if (not (and (boundp 'lyskom-buffer)
                    (bufferp lyskom-buffer)))
          (let ((debug-on-error t))
            (apply 'error args))
        (set-buffer lyskom-buffer)
        (lyskom-save-backtrace)
        (apply 'message args)
        (lyskom-insert-before-prompt (concat (apply 'format args) "\n"))
        (lyskom-insert-before-prompt (format "%s\n" (car (car lyskom-backtrace-list))))
        (lyskom-insert-before-prompt (format "%s\n" (elt (car lyskom-backtrace-list) 1)))))))

(eval-and-compile
  (if lyskom-debug-compile
      (defsubst lyskom-assert (check &rest args)
        (or check (if args (apply 'lyskom-assert-error args)
                    (lyskom-assert-error "Assertion failed"))))
    (defsubst lyskom-assert (check &rest args) nil)))

(eval-and-compile
  (if lyskom-debug-compile
      (defmacro lyskom-debug-forms (&rest forms)
        `(progn ,@forms))
    (defmacro lyskom-debug-forms (&rest forms))))

;;; ============================================================
;;; Local variables
;;;

(defmacro lyskom-setq-default (name value)
  ` (lyskom-set-default (quote ,name)
                         ,value))

(defmacro lyskom-ref-no ()
  `(prog1 lyskom-ref-no
     (setq lyskom-ref-no (1+ lyskom-ref-no))
     (when (< lyskom-ref-no 0) (setq lyskom-ref-no 1))))


(defsubst lyskom-mime-content-type (ct) (car ct))
(defsubst lyskom-mime-content-type-get (ct param) (cdr (assq param (cdr ct))))

(eval-and-compile (provide 'lyskom-macros))

;;; Local Variables: 
;;; eval: (put 'lyskom-traverse 'lisp-indent-hook 2)
;;; eval: (put 'lyskom-save-excursion 'lisp-indent-hook 0)
;;; eval: (put 'lyskom-ignore-errors 'lisp-indent-hook 2)
;;; eval: (put 'lyskom-with-external-functions 'lisp-indent-hook 1)
;;; end: 
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;; Copyright (C) 2001 Free Software Foundation, Inc.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: compatibility.el
;;;;
;;;; This file contains functions that may not exist in all supported
;;;; versions of Gnu Emacs. XEmacs-specific and Emacs 18-specific code
;;;; should go in some other file.
;;;;


;;; ============================================================
;;; Utility macros

(eval-and-compile
  (defvar lyskom-compatibility-definitions nil
    "Functions defined or redefined because they are incompatible with LysKOM")


(defmacro lyskom-provide-function (name &rest rest)
  "If NAME is not already defined, define it as a function."
  `(eval-and-compile
     (or (fboundp ',name)
         (progn (setq lyskom-compatibility-definitions
                      (cons ',name lyskom-compatibility-definitions))
                (defun ,name ,@rest)))))

(defmacro lyskom-provide-macro (name &rest rest)
  "If NAME is not already defined, define it as a macro."
  `(eval-and-compile
     (or (fboundp ',name)
         (progn (setq lyskom-compatibility-definitions
                      (cons ',name lyskom-compatibility-definitions))
                (defmacro ,name ,@rest)))))

(defmacro lyskom-macro-alias (name args &rest body)
  "Create a LysKOM name alias. If NAME is defined, then
lyskom-NAME will be an alias to it. Otherwise, lyskom-NAME
will be defined to be a macro with ARGS and BODY."
  (let ((alias-name (intern (concat "lyskom-" (symbol-name name)))))
    `(eval-and-compile
       (if (fboundp ',name)
           (defalias ',alias-name ',name)
         (setq lyskom-compatibility-definitions
               (cons ',name lyskom-compatibility-definitions))
         (defmacro ,alias-name ,args ,@body)))))

(defmacro lyskom-function-alias (name args &rest body)
  "Create a LysKOM name alias. If NAME is defined, then
lyskom-NAME will be an alias to it. Otherwise, lyskom-NAME
will be defined to be a function with ARGS and BODY."
  (let ((alias-name (intern (concat "lyskom-" (symbol-name name)))))
    `(eval-and-compile
       (if (fboundp ',name)
           (defalias ',alias-name ',name)
         (setq lyskom-compatibility-definitions
               (cons ',name lyskom-compatibility-definitions))
         (defun ,alias-name ,args ,@body)))))

(defmacro lyskom-xemacs-or-gnu (xemacs-form gnu-form)
  "Eval XEMACS-FORM in XEmacs and GNU-FORM in Gnu Emacs."
  (if (string-match "XEmacs" (emacs-version))
      xemacs-form
    gnu-form))
)



;;; ======================================================================
;;; Various definitions


;; Lots of Emacsen have buggy definitions of kbd (or no definition at
;; all) Although it's crufty to redefine a function from subr.el, I
;; will do so if it appears to be misbehaving. Don't like it? Tough!

(eval-and-compile
  (or (condition-case nil
           (or (equal (kbd (identity "<down-mouse-2>"))
                      [down-mouse-2])
               (error "Bad definition of kbd"))
         (error nil))
       (progn (fmakunbound 'kbd)
              (lyskom-provide-macro kbd (keys)
                "Convert KEYS to the internal Emacs key representation.
KEYS should be a string in the format used for saving keyboard macros
\(see `insert-kbd-macro')."
                (if (or (stringp keys)
                        (vectorp keys))
                    (read-kbd-macro keys)
                  `(read-kbd-macro ,keys))))))


;; Functions mostly from XEmacs that we use

(lyskom-macro-alias compiled-function-p (arg) `(byte-code-function-p ,arg))
(lyskom-function-alias characterp (obj) (integerp obj))
(lyskom-function-alias int-to-char (obj) obj)
(lyskom-function-alias char-to-int (c) c)
(lyskom-function-alias signum (num) (cond ((< num 0) -1)
                                          ((> num 0) 1)
                                          (t 0)))

;; If we use lyskom-function-alias here we'll get an obsolecense
;; warning about screen-width. This way the compiler won't notice :-)

(defun lyskom-frame-width ())
(if (fboundp 'frame-width)
    (defalias 'lyskom-frame-width 'frame-width)
  (fset 'lyskom-frame-width 'screen-width))

;; Some minibuffer compatibility stuff

(lyskom-with-external-functions (temp-minibuffer-message)
  (lyskom-function-alias minibuffer-contents () (buffer-string))
  (lyskom-function-alias minibuffer-message (message) 
    (temp-minibuffer-message message)))

;; Definition of map-keymap that hopefully works like the one in XEmacs
;; except that the sort-first argument is ignored.

(lyskom-function-alias map-keymap (fn keymap &optional sort-first)
  "Map FN over all bindings in KEYMAP."
  (let ((r 0))
    (when (keymapp keymap)
      (when (symbolp keymap) (setq keymap (symbol-value keymap)))
      (let ((keymap (cdr keymap))
            (el nil))
        (while (and keymap (not (keymapp keymap)))
          (setq el (car keymap) keymap (cdr keymap))
          (cond ((vectorp el)
                 (while (< r (length el))
                   (if (aref el r)
                       (funcall fn r (aref el r)))
                   (setq r (1+ r))))
                ((char-table-p el)
                 (map-char-table fn el))
                ((consp el) (funcall fn (car el) (cdr el)))))))))


;; set-keymap-parent also comes from XEmacs

(lyskom-function-alias set-keymap-parent (keymap new-parent)
  (let ((tail keymap))
    (while (and tail (cdr tail) (not (eq (car (cdr tail)) 'keymap)))
      (setq tail (cdr tail)))
    (if tail
        (setcdr tail new-parent))))


;; XEmacs and Gnu Emacs don't use the same names for mouse events
;; and such. This is to help us deal with that.

(defconst lyskom-gnu-keysym
  '((button1   . "<down-mouse-1>")
    (button2   . "<down-mouse-2>")
    (button3   . "<down-mouse-3>")
    (button1up . "<mouse-1>")
    (button2up . "<mouse-2>")
    (button3up . "<mouse-3>")))

(defconst lyskom-xemacs-keysym
  '((button1   . "<button1>")
    (button2   . "<button2>")
    (button3   . "<button3>")
    (button1up . "<button1up>")
    (button2up . "<button2up>")
    (button3up . "<button3up>")))


(defun lyskom-keys (sym)
  "Look up the key description for key SYM."
  (cdr (assq sym (lyskom-xemacs-or-gnu lyskom-xemacs-keysym
                                       lyskom-gnu-keysym))))



;;; ============================================================
;;; MULE stuff
;;;
;;; We used to define dummy versions of basic MULE functions so
;;; we could code using the standard names. We don't do that any
;;; more. Instead, we define aliases in our own namespace.

(unless (boundp 'enable-multibyte-characters)
  (defvar enable-multibyte-characters nil))

(lyskom-function-alias set-buffer-multibyte (arg)
  (put 'enable-multibyte-characters 'permanent-local t)
  (make-local-variable 'enable-multibyte-characters)
  (setq enable-multibyte-characters arg))

(lyskom-function-alias set-process-coding-system (proc &optional encoding decoding)) 
(lyskom-function-alias encode-coding-string (str coding-system) (copy-sequence str))
(lyskom-function-alias decode-coding-string (str coding-system) (copy-sequence str))
(lyskom-function-alias string-bytes (str) (length str))
(lyskom-function-alias check-coding-system (name) (error "No such coding system"))
(lyskom-function-alias find-coding-systems-for-charsets (cs) nil)
(lyskom-function-alias coding-system-get (cs prop) nil)
(lyskom-function-alias coding-system-aliases (cs) (list cs))
(lyskom-function-alias char-width (c) 1)
(lyskom-function-alias find-charset-string (str) (list (or (lyskom-default-value-safe 'lyskom-server-coding-system)
                                                           'ascii)))
(lyskom-function-alias string-as-unibyte (str) str)
(lyskom-function-alias string-make-unibyte (str) str)
(lyskom-function-alias string-make-multibyte (str) str)
(lyskom-function-alias multibyte-string-p (str) nil)
(lyskom-function-alias charsetp (arg) nil)
(lyskom-function-alias coding-system-type (arg) nil)
(lyskom-function-alias coding-system-property (arg sym) (lyskom-coding-system-get arg sym))
(lyskom-function-alias split-string (string &optional separators)
  (string-split (or separators "[ \f\t\n\r\v]+") string))

;; Detect buggy versions of encode-coding-string and
;; decode-coding-string such as those provided by APEL (part of TM and
;; often included in XEmacs)

(defun lyskom-encode-coding-string-with-copy-bug (str coding-system) str)
(eval-and-compile
  (if (let ((test "TEM")) (eq (lyskom-encode-coding-string test 'raw-text) test))
      (progn (fset 'lyskom-encode-coding-string-with-copy-bug
                   (symbol-function 'lyskom-encode-coding-string))
             (defun lyskom-encode-coding-string (str coding-system)
               (copy-sequence (lyskom-encode-coding-string-with-copy-bug
                               str coding-system))))))

;; Emacs 20.7, 21.2, 21.3 and CVS versions before 2005-02-14 have a bug
;; where the result of encode-coding-string is sometimes a multibyte
;; string. Detect this problem.

(defun lyskom-encode-coding-string-with-multibyte-bug (str coding-system) str)
(eval-and-compile
  (if (lyskom-multibyte-string-p
       (lyskom-encode-coding-string
        (substring (concat "å" (make-string 1025 ?x)) 1) 'iso-8859-1))
      (progn (fset 'lyskom-encode-coding-string-with-multibyte-bug
                   (symbol-function 'lyskom-encode-coding-string))
             (defun lyskom-encode-coding-string (str coding-system)
               (string-make-unibyte
                (lyskom-encode-coding-string-with-multibyte-bug str coding-system))))))


(defun lyskom-buggy-decode-coding-string (str coding-system) str)
(eval-and-compile
  (if (let ((test "TEM")) (eq (lyskom-decode-coding-string test 'raw-text) test))
      (progn (fset 'lyskom-buggy-decode-coding-string
                   (symbol-function 'lyskom-decode-coding-string))
             (defun lyskom-decode-coding-string (str coding-system)
               (copy-sequence (lyskom-buggy-decode-coding-string str coding-system))))))


;; defmacro lyskom-encode-coding-char in XEmacs so the compiled code
;; is quicker. In Gnu Emacs define it as a function.
;;
;; The definition is made at compile-time to avoid getting warnings 
;; about encode-coding-char.

(eval-and-compile
  (cond ((eval-when-compile (string-match "XEmacs" (emacs-version)))
         (defmacro lyskom-encode-coding-char (c system) c))
        ((eval-when-compile (fboundp 'encode-coding-char))
         (defun lyskom-encode-coding-char (c system)
           (let ((s (encode-coding-char c system)))
             (if (and s (= (length s) 1))
                 (elt s 0)))))
        (t (defmacro lyskom-encode-coding-char (c system) c))))


;; It seems that string-width is buggy with respect to handling
;; unibyte strings in multibyte environments or vice versa. Or
;; something like that. There has got to be a good explanation
;; for the mess below.

(eval-and-compile
  (if (fboundp 'string-width)
      (defalias 'lyskom-original-string-width 'string-width)
    (defmacro lyskom-original-string-width (s) `(length ,s)))
  (lyskom-xemacs-or-gnu
   (defalias 'lyskom-string-width 'string-width)
   (defun lyskom-string-width (str)
     (cond ((and (lyskom-multibyte-string-p str)
                 (null enable-multibyte-characters))
            (lyskom-original-string-width (lyskom-string-make-unibyte str)))
           ((and (null (lyskom-multibyte-string-p str))
                 enable-multibyte-characters)
            (lyskom-original-string-width (lyskom-string-make-multibyte str)))
           (t (lyskom-original-string-width str))))))

;; XEmacs doesn't have detect-coding-string
;; Make a version that just plain doesn't do anything

(lyskom-function-alias detect-coding-string (string &optional highest)
  (if highest 'undecided '(undecided)))


;;; ================================================================
;;; Faces and windows and stuff

(lyskom-function-alias frame-property (frame property &optional default)
  (or (cdr (assq property (frame-parameters frame))) default))

(lyskom-function-alias face-background (face) nil)
(lyskom-function-alias face-foreground (face) nil)

(lyskom-function-alias find-face (face) (and (facep face) face))

(defun lyskom-make-face (name temporary)
  "Like make-face in XEmacs"
  (lyskom-xemacs-or-gnu (make-face name nil temporary)
                        (make-face name)))

(lyskom-macro-alias set-face-underline (face underline &optional frame)
  "Compatibility for older emacs that don't have set-face-underline
Calls set-face-underline-p."
  `(set-face-underline-p ,face ,underline ,frame))

(if (not (lyskom-find-face 'strikethrough))
    (progn (make-face 'strikethrough)
           (if (eval-when-compile (fboundp 'set-face-strikethru-p))
               (set-face-strikethru-p 'strikethrough t)
             (lyskom-set-face-underline 'strikethrough t))))


(defun lyskom-get-buffer-window-list (buffer &optional minibuf frame)
  "Return windows currently displaying BUFFER, or nil if none.
See `walk-windows' for the meaning of MINIBUF and FRAME."
  (let ((buffer (if (bufferp buffer) buffer (get-buffer buffer))) windows)
    (walk-windows (function (lambda (window)
			      (if (eq (window-buffer window) buffer)
				  (setq windows (cons window windows)))))
		  minibuf frame)
    windows))


; ==================================================================
; Image display

(lyskom-function-alias display-images-p () nil)
(lyskom-function-alias put-image (what where) nil)
(lyskom-function-alias create-image (file-or-data) nil)

;;; ================================================================
;;; Color stuff

(lyskom-function-alias color-specifier-p (obj) nil)
(lyskom-function-alias color-name (obj) nil)

(lyskom-with-external-functions (color-rgb-components
                                 make-specifier
                                 set-specifier)
  (eval-and-compile
    (cond ((and (fboundp 'color-rgb-components)
                (fboundp 'make-specifier)
                (fboundp 'set-specifier))
           (defun lyskom-color-values (color)
             (when (stringp color)
               (let ((spec nil))
                 (set-specifier (setq spec (make-specifier 'color)) color)
                 (setq color spec)))
             (color-rgb-components color)))
          ((fboundp 'color-values) (fset 'lyskom-color-values 'color-values))
          ((fboundp 'x-color-values) (fset 'lyskom-color-values 'x-color-values)))))



;;; ======================================================================
;;; Event stuff

(lyskom-with-external-functions (event-start)
  (lyskom-function-alias event-glyph (e))
  (lyskom-function-alias event-point (e)
    "Return the character position of the given mouse event.
If the event did not occur over a window, or did not occur over text,
then this returns nil.  Otherwise, it returns an index into the buffer
visible in the event's window."
    (car (cdr (event-start e))))

  (lyskom-function-alias event-closest-point (e)
    "Return the character position closest to the mouse event EVENT."
    (car (cdr (event-start e))))

  (lyskom-function-alias event-type (e)
    (event-basic-type e)))


;;; ======================================================================
;;; Platform-specific stuff

(lyskom-provide-function w32-shell-execute (&rest args)
  "Dummy function that raises an error."
  (error "w32-shell-execute undefined"))



;;; ================================================================
;;; We provide these functions in the default namespace since there is
;;; no reason to beleive that (a) the definitions suck (they're taken
;;; from either XEmacs or Gnu Emacs) and (b) no package should be
;;; using them to figure out what environment it is running in
;;; (unlike, say, the MULE functions).
;;;
;;; There are also a few functions (regexpp and functionp) that may
;;; appear in some version of Emacs, and if they do we accept those
;;; definitions.
;;;

(lyskom-provide-function last (x &optional n)
  "Returns the last link in the list LIST.
With optional argument N, returns the Nth-to-last link (default 1)."
  "Returns the last link in the list LIST.
With optional argument N, returns Nth-to-last link (default 1)."
  (if n
      (let ((m 0) (p x))
	(while (consp p) (setq m (1+ m)) (setq p (cdr p)))
	(if (<= n 0) p
	  (if (< n m) (nthcdr (- m n) x) x)))
    (while (consp (cdr x)) (setq x (cdr x)))
    x))

(lyskom-provide-function plist-member (plist prop)
  "Return non-nil if PLIST has the property PROP.
PLIST is a property list, which is a list of the form
\(PROP1 VALUE1 PROP2 VALUE2 ...\). PROP is a symbol.
Unlike `plist-get', this allows you to distinguish between a missing
property and a property with the value nil."
      (while (and plist (not (eq (car plist) prop)))
        (setq plist (cdr (cdr plist))))
      (and plist t))

;; The make-temp-file function below is taken verbatim from Emacs 21.2.
(lyskom-provide-function make-temp-file (prefix &optional dir-flag)
  "Create a temporary file.  The returned file name (created by
appending some random characters at the end of PREFIX, and expanding
against `temporary-file-directory' if necessary, is guaranteed to
point to a newly created empty file.  You can then use `write-region'
to write new data into the file.

If DIR-FLAG is non-nil, create a new empty directory instead of a file."
  (let (file)
    (while (condition-case ()
	       (progn
		 (setq file
		       (make-temp-name
			(expand-file-name prefix 
                                          (lyskom-xemacs-or-gnu
                                           (temp-directory)
                                           temporary-file-directory))))
		 (if dir-flag
		     (make-directory file)
		   (write-region "" nil file nil 'silent nil))
		 nil)
             (file-already-exists t))
      ;; the file was somehow created by someone else between
      ;; `make-temp-name' and `write-region', let's try again.
      nil)
    file))

(lyskom-provide-function replace-in-string
    (str regexp newtext &optional literal)
  "Replaces all matches in STR for REGEXP with NEWTEXT string.
Optional LITERAL non-nil means do a literal replacement.
Otherwise treat \\ in NEWTEXT string as special:
  \\& means substitute original matched text,
  \\N means substitute match for \(...\) number N,
  \\\\ means insert one \\."
  (if (not (stringp str))
      (error "(replace-in-string): First argument must be a string: %s" str))
  (if (stringp newtext)
      nil
    (error "(replace-in-string): 3rd arg must be a string: %s"
	   newtext))
  (let ((rtn-str "")
	(start 0)
	(special)
	match prev-start)
    (while (setq match (string-match regexp str start))
      (setq prev-start start
	    start (match-end 0)
	    rtn-str
	    (concat
	      rtn-str
	      (substring str prev-start match)
	      (cond (literal newtext)
		    (t (mapconcat
			 (function
			   (lambda (c)
			     (if special
				 (progn
				   (setq special nil)
				   (cond ((eq c ?\\) "\\")
					 ((eq c ?&)
					  (substring str
						     (match-beginning 0)
						     (match-end 0)))
					 ((and (>= c ?0) (<= c ?9))
					  (if (> c (+ ?0 (length
							   (match-data))))
					      ;; Invalid match num
					      (error "(replace-in-string) Invalid match num: %c" c)
					    (setq c (- c ?0))
					    (substring str
						       (match-beginning c)
						       (match-end c))))
					 (t (char-to-string c))))
			       (if (eq c ?\\) (progn (setq special t) nil)
				 (char-to-string c)))))
			 newtext ""))))))
    (concat rtn-str (substring str start))))


(lyskom-provide-function buffer-live-p (object)
  "T of OBJECT is an editor buffer that has not been deleted."
  (and (bufferp object)
       (buffer-name object)))

(lyskom-provide-function string-to-vector (string)
  "Return a vector of characters in STRING."
  (let ((len (length string)) (i 0) val)
    (setq val (make-vector len 0))
    (while (< i len)
      (aset val i (aref string i))
      (setq i (1+ i)))
    val))

(lyskom-provide-function copy-tree (l)
  "Recursively copy the list L"
  (cond ((atom l) l)
        (t (cons (copy-tree (car l))
                 (copy-tree (cdr l))))))

(lyskom-provide-function functionp (obj)
  "Returns t if OBJ is a function, nil otherwise."
  (cond ((symbolp obj) (fboundp obj))
        ((subrp obj))
        ((lyskom-compiled-function-p obj))
        ((consp obj) (and (eq (car obj) 'lambda) (listp (car (cdr obj)))))
        (t nil)))

(lyskom-provide-function regexpp (re)
  "Return non-nil if RE looks like a valid regexp."
  (let ((result t))
    (save-match-data
      (condition-case nil
          (string-match re "")
        (error (setq result nil))))
    result))

(lyskom-macro-alias char-before (&optional pos buffer)
  `(save-excursion
     (save-restriction 
       (widen)
       ,@(if buffer `((set-buffer ,buffer)))
       ,(if pos 
	    `(if (or (> ,pos (point-max))
		     (<= ,pos (point-min)))
		 nil
	       (goto-char ,pos)
	       (preceding-char))
	   `(if (<= (point) (point-min))
		nil
	      (preceding-char))))))

(lyskom-function-alias make-hash-table (&rest args)
  "Create and return a new hash table.

This is a limited emulation of the make-hash-table function available
in some Emacsen. It ignores all arguments. The only test supported is
`eq'."
  (let ((test (car (cdr (memq ':test args)))))
    (or (eq test 'eq) (lyskom-error "internal error: invalid hash function"))
    (cons 'HASH nil)))

(lyskom-function-alias gethash (key table)
  "Look up KEY in TABLE and return its associated value.

This is a limited emulation of the gethash function available in some
Emacsen. It does not support the default argument."
  (cdr (assq key (cdr table))))

(lyskom-function-alias puthash (key value table)
  "Associate KEY with VALUE in hash table TABLE.
If KEY is already present in table, replace its current value with
VALUE.

This is a limited emulation of the puthash function available in some
Emacsen."
  (let ((tmp (assq key (cdr table))))
    (if tmp
        (setcdr tmp value)
      (setcdr table (cons (cons key value) (cdr table))))))

(lyskom-function-alias remhash (key table)
  "Remove KEY from TABLE.

This is a limited emulation of the remhash function available in some
Emacsen."
  (let ((tmp (assq key (cdr table))))
    (when tmp
      (setcdr table (delq tmp (cdr table))))))


;;; ================================================================
;;; Some symbols are self-evaluating. They're not in all versions
;;; of Emacs that we would like to support. So we make them
;;; self-evaluating here.

;; This code looks the way it does in order to avoid warnings in
;; Emacs 21.

(eval-when-compile
  (if (boundp ':lyskom-is-this-self-evaluating)
      (defmacro lyskom-make-self-evaluating (var))
    (defmacro lyskom-make-self-evaluating (var)
      `(eval-and-compile (set ',var ',var)))))

(lyskom-make-self-evaluating :default)
(lyskom-make-self-evaluating :mime-charset)
(lyskom-make-self-evaluating :default-help-echo)
(lyskom-make-self-evaluating :group)
(lyskom-make-self-evaluating :automatic)
(lyskom-make-self-evaluating :read-only)
(lyskom-make-self-evaluating :filter)
(lyskom-make-self-evaluating :filter-args)
(lyskom-make-self-evaluating :constraint)
(lyskom-make-self-evaluating :save)
(lyskom-make-self-evaluating :refer)
(lyskom-make-self-evaluating :width)
(lyskom-make-self-evaluating :prompt)
(lyskom-make-self-evaluating :align)
(lyskom-make-self-evaluating :format)
(lyskom-make-self-evaluating :output)
(lyskom-make-self-evaluating :subject-mode)
(lyskom-make-self-evaluating :subject-indent)
(lyskom-make-self-evaluating :text)
(lyskom-make-self-evaluating :text-stat)
(lyskom-make-self-evaluating :text-no)
(lyskom-make-self-evaluating :subjects)
(lyskom-make-self-evaluating :subject-last)
(lyskom-make-self-evaluating :comment-order)
(lyskom-make-self-evaluating :unique)
(lyskom-make-self-evaluating :weight)
(lyskom-make-self-evaluating :prompt-format)
(lyskom-make-self-evaluating :dead-ok)
(lyskom-make-self-evaluating :mark)
(lyskom-make-self-evaluating :test)
(lyskom-make-self-evaluating :size)
(lyskom-make-self-evaluating :may-interrupt)


;;; ================================================================
;;; Emacs 21.4 stuff

(lyskom-function-alias function-obsolete-p (function)
  "Return non-nil if FUNCTION is obsolete"
  (or (not (fboundp function))
      (get function 'byte-obsolete-info)))

(eval-and-compile
  (if (lyskom-function-obsolete-p 'process-kill-without-query)
      (if (fboundp 'set-process-query-on-exit-flag)
          (fset 'lyskom-set-process-query-on-exit-flag 'set-process-query-on-exit-flag)
        (defun lyskom-set-process-query-on-exit-flag (&rest args)))
    (fset 'lyskom-set-process-query-on-exit-flag 'process-kill-without-query)))

(eval-and-compile
  (if (lyskom-function-obsolete-p 'make-local-hook)
      (defun lyskom-make-local-hook (hook))
    (fset 'lyskom-make-local-hook 'make-local-hook)))

;;; ================================================================
;;; Emacs 22.1 stuff

(defun lyskom-string-to-number(string &optional base))
(if (fboundp 'string-to-number)
    (defalias 'lyskom-string-to-number 'string-to-number)
  (defalias 'lyskom-string-to-number 'string-to-int))

(defun lyskom-pos-visible-in-window-p-22-1 (&optional pos window)
  "Workaround for the buggy pos-visible-in-window-p in Gnu Emacs"
  (setq pos (or pos (point)))
  (condition-case nil
      (cond ((/= pos (point-max))
	     (pos-visible-in-window-p pos window))
	    ((eq ?\n (char-after (1- (point-max))))
	     (pos-visible-in-window-p pos window))
	    (t (pos-visible-in-window-p (1- pos) window)))
    (pos-visible-in-window-p pos window)))

(defalias 'lyskom-pos-visible-in-window-p 'pos-visible-in-window-p)
(when (and (not window-system)
	   (not (featurep 'xemacs))
	   (eq emacs-major-version 22)
	   (<= emacs-minor-version 1))
  (defalias 'lyskom-pos-visible-in-window-p
    'lyskom-pos-visible-in-window-p-22-1))

;;; ================================================================
;;; For emacs-23.2 and later.

(defmacro lyskom-called-interactively-p (kind)
  "Calls called-interactively-p if supported, interactive-p if not."
  (condition-case nil
      (progn (called-interactively-p nil)
             ;; If the call didn't signal an error, then the new form
             ;; is supported: use it.
             `(called-interactively-p ,kind))
    (wrong-number-of-arguments
     ;; Probably Emacs-23.1.
     (if (equal (eval kind) 'interactive)
         `(interactive-p)
       `(called-interactively-p)))
    (error
     ;; called-interactively-p seems not to be supported, fallback
     ;; on the good ol' interactive-p.
     `(interactive-p))))

(lyskom-macro-alias read-only-mode (&optional arg)
  "Compatibility for older emacs that don't have read-only-mode.
Calls toggle-read-only."
  `(toggle-read-only ,arg))

;;; ================================================================
;;; ================================================================
;;; ================================================================


;;; Set up edebug-form-spec and friends. We do this here because
;;; we have to have the eval block at the end, and this at least
;;; keeps everything together (it would have been nicer to put
;;; an eval block with each definition, but I don't think that
;;; would look good, and it might not always work).

(put 'lyskom-provide-function 'lisp-indent-hook 2)
(put 'lyskom-function-alias 'lisp-indent-hook 2)
(put 'lyskom-macro-alias 'lisp-indent-hook 2)
(put 'lyskom-xemacs-or-gnu 'edebug-form-spec '(form form))

;;; Local Variables:
;;; eval: (put 'lyskom-provide-function 'lisp-indent-hook 2)
;;; eval: (put 'lyskom-function-alias 'lisp-indent-hook 2)
;;; eval: (put 'lyskom-macro-alias 'lisp-indent-hook 2)
;;; eval: (put 'lyskom-xemacs-or-gnu 'edebug-form-spec '(form form))
;;; end:
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: language.el
;;;; Author: Niels Möller
;;;;
;;;;

(require 'lyskom-vars "vars")

;;; Variables

;;(defvar lyskom-language-symbols nil
;;  "Symbols with language data bound to them")

(defvar lyskom-languages nil
  "An alist of defined languages.
Each entry is a pair (SYMBOL . (NAME NAME ...)) where symbol is the symbol
used for identification, and the NAMEs are names of the language.")

(defvar lyskom-language-categories nil
  "Categories of language-specific variables.
Each element is a cons cell (NAME . SCOPE), where NAME is the name of
the category and SCOPE is its scope (global or local).")

(defvar lyskom-language-vars nil
  "A list of all language-dependent variables.
Each element is a cons cell (NAME . SCOPE), where NAME is the name of
the category and SCOPE is its scope (global or local).")


(defun lyskom-language-var-internal (scope var language val)
  "Defines a language-local variable value."
  (or (assq var lyskom-language-vars)
      (setq lyskom-language-vars
	    (cons (cons var scope) lyskom-language-vars)))
  (let* ((alist (get var 'lyskom-language-var))
	 (entry (assq language alist)))
    (if entry
	(setcdr entry val)
      (put var 'lyskom-language-var (cons (cons language val) alist)))))

(defmacro lyskom-language-var (scope var language val)
  `(lyskom-language-var-internal ',scope
                                 ',var
                                 ',language
                                 ',val))

(put 'lyskom-language-var 'lisp-indent-function 2)

(defun lyskom-set-language-vars (language scope)
  "Set language-specific variables to values for LANGUAGE.
SCOPE must be one of global or local, and specifies the scope of the change.
If SCOPE is global, change all variables, even those that affect multiple
sessions."
  (mapcar
   (lambda (spec)
     (let ((var (car spec))
           (var-scope (cdr spec)))
       (when (or (eq scope 'global)
                 (eq var-scope 'local))
         (when (eq scope 'global)
           (set-default var (eval (cdr (assq language
                                     (get var 'lyskom-language-var))))))
         (when (or (not (symbol-value var))
                   (get var 'lyskom-language-force))
           (set var (eval (cdr (assq language
                                     (get var 'lyskom-language-var)))))))))
   lyskom-language-vars))

;;; Keymaps

(defvar lyskom-language-keymaps nil
  "A list of all language-dependent keymaps.")

(defun lyskom-language-keymap-internal (keymap language langmap)
  "Defines a language-local keymap."
  ;; If the "real" keymap has no value, set it to an empty keymap
  (if (eval keymap)
      nil
    (set keymap (make-sparse-keymap))
    (define-key (symbol-value keymap) [follow-link] 'mouse-face))
  ;; Add it to the list of keymaps
  (or (memq keymap lyskom-language-keymaps)
      (setq lyskom-language-keymaps
	    (cons keymap lyskom-language-keymaps)))
  ;; Modify the property list
  (let* ((alist (get keymap 'lyskom-language-keymap))
	 (entry (assq language alist)))
    (if entry
	(setcdr entry langmap)
      (put keymap 'lyskom-language-keymap
	   (cons (cons language langmap) alist)))))

(defmacro lyskom-language-keymap (keymap language langmap)
  `(lyskom-language-keymap-internal ',keymap
                                    ',language
                                    ',langmap))

(put 'lyskom-language-keymap 'lisp-indent-function 2)

(defun lyskom-set-language-keymaps (language)
  (mapcar
   (lambda (map)
     (lyskom-set-keymap-parent (symbol-value map)
                        (eval (cdr (assq language
                                         (get map
                                              'lyskom-language-keymap))))))
   lyskom-language-keymaps))



;;; String catalogs

(defun lyskom-language-strings-internal (scope category language alist)
  "Associates names to symbols.
See documentation of lyskom-language-strings for information on the 
parameters to this function."
  ;; Record category
  (or (assq category lyskom-language-categories)
      (setq lyskom-language-categories
	    (cons (cons category scope) lyskom-language-categories)))
  (let ((record (get category 'lyskom-language-symbols)))
    (mapc (lambda (pair)
              (let* ((symbol (car pair))
                     (string (cdr pair))
                     (llist (get symbol category))
                     (entry (assq language llist)))
                ;; Record symbol
                (or (memq symbol record)
                    (setq record (cons symbol record)))
                (if entry
                    (setcdr entry string)
                  (put symbol category (cons (cons language string) llist)))))
	    alist)
    (put category 'lyskom-language-symbols record)))

(defmacro lyskom-language-strings (scope category language alist)
  "Define a category of strings.
SCOPE is the scope of language-specificity. If it is global, then these
strings apply globally and will not be altered by changing the session
language.

CATEGORY is the name of the category.

LANGUAGE is the language, a symbol denoting the ISO639 language code.

ALIST is the list of strings."
  `(lyskom-language-strings-internal ',scope
                                     ',category
                                     ',language
                                     ,alist))

(defun lyskom-language-missing-string-internal (category string languages)
  (let ((old-missing (assq 'lyskom-missing-languages (get string category))))
    (if old-missing
        (setcdr old-missing (append languages (cdr old-missing)))
      (put string category (cons (cons 'lyskom-missing-languages languages)
                                 (get string category))))))

(defun lyskom-language-ending-mismatch-internal (category string l1 l2)
  (let ((old-mismatch (assq 'lyskom-ending-mismatch (get string category))))
    (if old-mismatch
        (setcdr old-mismatch (append (list (cons l1 l2) (cons l2 l1))
                                     (cdr old-mismatch)))
      (put string category (cons (cons 'lyskom-ending-mismatch 
                                       (list (cons l1 l2) (cons l2 l1)))
                                 (get string category))))))

(defmacro lyskom-language-missing-string (category string &rest languages)
  `(lyskom-language-missing-string-internal ',category ',string ',languages))

(defmacro lyskom-language-ending-mismatch (category string l1 l2)
  `(lyskom-language-ending-mismatch-internal ',category ',string ',l1 ',l2))

(put 'lyskom-language-strings 'lisp-indent-function 2)

(defsubst lyskom-tell-string (key)
  "Retrieve the phrase indexed by the key from the kom-tell-phrases
assoc list."
  (condition-case nil
      (lyskom-get-string key 'kom-tell-phrases)
    (lyskom-internal-error (message "Bad kom-tell-phrases: missing %s" key)
                           "")))

(defsubst lyskom-try-get-string (symbol category &optional language)
    (cdr (assq (if (eq (cdr (assq category lyskom-language-categories)) 'local)
                   (or language lyskom-language)
                 (or language lyskom-global-language))
               (get symbol category))))

(defsubst lyskom-get-string-error (function symbol category)
  (signal 'lyskom-internal-error
	  (list function (list symbol category ": string not found"))))

(defun lyskom-get-string (symbol &optional category language)
  "Returns string associated with SYMBOL"
    (or (lyskom-try-get-string symbol (or category 'lyskom-message) language)
        (lyskom-get-string-error 'lyskom-get-string
                                 symbol
                                 (or category 'lyskom-message))))

(defun lyskom-get-string-sol (symbol &optional category)
  "Returns string associated with SYMBOL
If kom-long-lines is set, return the long form of the string, if it exists."
  (or  (and kom-long-lines
            (lyskom-try-get-string (intern (concat (symbol-name symbol)
                                                        "-long"))
                                        (or category 'lyskom-message)))
       (lyskom-try-get-string symbol
                                   (or category 'lyskom-message))
       (lyskom-get-string-error 'lyskom-get-string
                                symbol
                                (or category 'lyskom-message))))


(defun lyskom-get-strings (symbols &optional category)
  "Returns an alist of (symbol . string) pairs

according to CATEGORY and lyskom-language. Kind of inverse to
lyskom-define-language."
  (mapcar (lambda (symbol)
            (cons symbol (lyskom-get-string symbol category)))
	  symbols))

(defun lyskom-get-menu-string (symbol)
  "Returns the name of a menu(item)

Looks for the 'lyskom-menu category, or 'lyskom-command
if 'lyskom-menu is not found."
  (or (lyskom-try-get-string symbol 'lyskom-menu)
        (lyskom-try-get-string symbol 'lyskom-command)
        (lyskom-get-string-error 'lyskom-get-menu-string symbol 'lyskom-menu)))

(defun lyskom-define-language (language coding &rest names)
  (let ((match (assq language lyskom-languages)))
    (if match
	(setcdr match names)
      (setq lyskom-languages (cons (cons language names) lyskom-languages))))
  (put language 'lyskom-language-coding coding))

(defun lyskom-language-coding (language)
  (or (get language 'lyskom-language-coding)
      'raw-text))

(defun lyskom-language-name (language)
  "Return the name of language code LANGUAGE in the current language."
  (save-excursion
    (when lyskom-buffer (set-buffer lyskom-buffer))
    (or (cdr (assq language lyskom-language-codes))
        (lyskom-format (cdr (assq '-- lyskom-language-codes))
                       (symbol-name language)))))

(defun lyskom-set-language (language scope)
  "Set the current language to LANGUAGE.
Returns non-nil on success and nil on failure."
  (when (listp language) 
    (setq language (lyskom-first-available-language language)))
  (cond ((not (assq language lyskom-languages))
         (lyskom-format-insert-before-prompt 'language-not-loaded
                                             (lyskom-language-name language))
         nil)
        (t 
         (cond ((eq scope 'local) 
                (setq lyskom-language language))
               ((eq scope 'global) 
                (setq lyskom-global-language language)
                (lyskom-set-language-keymaps language)))
         (lyskom-set-language-vars language scope)
         (when (eq scope 'global) (lyskom-update-menus))
         (lyskom-update-prompt t)
         (lyskom-update-command-completion)
         t)))

(defun lyskom-first-available-language (langs)
  "Return the first language in LANGS that is supported."
  (or (lyskom-traverse el (if (listp langs) langs (list langs))
        (when (assq el lyskom-languages)
          (lyskom-traverse-break el)))
      (car (car lyskom-languages))))

(defun lyskom-language-from-environment (var)
  "Return language name from value of environment variable VAR."
  (let ((tmp (getenv var)))
    (and tmp 
         (string-match "^\\([a-z]+\\)" tmp)
         (intern (match-string 1 tmp)))))

(defun lyskom-default-language ()
  "Return the default language for LysKOM"
  (let ((languages (append
                    (if (listp kom-default-language)
                        kom-default-language
                      (list kom-default-language))
                    (list
                     (lyskom-language-from-environment "KOMLANGUAGE")
                     (lyskom-language-from-environment "LC_ALL")
                     (lyskom-language-from-environment "LC_MESSAGES")
                     (lyskom-language-from-environment "LANG")))))
    (or (lyskom-traverse lang languages
          (when (assq lang lyskom-languages)
            (lyskom-traverse-break lang)))
        (car (car (last lyskom-languages))))))




(eval-and-compile (provide 'lyskom-language))

;;; language.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: help.el
;;;;
;;;; Functions for formatting help strings in the client
;;;;


(defgroup lyskom-faces nil
  "Faces for use in the LysKOM elisp client."
  :group 'lyskom)

(defgroup lyskom nil
  "Faces for use in the LysKOM elisp client."
  :group 'default)

(defface lyskom-help-h1-face '((t (:weight bold :underline t :height 2.0 :family "helvetica")))
  "First level header in LysKOM help."
  :group 'lyskom-faces)

(defface lyskom-help-h2-face '((t (:weight bold :underline t :height 1.5 :family "helvetica")))
  "Second level header in LysKOM help."
  :group 'lyskom-faces)

(defface lyskom-help-h3-face '((t (:weight bold :family "helvetica")))
  "Third level header in LysKOM help."
  :group 'lyskom-faces)

;;; Note that these are duplicated in help-compile.el. Change one and change both!

;;UNUSED: lyskom-help-create-data
(defsubst lyskom-help-create-data (tag attr data)
  (list tag attr data))

(defsubst lyskom-help-data-get-tag (data)
  (elt data 0))

(defsubst lyskom-help-data-get-attr (attr data)
  (cdr (assq attr (elt data 1))))

;;UNUSED: lyskom-help-data-get-attrs
(defsubst lyskom-help-data-get-attrs (data)
  (elt data 1))

(defsubst lyskom-help-data-get-data (data)
  (elt data 2))


(defvar lyskom-help-format-handlers
  '((h1 . lyskom-help-format-h1)
    (h2 . lyskom-help-format-h2)
    (h3 . lyskom-help-format-h3)
    (p  . lyskom-help-format-p)
    (b  . lyskom-help-format-b)
    (i  . lyskom-help-format-i)
    (list . lyskom-help-format-list)
    (item . lyskom-help-format-item)
    (keymap . lyskom-help-format-keymap)
    (inline . lyskom-help-format-inline)
    (refer . lyskom-help-format-refer)
    (cref . lyskom-help-format-cref)
    (section . lyskom-help-format-section)
    (call . lyskom-help-call-function)
    (TEXT . lyskom-help-format-TEXT))
  )

(defun lyskom-help-get-section (section)
  (elt (assq section lyskom-help-data) 2))

(defun lyskom-help-show-section (section)
  "Format and insert section SECTION."
  (lyskom-do-help-format (lyskom-help-get-section section)))

(defun lyskom-do-help-format (data)
  "Format ant insert help data DATA."
  (let ((inhibit-read-only t))
    (cond ((symbolp (car data))
           (funcall (cdr (assq (car data) lyskom-help-format-handlers)) data))
          (t (lyskom-traverse el 
                 data
               (let ((tag (lyskom-help-data-get-tag el)))
                 (funcall (cdr (assq tag lyskom-help-format-handlers))
                          el)))))))

(defun lyskom-help-format-text-properties (data props)
  (let ((start (point-max-marker)))
    (set-marker-insertion-type start nil)
    (lyskom-traverse el (lyskom-help-data-get-data data)
      (lyskom-do-help-format el))
    (add-text-properties start (point-max) props)
    (set-marker start nil)))

(defun lyskom-help-format-section (section)
  "Format and insert section SECTION."
  (lyskom-do-help-format (lyskom-help-data-get-data section)))

(defun lyskom-help-format-h1 (data)
  (lyskom-insert "\n\n")
  (lyskom-help-format-text-properties data '(face lyskom-help-h1-face))
  (lyskom-insert "\n\n"))

(defun lyskom-help-format-h2 (data)
  (lyskom-insert "\n")
  (lyskom-help-format-text-properties data '(face lyskom-help-h2-face))
  (lyskom-insert "\n\n"))

(defun lyskom-help-format-h3 (data)
  (lyskom-help-format-text-properties data '(face lyskom-help-h3-face))
  (lyskom-insert "\n"))

(defun lyskom-help-format-i (data)
  (lyskom-help-format-text-properties data '(face italic)))

(defun lyskom-help-format-b (data)
  (lyskom-help-format-text-properties data '(face bold)))

(defun lyskom-help-format-TEXT (data)
  (lyskom-insert (cdr data)))

(defun lyskom-help-format-list (data)
  (lyskom-insert "\n")
  (when (lyskom-help-data-get-attr 'header data)
    (lyskom-insert (lyskom-help-data-get-attr 'header data))
    (lyskom-insert "\n"))
  (lyskom-do-help-format (lyskom-help-data-get-data data))
  (lyskom-insert "\n"))

(defun lyskom-help-format-item (data)
  (lyskom-insert "  * ")
  (lyskom-do-help-format (lyskom-help-data-get-data data))
  (lyskom-insert "\n"))

(defun lyskom-help-format-keymap (data)
  (let ((this-command-keys nil))
    (lyskom-insert (lyskom-help t))))

(defun lyskom-help-call-function (data)
  (let ((fn (lyskom-help-data-get-attr 'function data)))
    (funcall (intern fn))))

(defun lyskom-help-format-refer (data)
  (let* ((id (intern (lyskom-help-data-get-attr 'id data)))
         (section (lyskom-help-get-section id))
         (string (or (lyskom-help-data-get-attr 'prompt section)
                     (format "%s" id))))
    (lyskom-format-insert 
     "%#2@%#1s"
     string
     `(face underline
            mouse-face highlight
            lyskom-button t
            lyskom-button-text ""
            lyskom-button-type func
            lyskom-buffer ,(current-buffer)
            lyskom-button-arg (kom-help (,string . ,id)))))

)


(defun lyskom-help-format-cref (data)
  (let* ((command (intern (lyskom-help-data-get-attr 'id data)))
         (command-name (lyskom-get-string command 'lyskom-command))
         (keys (delq nil
                     (mapcar (lambda (x)
                               (if (and (arrayp x)
                                        (eq (elt x 0) 'menu-bar))
                                   nil
                                 x))
                             (where-is-internal command))))
         (heading (lyskom-format "%#1@%[%#2s%]%#3?b%[ (%#3s)%]%[%]"
                                 '(face italic)
                                 command-name
                                 (and keys 
                                      (mapconcat 'key-description
                                                 keys
                                                 "; ")))))
    (lyskom-insert heading)))

(defun lyskom-help-format-inline (data)
  (let* ((id (intern (lyskom-help-data-get-attr 'id data)))
         (section (lyskom-help-get-section id)))
    (cond (section (lyskom-do-help-format section))
          (t (lyskom-insert (format "[%S]" id))))))

(defun lyskom-help-format-p (data)
  (let ((start (point-max-marker)))
    (set-marker-insertion-type start nil)
    (lyskom-traverse el (lyskom-help-data-get-data data)
      (lyskom-do-help-format el))
      (save-excursion
        (save-restriction
          (narrow-to-region start (point-max))
          (goto-char (point-min))
          (while (re-search-forward "^\\s-+" nil t)
            (replace-match "" nil nil))
          (lyskom-fill-region (point-min) (point-max))))
      (lyskom-insert "\n\n")
      (set-marker start nil)))

(defvar lyskom-swedish-bindings-reverse)
(defun lyskom-help-on-command (&rest args)
  (let* ((cmd (lyskom-read-extended-command))
         (specials (lyskom-menu-guess-shortcuts (current-local-map)))
         (shortcut nil))
    (when cmd
      (if (assq cmd specials)
        (unless (lyskom-traverse key (cdr (assq cmd specials))
                  (unless (condition-case nil
                              (eq (lookup-key (current-local-map) key) cmd)
                            (error nil))
                    (lyskom-traverse-break t)))
          (setq shortcut
                (mapconcat 
                 (lambda (key)
                   (if (assq key lyskom-swedish-bindings-reverse)
                       (symbol-name 
                        (cdr (assq key lyskom-swedish-bindings-reverse)))
                     (single-key-description key)))
                 (car (cdr (assq cmd specials)))
                 " ")))
        (setq shortcut (delq nil (mapcar (lambda (kd) (unless (symbolp (elt kd 0)) kd))
                                         (where-is-internal cmd)))))
      (lyskom-format-insert "\n%#1C%#2?b%[ [%#2s]%]%[%]\n\n%#3s\n\n" 
                            cmd
                            (if (stringp shortcut)
                                shortcut
                              (mapconcat
                               (lambda (x) 
                                 (format "`%s'" (key-description x)))
                               shortcut ", "))
                            (documentation cmd)))))

;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: swedish-strings.el
;;;;
;;;; This file contains all strings in the LysKOM elisp client.
;;;; Language: Swedish.
;;;;
;;;; ================================================================
;;;;

(require 'lyskom-vars "vars")
(require 'lyskom-language "language")


;;; ================================================================
;;; The language definition

(lyskom-define-language 'sv
                        'iso-8859-1
                        "Svenska"
                        )


;;; ================================================================
;;; lyskom-edit-mode-map

(defvar lyskom-sv-edit-mode-map nil)
(lyskom-language-keymap lyskom-edit-mode-map sv lyskom-sv-edit-mode-map)

;;; Set the keymap for lyskom-edit-mode

(defvar lyskom-sv-edit-prefix)
(defvar lyskom-sv-edit-review-prefix)
(defvar lyskom-sv-edit-insert-prefix)
(defvar lyskom-sv-edit-aux-prefix)
(defvar lyskom-sv-edit-add-prefix)

(if lyskom-sv-edit-mode-map
    nil
  (setq lyskom-sv-edit-mode-map (make-sparse-keymap))
  (define-prefix-command 'lyskom-sv-edit-prefix)
  (define-prefix-command 'lyskom-sv-edit-review-prefix)
  (define-prefix-command 'lyskom-sv-edit-insert-prefix)
  (define-prefix-command 'lyskom-sv-edit-aux-prefix)
  (define-prefix-command 'lyskom-sv-edit-add-prefix)
  (define-key lyskom-sv-edit-mode-map (kbd "*")     'kom-button-press-or-self-insert-command)
  (define-key lyskom-sv-edit-mode-map (kbd "=")     'kom-menu-button-press-or-self-insert-command)
  (define-key lyskom-sv-edit-mode-map (kbd "TAB")   'kom-edit-next-button-or-self-insert)
  (define-key lyskom-sv-edit-mode-map (kbd "M-TAB") 'kom-edit-prev-button)
  (define-key lyskom-sv-edit-mode-map (kbd "<S-tab>") 'kom-edit-prev-button)
  (define-key lyskom-sv-edit-mode-map (kbd "C-c")   'lyskom-sv-edit-prefix)
  (define-key lyskom-sv-edit-mode-map (kbd (lyskom-keys (lyskom-xemacs-or-gnu 'button2 'button2up))) 'kom-button-click-or-yank)
  (define-key lyskom-sv-edit-mode-map (kbd (lyskom-keys (lyskom-xemacs-or-gnu 'button2up 'button2))) 'kom-mouse-null)
  (define-key lyskom-sv-edit-mode-map (kbd (lyskom-keys 'button3)) 'kom-popup-menu)
  (define-key lyskom-sv-edit-mode-map (kbd (lyskom-keys 'button3up))     'kom-mouse-null)

  (define-key lyskom-sv-edit-prefix (kbd "C-x")     'lyskom-sv-edit-aux-prefix)
  (define-key lyskom-sv-edit-prefix (kbd "C-a")     'lyskom-sv-edit-add-prefix)
  (define-key lyskom-sv-edit-prefix (kbd "C-i")     'lyskom-sv-edit-insert-prefix)
  (define-key lyskom-sv-edit-prefix (kbd "TAB")     'lyskom-sv-edit-insert-prefix)

  (lyskom-try-define-key lyskom-sv-edit-prefix (kbd "C-]")       'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix (kbd "C-}")       'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix (kbd "C-å")       'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix (kbd "C-Å")       'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [(control aring)] 'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [(control Aring)] 'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix "\205"            'lyskom-sv-edit-review-prefix) ; For windows and Emacs 19

  (lyskom-try-define-key lyskom-sv-edit-prefix (kbd "å") 'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix (kbd "}") 'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix (kbd "]") 'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [aring]   'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [Aring]   'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [å]       'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [Å]       'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [229]     'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [197]     'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [2277]    'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [2245]    'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [3909]    'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [3941]    'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [195 165] 'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [195 165] 'lyskom-sv-edit-review-prefix)
  (lyskom-try-define-key lyskom-sv-edit-prefix [(Ã)]     (let ((k (make-sparse-keymap)))
							   (define-key k (vector (intern "\205")) 'lyskom-sv-edit-review-prefix)
							   (define-key k [¥] 'lyskom-sv-edit-review-prefix)
							   k))

  (define-key lyskom-sv-edit-prefix (kbd "?")       'lyskom-help)
  (define-key lyskom-sv-edit-prefix (kbd "*")       'kom-button-press)
  (define-key lyskom-sv-edit-prefix (kbd "=")       'kom-menu-button-press)
  (define-key lyskom-sv-edit-prefix (kbd "C-c")     'kom-edit-send)
  (define-key lyskom-sv-edit-prefix (kbd "C-s")     'kom-edit-ispell-message)
  (define-key lyskom-sv-edit-prefix (kbd "C-k")     'kom-edit-quit)
  (define-key lyskom-sv-edit-prefix (kbd "C-y")     'kom-edit-insert-commented)
  (define-key lyskom-sv-edit-prefix (kbd "C-b")     'kom-edit-insert-buglist)

  (define-key lyskom-sv-edit-insert-prefix (kbd "?")   'lyskom-help)
  (define-key lyskom-sv-edit-insert-prefix (kbd "C-k") 'kom-edit-insert-commented)
  (define-key lyskom-sv-edit-insert-prefix (kbd "C-y")     'kom-edit-insert-commented)
  (define-key lyskom-sv-edit-insert-prefix (kbd "1")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "2")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "3")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "4")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "5")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "6")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "7")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "8")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "9")   'kom-edit-insert-digit-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "SPC") 'kom-edit-insert-text)
  (define-key lyskom-sv-edit-insert-prefix (kbd "C-l") 'kom-edit-insert-link)
  (define-key lyskom-sv-edit-insert-prefix (kbd "C-i") 'kom-edit-insert-last-info-nodename)

  (define-key lyskom-sv-edit-add-prefix (kbd "C-m") 'kom-edit-add-recipient)
  (define-key lyskom-sv-edit-add-prefix (kbd "<RET>") 'kom-edit-add-recipient)
  (define-key lyskom-sv-edit-add-prefix (kbd "C-e") 'kom-edit-add-copy)
  (define-key lyskom-sv-edit-add-prefix (kbd "C-b") 'kom-edit-add-bcc)
  (define-key lyskom-sv-edit-add-prefix (kbd "C-f") 'kom-edit-move-text)
  (define-key lyskom-sv-edit-add-prefix (kbd "C-j") 'kom-edit-add-recipient)
  (define-key lyskom-sv-edit-add-prefix (kbd "<LFD>") 'kom-edit-add-recipient)
  (define-key lyskom-sv-edit-add-prefix (kbd "C-k") 'kom-edit-add-comment)
  (define-key lyskom-sv-edit-add-prefix (kbd "C-x") 'kom-edit-add-cross-reference)
  (define-key lyskom-sv-edit-add-prefix (kbd "?")   'lyskom-help))

  (define-key lyskom-sv-edit-aux-prefix (kbd "C-p") 'kom-edit-add-personal-comments)
  (define-key lyskom-sv-edit-aux-prefix (kbd "C-n") 'kom-edit-add-no-comments)
  (define-key lyskom-sv-edit-aux-prefix (kbd "C-l") 'kom-edit-add-world-readable)
  (define-key lyskom-sv-edit-aux-prefix (kbd "C-b") 'kom-edit-add-read-confirm-request)

  (define-key lyskom-sv-edit-review-prefix (kbd "?")     'lyskom-help)
  (define-key lyskom-sv-edit-review-prefix (kbd "C-k")   'kom-edit-show-commented)
  (define-key lyskom-sv-edit-review-prefix (kbd "k")     'kom-edit-show-commented)



(defconst lyskom-strings-missing '())



;;; The alist formely known as lyskom-strings
(lyskom-language-strings local lyskom-message sv
  '(
    ;; From vars.el: 
    ;; From komtypes.el: nil
    ;; From clienttypes.el: nil
    ;; From startup.el:
    (server-q . "LysKOM-server? ")
    (try-connect . "LysKOM elisp-klient version %#1s.\nFörsöker koppla upp mot %#2s.\n")
    (too-old-server . "Servern är för gammal för den här versionen av klienten.")
    (connection-done . "Uppkopplingen klar. Serverns versionsnummer är %#1s.\n\n")
    (what-is-your-name . "Vad heter du? ")
    (password . "Lösenord? ")
    (wrong-password . "Fel lösen.\n")
    (wrong-password-help . "
Du kan skicka e-mail till någon av nedanstående adresser för att få
ett nytt lösenord eller för att få din gamla person raderad så att du
kan skapa en ny:\n")
    (wrong-password-email . "* %#1t\n")
    (are-logged-in . "Du är nu inloggad. Vänta ett tag.\n")
    (you-have-motd . "\nDu har en lapp på dörren:\n\n")
    (server-has-motd . "Servern har ett loginmeddelande:\n")
    (lyskom-motd-was-garbed . "\nLoginmeddelandet finns inte!
Det meddelande som ska visas efter inloggning har försvunnit.
Kontakta LysKOM-administratören.\n")
    (first-greeting . "%#1s
Det tycks vara första gången du använder LysKOM. Välkommen!
Kontrollera att du stavat ditt namn rätt. Använd gärna ditt fullständiga
namn och organisation, t ex \"Eskil Block, FOA\". Om du stavat ditt namn
fel, eller vill ändra ditt namn, svara nej på frågan nedan.

Observera att all information än så länge sparas så att vem som helst
kan läsa den. Dock är lösenordet krypterat.

Om du är osäker på hur man använder LysKOM kan du hämta en manual via
anonym ftp från ftp.lysator.liu.se. Fråga din systemadministratör om
du är osäker. Du kan även skriva \"?\" för kommandot \"Hjälp\".
")

    (is-name-correct . "Är namnet %#1s korrekt? ")
    (personal-password . "Ange ett personligt lösen: ")
    (repeat-password . "Repetera för kontroll: ")
    (repeat-failure . "Du angav inte samma lösenord bägge gångerna.\n")

    (could-not-create-you .  "Det gick inte att skapa personen.\n")
    (presentation-subject . "%#1s")
    (presentation-form . "\
Namn:    
Adress:  
Stad:    
Telefon: 
E-post:  
WWW:     

Annat:   ")
    (presentation-help . "Du skriver just  nu din presentation.\n")
    (not-present-anywhere . "Ej närvarande i något möte.")
    (in-secret-conference . "Hemligt möte (%#1d).")
    (start-new-session-same-server
     . "Du kör redan mot den servern. Vill du starta en ny session? ")
    (new-session-in-buffer . "\n\n---- Ny session startad %s ----\n\n")
    (warning-about-uncompiled-client . "
OBS: LysKOM-klienten är inte kompilerad. För att öka prestanda bör du köra en
kompilerad version av klienten. Läs i den medföljande README-filen hur man
gör det.
")

    ;; From internal.el:
    (shaky-tcp . "Nu når jag inte servern. TCP/IP-förbindelsen är skakig%#1s")
    (retrying-tcp . "Jag försöker igen.")

    ;; From parse.el:
    (protocol-error . "protokollfel: %s")

    ;; From services.el:
    (interrupted . "Kommandot avbrutet\n")

    ;; From cache.el:
    ;; No entries.

    ;; From commands1.el:
    (appreciation . "Du är mycket vacker och mycket klok. Många älskar dig både till kropp
och till själ. Du kommer att överösas med rikedom och få stor lycka i ditt
liv. Var glad att just du är du. Det har du all anledning att vara.
Fantomen önskar dig en bra dag.\n\n")
    (abuse . "Du är mycket ful och mycket dum. Många hatar dig både till kropp
och till själ. Du kommer att överösas med sjukdomar och inte få någon som
helst lycka i ditt liv. Du borde verkligen ta dig samman och göra något av
ditt liv även om det inte är mycket att starta med.
Guran vill helst sätta en giftpil i dig.\n\n")

    (what-conf-to-delete . "Vilket möte/person vill du utplåna: ")
    (what-conf-to-change . "Vilket möte vill du ändra: ")
    (confirm-delete-pers-or-conf . "Ta bort %#1s %#2s? ")
    (the-pers . "personen")
    (the-conf . "mötet")
    (deletion-not-confirmed . "Utplåningen avbruten\n")
    (somebody-else-deleted-that-conf . "Någon annan tog precis bort mötet.\n")
    (conf-is-deleted . "Ok, nu är %#1s utplånad.\n")
    (you-could-not-delete . "%#1M kunde inte utplånas av dig.\n")
    (you-have-deleted-yourself . "Du har utplånat dig själv.\n")

    (what-text-to-delete . "Vilket inlägg skall tas bort? ")
    (delete-marked-text . "Inlägget är %#1s. Ta bort ändå? ")
    (delete-marked-by-you . "markerat av dig")
    (delete-marked-by-you-and-others . "markerad av dig och %#1?d%[någon annan%]%[%#1d andra%]")
    (delete-marked-by-several . "markerat av %#1d person%#1?d%[%]%[er%]")
    (delete-commented-text-help . "\
Inlägget du håller på att radera har kommentarer. Om du tar bort inlägget
så kommer dessa kommentarer att förlora sitt sammanhang. Detta irriterar
många läsare. Överväg att flytta inlägget till ett annat möte istället
för att radera det.")
    (delete-commented-text . "Inlägget har kommentarer. Radera ändå? ")
    (really-delete-commented-text . "Att radera kommenterade inlägg irriterar många läsare. Radera ändå?")
    (deleting-text . "Radering av text %#1:n...")

    (presentation-for-whom . "Se presentation för vilket möte/person? ")
    (unread-presentation-for-whom . "Oläsmarkera presentation för vilket möte/person? ")
    (text-to-see-author-of . "Se presentation för vilket inläggs författare? ")
    (text-to-unread-author-of . "Oläsmarkera presentation för vilket inläggs författare? ")
    (somebody-deleted-that-conf . "Någon tog precis bort mötet.\n")
    (review-presentation-of . "Återse presentation av %#1M.\n")
    (has-no-presentation . "%#1:M har ingen presentation.\n")

    (have-to-read . "Du måste läsa ett inlägg först.\n")

    (no-comment-to . "Det finns inget kommenterat inlägg att titta på.\n")
    (no-text-at-point . "Det finns inget inlägg vid markören.\n")
    (what-ancestor . "Vilken av de kommenterade texterna önskas? ")

    (who-letter-to . "Vem vill du skicka brev till? ")
    (who-send-text-to . "Vart vill du skicka inlägget? ")
    (has-motd . "%#1P har en lapp på dörren:\n\n")
    (motd-persist-q . "Vill du fortfarande skriva texten? ")

    (who-to-add . "Vem vill du addera? ")
    (where-to-add . "Vilket möte skall han/hon adderas till? ")
    (where-to-add-self . "Vilket möte vill du bli medlem i? ")
    (priority-q . "Prioritet på ditt medlemskap i mötet? (0 (låg) - 255 (hög)) ")
    (other-priority-q . "Prioritet på den adderades medlemskap i mötet? (0 (låg) - 255 (hög)) ")
    (done . "klart.\n")
    (cancelled . "avbrutet.\n")
    (nope . "det gick inte.\n")
    (add-already-member . "%#2P är redan medlem i %#1M.\n")
    (you-already-member . "Du är redan medlem i %#1M.\n")

    (cant-find-supervisor . "Hittar inte organisatören för %#1M.\n")
    (is-read-protected-contact-supervisor . "%#1M är slutet.
Skicka ett brev till %#2P för medlemskap.\n")

    (conf-does-not-exist . "\nMötet finns inte.\n")

    (who-to-exclude . "Vem vill du utesluta? ")
    (where-from-exclude . "Vilket möte skall han/hon uteslutas ifrån? ")

    (leave-what-conf . "Vilket möte vill du gå ur? ")

    (error-fetching-person . "Fel i personhämtningen.\n")
    (error-fetching-conf . "Fel i möteshämtningen.\n")

    (name-of-conf . "Vad ska mötet heta? ")
    (anyone-member . "Får vem som helst bli medlem? ")
    (secret-conf . "Hemligt möte? ")
    (comments-allowed . "Får man skriva kommentarer? ")
    (anonymous-allowed . "Får man skriva anonyma texter? ")
    (secret-members-allowed . "Får man gå med som hemlig medlem? ")
    (what-comment-no . "Kommentera text nummer: ")
    (what-footnote-no . "Fotnotera text nummer: ")
    (what-private-no . "Personligt svar till text nummer: ")
    (quit-in-spite-of-unsent . "Vill du avsluta sessionen trots oinskickat inlägg? ")
    (really-quit . "Vill du verkligen avsluta sessionen? ")
    (session-ended . "
*****************************
LysKOM-sessionen är avslutad.
*****************************\n")
    (session-auto-ended . "
===========================================================
Kopplar ned från LysKOM eftersom LysKOM är fullt och
du har läst klart allting. Kom tillbaks senare.
===========================================================\n\n")
    (session-ended-long . "
*******************************************************************************
LysKOM-sessionen är avslutad.
*******************************************************************************
")
    (session-auto-ended-long . "
===============================================================================
Kopplar ned från LysKOM eftersom LysKOM är fullt och
du har läst klart allting. Kom tillbaks senare.
===============================================================================
\n")
    (what-to-change-pres-you . "Vilket möte/person vill du ändra presentationen för: ")
    (what-to-change-faq-you . "Vilket möte vill du ändra FAQ för: ")
    (who-to-put-motd-for . "Vilket möte/person vill du sätta lapp på dörr för: ")

    (what-to-set-pres-you . "Vilket möte/person vill du sätta presentationen för: ")
    (what-text-to-set-as-pres-no . "Vilket inlägg vill du ska vara den nya presentationen: ")
    (what-to-set-motd-you . "Vilket möte/person vill du sätta lapp på dörr för: ")
    (what-text-to-set-as-motd-no . "Vilket inlägg vill du ska vara den nya lappen: ")
    (conf-already-has-pres . "Mötet/personen har redan en presentation. Fortsätt ändå? ")
    (conf-already-has-motd . "Mötet/personen har redan en lapp på dörren. Fortsätt ändå? ")
    (setting-conf-pres . "Sätter presentation för %#1M till text %#2n...")
    (setting-conf-motd . "Sätter lapp på dörren för %#1M till text %#2n...")

    (who-to-remove-pres-for . "Vilket möte/person vill du ta bort presentationen från: ")
    (removing-pres-for-conf . "Tar bort presentation (text %#2n) från %#1M...")

    (cant-get-conf-stat . "Kan ej hämta mötesstatus för mötet.\n")
    (go-to-conf-p . "Gå till möte: ")
    (want-become-member . "Vill du bli medlem? ")
    (no-ok . "Nehej.\n")

    (who-to-remove-motd-for . "Vilket möte/person vill du ta bort lapp på dörr för: ")

    (conf-all-read . "inga olästa")
    (no-in-conf . "Du är inte närvarande i något möte.\n")

    (search-for-pers . "Ange sökvillkor (RETURN för alla personer): ")
    (search-for-conf . "Ange sökvillkor (RETURN för alla möten): ")
    (search-re . "Ange sökuttryck (reguljärt uttryck): ")
    (search-re-for-what . "Vad vill du söka? ")
    (search-re-persons . "pSök personer")
    (search-re-confs . "mSök möten")
    (search-re-all . "bSök alla")

    (no-matching-confs . "Inga möten uppfyller sökvillkoret \"%#1s\".\n")
    (no-matching-perss . "Inga personer uppfyller sökvillkoret \"%#1s\".\n")
    (no-matching-anys . "Inga personer eller möten uppfyller uttrycket \"%#1s\".\n")
    (no-confs-exist . "Det finns inga möten i databasen.\n")
    (no-pers-confs-exist . "Det finns inga möten eller personer i databasen.\n")
    (list-confs-created-by . "Lista ägda möten för: ")
    (listing-confs-created-by . "Ägda möten för %#1P\n\
    S=Skapat, O=Organisatör, M=Brevlåda som supermöte; S=Slutet, H=Hemligt\n")
    (list-pers-confs-created-by . "Lista ägda möten (med brevlådor) för: ")
    (getting-all-confs . "Hämtar en lista av alla möten från servern...")
    (getting-all-pers-confs . "Hämtar en lista av alla personer och möten från servern...")
    (getting-all-confs-done . "Hämtar en lista av alla möten från servern...klart")
    (getting-all-pers-confs-done . "Hämtar en lista av alla personer och möten från servern...klart")
    (finding-created-confs . "Söker ägda möten (%#1d av %#2d klart)")
    (finding-created-pers-confs . "Söker ägda möten (med brevlådor) (%#1d av %#2d klart)")
    (no-created-confs . "%#1P är inte skapare, organisatör eller supermöte för något möte.\n")

    (name-to-be-changed . "Ange det namn som skall ändras: ")
    (no-such-conf-or-pers . "Mötet eller personen finns ej.\n")
    (new-name . "Nytt namn: ")
    (new-paren . "Ny parentes: ")
    (too-many-parens-in-name . "Namnet innehåller mer än en parentes. Använd \"Ändra namn\" istället.\n")
    (no-paren-in-name . "Namnet innehåller ingen parentes.\n")
    (who-to-change-supervisor-for . "Vem vill du ändra organisatör för? ")
    (new-supervisor . "Ny organisatör: ")
    (text-to-mark . "Vilket inlägg vill du markera? ")
    (text-to-unmark . "Vilket inlägg vill du avmarkera? ")
    (what-mark . "Vilken typ av markering vill du sätta (namn eller 0-255)? ")
    (erroneous-mark . "Felaktig markeringstyp.\n")
    (want-to-create-symbolic-mark . "Markeringstypen \"%#1s\" finns inte. Vill du skapa den? ")
    (creating-symbolic-mark-type . "Skapar markeringstypen \"%#1s\" (%#2d).\n")
    (no-mark-types-left . "Ledsen, det finns inga lediga markeringstyper. Rensa bland de gamla först.")
    (unmarking-textno . "Avmarkering av text %#1n...")
    (marking-textno . "Markering av text %#1n med markering %#2?b%[\"%#2s\" (%#3d)%]%[%#3d%]...")
    (list-which-mark . "Lista vilken markeringstyp (namn eller 0-255, RET för alla)? ")

    (new-passwd-again . "Mata in det nya lösenordet igen för kontroll: ")
    (what-mark-to-unread . "Oläsmarkera vilken markeringstyp (namn eller 0-255, RET för alla)? ")
    (what-mark-to-view . "Återse vilken markeringstyp (namn eller 0-255, RET för alla)? ")
    (whos-passwd . "Vem vill du ändra lösenord för? ")
    (old-passwd . "Mata in ditt nuvarande lösenord: ")
    (new-passwd . "Mata in det nya lösenordet: ")
    (changing-passwd . "Ändrar lösenordet...")
    (retype-dont-match . "Lösenorden är inte samma. Gör om.\n")
    (palindrome . " (en palindrom!)")
    (lyskom-name . "Användare")
    (is-in-conf . "Närvarande i möte")
    (from-machine . "Kör från")
    (is-doing . "Gör")
    (connection-time . "Loggade in")
    (active-last . "Aktiv senast")
    (active . "Aktiv")
    (lyskom-client . "Klient")
    (move-roots-q . "Inlägg %#1n är en fotnot/bilaga. Flytta %#2n%#3?b%[ mfl.%]%[%] istället? ")
    (add-roots-q . "Inlägg %#1n är en fotnot/bilaga. Addera till %#2n%#3?b%[ mfl.%]%[%] istället? ")
    (sub-roots-q . "Inlägg %#1n är en fotnot/bilaga. Subtrahera från %#2n%#3?b%[ mfl.%]%[%] istället? ")
    (text-to-add-recipient . "Vilket inlägg vill du addera mottagare till:")
    (text-to-add-copy . "Vilket inlägg vill du addera en extra kopia till:")
    (text-to-add-bcc . "Vilket inlägg vill du skicka som dold kopia:")
    (text-to-delete-recipient . "Vilket inlägg vill du subtrahera mottagare från:")
    (text-to-move . "Vilket inlägg vill du flytta:")
    (text-tree-to-move . "Vilket inlägg är rot till trädet du vill flytta:")
    (text-to-add-comment-to . "Vilket inlägg vill du addera en kommentar till:")
    (text-to-delete-comment-from . "Vilket inlägg vill du subtrahera en kommentar från:")
    (text-to-add-footnote-to . "Vilket inlägg vill du addera en fotnot till:")
    (text-to-delete-footnote-from . "Vilket inlägg vill du subtrahera en fotnot från:")
    (comment-to-move-q . "Vilken kommentar vill du flytta:")
    (comment-move-from-q . "Från vilken text vill du flytta %#1n:")
    (comment-move-to-q . "Till vilken text vill du flytta %#1n:")
    (text-is-not-a-comment . "Inlägg %#1n är inte en kommentar till något inlägg\n")
    (cant-move-footnotes . "Kan inte flytta fotnoter.\n")
    (moving-comment . "Flyttar %#1n från %#2n till %#3n...\n")

    (text-to-add-cross-reference-to . "Vilket inlägg vill du addera en referens till:")
    (text-has-no-recipients-r . "Inlägg %#1n har inga mottagare\n")
    (cant-move-from-to-same . "Mötet du flyttar till kan inte vara samma som du flyttar från.\n")

    (where-on-list-q . "Placering på listan? (0-%#1d) ")
    (accepting-invitation . "Accepterar inbjudan till %#2M%#3?b%[ %#3s%]%[%]...")
    (member-in-conf . "Bli medlem i %#2M%#3?b%[ %#3s%]%[%]...")
    (member-in-conf-with-low-priority . "%#1M har lägre prioritet än din läsnivå och kommer därför inte att visas.\n")
    (member-in-conf-with-unread-date . "med olästa från %#3d %#2s %#1d")
    (member-in-conf-with-unread . "med %#1d olästa")
    (add-member-in . "Addera %#1P som medlem i %#2M...")
    (change-priority-for-q . "Ändra prioritet för möte: ")
    (cant-change-priority-of-passive . "Kan inte ändra prioritet på passiva medlemskap.\n")
    (change-priority-for . "Ändra prioritet på %#2M...")
    (unsubscribe-to . "Utträda ur %#1M...")
    (unsubscribe-to-2 . "Utträda ur %#1M%#2s...")
    (confirm-each-join . "Vill du bekräfta medlemskap i enskila möten? ")
    (confirm-each-msg . "Vill du bekräfta mottagande av gruppmeddelande i enskila möten? ")
    (receive-each-msg . "Vill du ta emot gruppmeddelande i alla möten? ")
    (no-confirm-each-sure . "Är du helt säker på att du vill gå med i %#1d möten automatiskt? ")
    (confirm-join . "Gå med i %#1M? ")
    (getting-all-confs . "Hämtar en lista med alla möten...")
    (getting-all-confs-progress . "Hämtar en lista med alla möten (%#1d/%#2d)...")
    (getting-all-confs-done . "Hämtar en lista med alla möten...klart")
    (unsub-all-skipping-letterbox . "nix\nMötet är din brevlåda.\n")
    (unsub-all-skipping-supervised . "nix\nDu är organisatör för mötet.\n")
    (unsub-secret-conf-q . "%#1M är hemligt. Vill du gå ur mötet ändå? ")
    (unsub-all-secret . "aGå ur alla hemliga möten")
    (unsub-closed-conf-q . "%#1M är slutet. Vill du gå ur mötet ändå? ")
    (unsub-all-closed . "aGå ur alla slutna möten")
    (unsub-open-conf-q . "Vill du gå ur %#1M? ")
    (unsub-all-open . "aGå ur alla öppna möten")
    (abc-yes . "jJa")
    (abc-no . "nNej")
    (abc-yes-all . "aJa till alla")
    (abc-no-all . "xNej till alla")

    (move-tree-move . "fFlytta")
    (move-tree-sub  . "sSubtrahera")
    (move-tree-add  . "aAddera")
    (move-tree-ign  . "iIngenting")
    (move-tree-jump . "hHoppa")
    (move-tree-quit . "qAvbryt")

    (move-tree-add-rcpt-type . "Addera som: ")
    (move-tree-rcpt . "mMottagare")
    (move-tree-cc . "eExtra kopia")
    (move-tree-bcc . "dDold kopia")

    (move-tree-leave-cc-q . "Lämna ursprungsmottagare som extrakopiemottagare? ")

    (exclude-from . "Utesluta %#1P från %#2M...")

    (unsubscribe-failed . "\nDet gick inte. %#1P var kanske aldrig medlem i %#2M?\n")
    (activate-mship . "Bli aktiv medlem i %#1M...")
    (activate-mship-done . "Du är nu åter aktiv medlem i %#1M.\n")
    (passivate-done . "Du är nu passiv medlem i %#1M.
Utträd ur mötet en gång till för gå ur helt.\n")

    (You . "Du")
    (could-not-create-conf . "Mötet \"%#1s\" kunde ej skapas.\n")
    (created-conf-no-name . "Möte nummer %[%#3@%#1:m %#2:M%], skapat.\n")
    (cant-read-textno . "Du får inte läsa text %#1:n.\n")

    (not-supervisor-for . "Du är inte organisatör för %#1M.\n")
    (not-supervisor-for-server . "Du är inte administratör för LysKOM-servern.\n")
    (go-to-conf . "Gå till %#1M.\n")
    (cant-go-to-his-mailbox . "Du får inte gå till %#1Ms brevlåda.\n")
    (not-member-of-conf . "Du är inte medlem i %#1M.\n")
    (about-to-change-name-from . "%#1M\n")
    (change-name-done . "Klart. Nytt namn: %[%#2@%#1:M%].\n")
    (change-name-nope . "Det gick inte bra att ändra till %#1s.\nFelkod %#3d. %#2s.\n")
    (change-supervisor-from-to . "Ändra organisatör för %#1M till %#2P...")
    (change-supervisor-nope . 
			    "\nDet gick inte. Kanske du inte får ändra organisatör för %#1M?\n")
    
    (no-marked-texts . "Du har inga markerade inlägg.\n")
    (no-marked-texts-mark . 
			  "Du har inga markerade inlägg med markeringstypen \"%#1s\".\n")

;;; For later
;    (northward . "norrut")
;    (southward . "söderut")
;    (permanent-sundown . "Solen kommer inte att gå upp. Flytta %#1s!")
;    (permanent-sunup . "Solen kommer inte att gå ned. Flytta %#1s!")
;    (sunup-soon . "Solen går snart upp")
;    (sundown-recently . "Solen gick nyligen ned")
;    (after-sunset . "Solen har gått ned")
;    (before-sunup . "Solen har inte gått upp ännu")
;    (sun-is-up . "Solen är uppe")
;;;
    (weekdays . ["söndag" "måndag" "tisdag" "onsdag" "torsdag"
		 "fredag" "lördag" "söndag"])
    (weekdays-short . ["sön" "mån" "tis" "ons" "tor" "fre" "lör" "sön"])
    (time-is . "Det är %#1s%#2s (enligt servern).")
    (time-is-week . "Det är %#1s%#2s, vecka %#3d (enligt servern).")

    (xmaseve . "\nJulafton! Har du öppnat dina julklappar än?")
    (xmasday . "Juldagen.\nDu har väl varit i julottan?")
    (newyearday . "Nyårsdagen. Gott nytt %#1d!")
    (newyearevelate . "Mindre än en timme kvar av %#1d...")
    (newyeareve . "Nyårsafton.")
    (cgdag . "Valborgsmässoafton.\nKonungens födelsedag.")
    (sixjune . "Sveriges nationaldag och svenska flaggans dag.")
    (holdnose . "Håll för näsan...")
    (lysbday . "
På denna dag, år 1973, grundades Lysator, och det var en stor dag
i svensk datorhistoria. Läs mer på http://www.lysator.liu.se/history/")
    (13dayxmas . "Trettondedag jul (\"Trettondagen\").")
    (20dayxmas . "Tjugondedag jul (\"Tjugondag Knut\").")
    (kyndeldag . "Kyndelsmässodagen.")
    (skottdag . "Skottdagen.")
    (intwomday . "Internationella kvinnodagen.")
    (mariebdag . "Marie bebådelsedag.\nFram med våffeljärnet!")
    (johannesddag . "Johannes Döparens dag.")
    (fnday . "FN-dagen.")
    (allhelgonadag . "Allhelgonadagen.")
    (varnlosdag . "Värnlösa barns dag.")
    (luciaday . "\nLuciadagen! Glöm inte att lussa för dina kollegor.")
    (towelday . "\nTowel Day! Glöm inte att ta med handduken.")

    (total-users . "    Sammanlagt %#1d användare (%#2s).\n")
    (total-users-sans-date . "    Sammanlagt %#1d användare\n")
    (total-visible-users . "    Sammanlagt %#1d synlig%#1?d%[%]%[a%] användare (%#2s).\n")
    (total-active-users . "    Sammanlagt %#1d aktiv%#1?d%[%]%[a%] användare (%#2s).\n")
    (total-visible-active-users
     . "    Sammanlagt %#1d synlig%#1?d%[%]%[a%] aktiv%#1?d%[%]%[a%] användare (%#2s).\n")
    (client-statistics . "    Klienternas fördelning:\n")
    (client-statistics-line . "    %4#2d %#1s%#3?d%[%]%[ [%#3d varianter]%]")
    (who-to-add-q . "Vilket möte/person vill du addera som mottagare till text %#1n? ")
    (who-to-add-copy-q . "Vilket möte/person vill du addera som kopiemottagare till text %#1n? ")
    (who-to-add-bcc-q . "Vilket möte/person vill du addera som mottagare av en dold kopia till text %#1n? ")
    (add-footnotes-too-q . "Försök addera mottagare till fotnoter också? ")
    (add-attachments-too-q . "Försök addera mottagare till bilagor också? ")

    (really-add-as-recpt-q . "Skall framtida kommentarer till inlägget också skickas till %#1M? ") 
    (who-to-sub-q . "Vilket möte/person vill du subtrahera som mottagare till text %#1n? ")
    (sub-footnotes-too-q . "Försök subtrahera mottagare från fotnoter också? ")
    (sub-attachments-too-q . "Försök subtrahera mottagare från bilagor också? ")

    (who-to-move-from-q . "Från vilket möte vill du flytta text %#1n? ")
    (who-to-move-to-q . "Vart vill du flytta text %#1n? ")
    (edit-who-to-move-to-q . "Vart vill du flytta texten? ")
    (who-to-move-to-or-sub-q . "Vart vill du flytta texten (tomt för att bara subtrahera)? ")
    (move-footnotes-too-q . "Försök flytta fotnoter också? ")
    (move-attachments-too-q . "Försök flytta bilagor också? ")
    (move-text-not-recipient . "har ej rätt mottagare.\n")

    (adding-name-as-recipient . "Adderar %#1M som mottagare till text %#2n...")
    (adding-name-as-copy . "Adderar %#1M som kopiemottagare till text %#2n...")
    (adding-name-as-bcc . "Adderar %#1M som dold kopiemottagare till text %#2n...")
    (remove-name-as-recipient . "Subtraherar %#1M som mottagare från text %#2n...")


    (unknown-recpt-text . "okänd mottagare")
    (cc-recpt-text . "kopiemottagare")
    (bcc-recpt-text . "dold kopiemottagare")
    (recpt-text . "mottagare")
    (add-conf-as-rcpt-q . "Vill du göra %#1M %#2s till text %#3n? ")
    (del-conf-as-rcpt-q . "Vill ta bort %#1M som %#2s till text %#3n? ")
    (change-conf-as-rcpt-q . "Vill du ändra %#2s %#1M till en %#3s till text %#4n? ")
    (move-conf-as-rcpt-q . "Vill du flytta %#1n från %#2M till %#3M? ")

    (adding-cross-reference . "Adderar referens...")

    (error-recipient-limit . "Text %#1n har för många mottagare.\n")
    (error-already-recipient . "%#2M är redan mottagare till inlägg %#1n.\n")
    (error-permission-denied-add-recpt . "Bara författaren till %#1n eller organisatören för %#2M kan ändra mottagartyp.\n")
    (error-permission-denied-sub-recpt . "Bara författaren till %#1n eller organisatören för %#2M kan subtrahera mottagare.\n")
    (error-access-denied-add-recpt . "Du får inte addera inlägg till %#2M.\n")
    (error-not-recipient . "%#2M är inte mottagare till inlägg %#1n.\n")

    (moving-tree-what-action-q . "Vad vill du göra? ")
    (moving-cant-read . "Det gick inte att inte flytta text %#1n (kan inte läsa texten).\n")
    (moving-name . "Flyttar text %#3n från %#1M till %#2M...")
    (moving-already-moved . "Inlägg %#1n har inte %#2M som mottagare.\nFlyttar inte inlägget eller dess kommentarsträd.\n")
    (text-to-add-q . "Vilket inlägg vill du addera som kommentar till %#1n? ")
    (text-to-remove-q . "Vilket inlägg vill du subtrahera som kommentar från %#1n? ")
    (text-to-add-footn-q . "Vilket inlägg vill du addera som fotnot till %#1n? ")
    (text-to-remove-footn-q . "Vilket inlägg vill du subtrahera som fotnot från %#1n? ")
    (add-comment-to . "Adderar text %#1n som kommentar till text %#2n...") ;
    (sub-comment-to . "Subtraherar text %#1n som kommentar till text %#2n...")
    (add-footnote-to . "Adderar text %#1n som fotnot till text %#2n...") ;
    (sub-footnote-to . "Subtraherar text %#1n som fotnot till text %#2n...")
    (comment-keep-recpt-p ."Ska %#1s vara mottagare? ")
    (comment-all-relevant-p . "Inlägget har flera mottagare. Är alla relevanta? ")
    (please-edit-recipients . "Ändra mottagarlistan och skicka in inlägget igen.")
    (checking-rcpt . "Kontrollerar mottagare...")
    (checking-rcpt-done . "Kontrollerar mottagare...klart")
    (checking-comments . "Kontrollerar kommenterade texter...")
    (checking-comments-done . "Kontrollerar kommenterade texter...klart")
    (please-check-commented-texts . "Återse de kommenterade texterna och deras kommentarer.")
    (have-unread-comment . "Skicka in trots olästa kommentarer till text %#1n? ")
    (duplicate-recipients . "Kan inte skicka in texten. En mottagare förekommer flera gånger (%#1M)")
    (no-recipients . "Kan inte skicka in texten. Inga mottagare har angivits.")

    (add-recipient-p . "Addera mottagare %#2?b%[ %#2M för att nå%]%[%] %#1P? ")
    (matching-regexp . "Möten/personer som matchar '%#1s'\n")
    (matching-regexp-perss . "Personer som matchar '%#1s'\n")
    (matching-regexp-confs . "Möten som matchar '%#1s'\n")

    (who-is-active-all . "Visar alla sessioner.\n")
    (who-is-active-last-minutes . "Visar alla sessioner som har varit aktiva de senaste %#1d minuterna.\n")
    (showing-invisibles . "Visar osynliga sessioner.\n")
    (null-who-info . "Det finns inga (aktiva) inloggade.\n")

    (no-other-lyskom-r . "Det finns inga fler aktiva LysKOM-sessioner.\n")
    (no-lyskom-session . "Det finns ingen aktiv LysKOM-session.")
    (no-other-unread-lyskom-r . "Hittar ingen annan LysKOM-session med olästa.\n")
    (no-unread-lyskom-r . "Hittar ingen aktiv LysKOM-session med olästa.\n")
    (no-unread-lyskom . "Hittar ingen aktiv LysKOM-session med olästa.")

    (who-is-on-in-what-conference . "Vilka i vilket möte: ")
    (who-is-present-in-what-conference . "Vilka närvarande i vilket möte: ")
    (who-is-friend . "Visar endast vänner.\n")
    (who-is-active-and-member . "Visar endast medlemmar i %#1M.\n")
    (who-is-active-and-present . "Visar endast medlemmar närvarande i %#1M.\n")

    (personal-comment-to-anonymous . "Det går inte att skriva ett personligt svar på ett anonymt inlägg.\n")

    ;; From commands2.el:

    (your-memberships . "Ditt medlemskap i olika KOM-möten:\n")
    (memberships-header . "Senast inne	   Prio	Olästa	Mötesnamn\n")
    (memberships-line . "%16#1s  %#2d\t%#3d\t%#4M\n")
    (conf-for-status . "Vilket möte vill du se statusen för? ")
    (no-such-conf . "Mötet finns ej.\n")
    (status-record . "Status för möte %#1M (%#1m)%#2s\n\n")
    (change-type-prompt . "Ändra mötestyp för möte %#1M (%#1m) (%#2s)...")
    (Mailbox . "Brevlåda")
    (Protected . "Hemligt")
    (no-comments . "original")
    (closed . "slutet")
    (allow-anon . "anonymt ok")
    (allow-secret . "hemliga medl")

    (created-by . "Skapat av person %25#1p %#3s(%#2P)\n")
    (created-at . "Skapad:%35#1s\n")
    (members .    "Antal medlemmar: %25#1d\n")
    (conf-allows-secret-members . "Hemliga medlemmar:                       %#1s\n")
    (conf-allows-anon-texts .     "Anonyma inlägg:                          %#1s\n")
    (anon-texts-permitted . "Anonyma inlägg är tillåtna")
    (anon-texts-not-permitted . "Anonyma inlägg är inte tillåtna")
    (secret-members-permitted . "Hemliga medlemmar är tillåtna")
    (secret-members-not-permitted . "Hemliga medlemmar är inte tillåtna")
    (garb-nice .      "Livslängd på inlägg:        %14#1d dag%#1?d%[%]%[ar%]\n")
    (keep-commented . "Minsta livslängd för kommenterade inlägg: %#1d dag%#1?d%[%]%[ar%]\n")
    (lowest-local-no ."Lägsta existerande lokala nummer: %8#1d\n")
    (highest-local-no . "Högsta existerande lokala nummer: %8#1d\n")
    (last-text-time . "Tid för senaste inlägg: %18#1s (står det i din cache)\n")
    (no-of-motd . "Lapp på dörren i text nummer: %12#1n\n")
    (pers-has-privileges .  "Rättigheter: %#1s\n")
    (pers-has-privileges-2 .  "%28#1s")
    (superconf-is-no-name . "Supermöte:       %25#1m %#3s(%#2M)\n")
    (permitted-submitters-no-name . "Tillåtna författare:%22#1m %#3s(%#2M)\n")
    (supervisor-is-no-name . "Organisatör:     %25#1p %#3s(%#2P)\n")
    (presentation-no . "Presentation:    %25#1n\n")
    (conf-mship-priority . "Prioritet:       %25#1n%#2?b%[ %#2s%]%[%]\n")
    (conf-has-motd . "\n%#1M har en lapp på dörren:\n")
    (status-conf-generic . "%-40#1s %#2s\n")
    (status-aux-item .   "Okänd tilläggsinformation: %15#1s%#3s (skapad av %#2M)\n")
    (conf-mx-allow-envelope-sender-regexp . "Tillåten avsändare vid import:           %#1s %#2s\n")
    (conf-mx-list-name . "Importerad mailinglista:                 %#1s %#2s\n")
    (conf-mx-refuse-import . "Vägra import av:                         %#1s %#2s\n")
    (recommended-conf-aux . "Rekommenderat möte:                      %#1M <%#1m> %#2s\n")
    (bad-recommended-conf-aux . "Rekommenderat möte:                     \"%<20...#1s\" %[%#2@[ogiltigt]%] %#3s\n")
    (status-read-faq-aux-item . "Läst FAQ:                 %15#2n för %#1?z%[%#1M <%#1m>%]%[servern%] %#3s\n")
    (bad-status-read-faq-aux-item . "Läst FAQ:                              \"%<20...#1s\" %[%#2@[ogiltigt]%] %#3s\n")
    (status-rejected-recommendation-aux-item . "Avvisad mötesrekommendation:            %#1M %#2s\n")
    (bad-status-rejected-recommendation-aux-item . "Avvisad mötesrekommendation:           \"%<20...#1s\" %[%#2@[ogiltigt]%] %#3s\n")
    (status-send-comments-to . "Dirigera om kommentarer till:           %#1M <%#1m> %#2s\n")
    (bad-status-send-comments-to . "Dirigera om kommentarer till:          \"%<20...#1s\" %[%#2@[ogiltigt]%] %#3s\n")

    (Everybody . "Alla")
    (show-members-list-also-q . "Vill du se medlemslistan också? ")
    (show-membership-info-q . "Visa medlemskapsinformation och antalet olästa? ")
    (conf-has-these-members . "\n%#1M har följande medlemmar:\n")
    (conf-has-no-members . "\n%#1M har inga medlemmar.\n")
    (member-list-header . "\nSenast inne         Osett   Namn\n")
    (secret-membership . "*** Hemlig rad ***\n")
    (conf-membership-line . "%#4@%#1s%#2M %#3s\n")
    (conf-membership-line-2 . "                            Adderad %#1s av %#2P\n")
    (pers-for-status . "Vem vill du se statusen för? ")
    (text-to-see-author-status-of . "Vilken texts författare vill du statusen för? ")
    (no-such-pers . "Det finns ingen sådan person.\n")
    (pers-status-record . "Status för person %#1P (%#1p)\n")
    (created-time .  "Skapad:%34#1s\n\n")
    (created-confs . "Skapade möten:%27#1d\n")
    (created-persons . "Skapade personer:%24#1d\n")
    (created-texts . "Skapade texter:%26#1d\n")
    (created-lines . "Skapade rader:%27#1d\n")
    (created-chars . "Antal skapade tecken:%20#1d\n")
    (no-of-sessions . "Antal sessioner:%25#1d\n")
    (present-time-d-h-m-s . "Närvarotid:%19#1d d %02#2d:%02#3d:%02#4d\n")
    (last-log-in . "Senaste in-/utloggning:%18#1s\n")
    (user-name . "Användare: %30#1s\n")

    (read-texts . "Lästa texter:%28#1d\n")
    (marked-texts . "Markerade texter:%24#1d\n")
    (time-for-last-letter . "Tid för senaste brev:%20#1s (står det i din cache)\n")
    (superconf . "Supermöte:%31#1m %#3s(%#2M)\n")
    (supervisor . "Organisatör:%29#1p %#3s(%#2P)\n")
    (member-of-confs . "Medlem i (antal möten):%18#1d\n")
    (presentation . "Presentation:    %24#1n\n")
    (show-membership-list-also-q . "Vill du se vilka möten personen är medlem i också? ")
    (not-allowed-see-confs . "%#1P är inte medlem i några möten.\n")
    (is-member-of . "\n%#1P är medlem i följande möten:\n")
    (membership-list-header . "\nSenast inne           Osett  Namn\n")
    (pers-membership-line . "%#5@%#1s%#2s%#3M %#4s\n")
    (pers-membership-line-2 . "                             Adderad %#1s av %#2P\n")
    (is-supervisor-mark . "O ")
    (moronify-saving . "Sparar ändringen...")
    (moronify-saving-done . "Sparar ändringen...klart")
    (moronify-saving-error . "Kunde inte spara ändringen!")
    (befriend-saving . "Sparar ändringen...")
    (befriend-saving-done . "Sparar ändringen...klart")
    (befriend-saving-error . "Kunde inte spara ändringen!")
    (moronify-whom . "Lägg vem på minuslistan? ")
    (befriend-whom . "Lägg vem på pluslistan? ")
    (who-to-send-message-to . "Vem vill du skicka meddelandet till? (%#1s) ")
    (send-empty-message-p . "Meddelandet är tomt. Vill du ändå skicka det? ")
    (his-total-unread . "\n%#1M har totalt %#2d olästa (plus %#3d i passiva medlemskap).\n")
    (message-prompt . "Meddelande%#1?z%[%]%[ (till alla inloggade)%]: ")
    (message-sent-to-user . "\
%[%#3$\
================================================================
%]%[%#4$\
Ditt meddelande till %#2M (%#5s):

%#1t
%]%[%#3$\
----------------------------------------------------------------
%]")
    (message-sent-to-all . "\
%[%#3$\
================================================================
%]%[%#4$\
Ditt alarmmeddelande löd (%#5s):

%#1t
%]%[%#3$\
----------------------------------------------------------------
%]")
    (message-sent-to-user-long . "\
%[%#3$\
===============================================================================
%]%[%#4$\
Ditt meddelande till %#2M (%#5s):

%#1t
%]%[%#3$\
-------------------------------------------------------------------------------
%]")
    (message-sent-to-all-long . "\
%[%#3$\
===============================================================================
%]%[%#4$\
Ditt alarmmeddelande löd (%#5s):

%#1t
%]%[%#3$\
-------------------------------------------------------------------------------
%]")
    (message-use-alarm-instead . "Använd kommandot %#1s för att skicka alarmmeddelanden.\n")
    (message-all-info . "\
%#1@Sänd alarmmeddelande till alla inloggade.

%[%#3FÖverväg att använda \"%#2C\" för att skicka meddelandet till ett
specifikt möte eller en enskild användare.%]
")
    (message-recipient-info . "Sänd meddelande till %#1M\n")
    (message-nope
     . "Det gick inte. %#3s\
Meddelandet du försökte sända till %#1M var:
%#2t\n")
    (only-last . "Endast läsa senaste i %#1s: ")
    (initial-unread . "Initialt antal olästa (tomt för alla): ")
    (only-error . "Något gick galet. Ledsen.\n")

    (lp--only-last . "Antal texter att läsa: ")

    (session-list-unreads-in-confs . 
			      "Du har %#2?z%[%#2d oläst%#2?d%[%]%[a%] brev och totalt %]%[%]%#3d oläst%#3?d%[%]%[a%] i %#4d möte%#4?d%[%]%[n%] i %#1s.\n")
    (session-list-no-unread-in . "Du har läst ut %#1s.\n")

    (you-have-unreads . "Du har %#1d oläst%#1?d%[%]%[a%] inlägg i %#2M\n")
    (you-have-unreads-special . "Du har %#1d okommentera%#1?d%[t%]%[de%] inlägg i %#2M\n")
    (you-have-no-unreads . "Du har inget oläst i %#1M\n")
    (you-have-read-everything . "Du har sett alla nyheter.\n")
    (no-unreads-shown . "Hittade inga möten som matchade det kriteriet.\n")
    (total-unreads . "Du har totalt %#1d oläst%#1?d%[%]%[a%] inlägg i %#2d möte%#2?d%[%]%[n%].\n")
    (shown-unreads . "Visade %#1d oläst%#1?d%[%]%[a%] inlägg i %#2d möte%#2?d%[%]%[n%].\n")
    (list-unread-with-n-unread . "Visar endast möten med minst %#1d olästa.\n")
    (list-unread-with-at-most-n-unread . "Visar endast möten med som mest %#1d olästa.\n")
    (waiting-for-anything .
			  "Du väntar på ett inlägg i vilket möte som helst.\n")
    (waiting-higher-than . 
			 "Du väntar på ett inlägg i ett möte med högre prioritet än %#1d.\n")

    (have-to-be-in-conf-with-unread . "Du måste gå till ett icketomt möte först.\n")
    (Texts . "Inlägg")
    (Written . "Skrivet")
    (Lines . "Rader")
    (Author . "Författare")
    (Subject . "Ärende")
    (Comments . "Ko")
    (Num-marks . "Mk")
    (mark-type . "Markering")
    (mark-no . "Mrk")
    (could-not-read . "Du fick inte läsa denna text (%#1n).\n")
    (multiple-choice . "Flera alternativ finns.")
    (does-not-exist . "Detta kommando finns inte.")
    (summary-line . "%=-8#1n%#2s%4#3d  %[%#4@%#5:P%]  %[%#6@%#7r%]\n")
    (diff-what-text-old . "Gammal text att jämföra: ")
    (diff-what-text-new . "Ny text att jämföra: ")


    ;; Only people fixing bugs or receiving bug reports should
    ;; change these:
    (buggreport-compilestart . "Skapar buggrapporten...")
    (buggreport-compileend . "Skapar buggrapporten...klart")
    (buggreport-description . "Detta gjorde jag:
\(Fyll i dina kommentarer nedan\)\n================\n\n
================
Bland informationen nedan finns också en lista på de 100 sist tryckta
tangenterna i din emacs. Om du nyligen loggat in kan den innehålla ditt
lyskomlösenord. Titta igenom den och ändra det som är ditt lösenord
till * * * eller m i t t l ö s e n eller något annat lämpligt.

När du skrivit klart skall du skicka in din buggrapport till LysKOMs
elispklientutvecklare. Det sker antingen:
* med email till bug-lyskom@lysator.liu.se
* med vanligt brev till:
\tLysator
\tc/o ISY
\tLinköping University
\tS-581 83 Linkoping
\tSWEDEN.

Märk kuvertet \"LysKOM buggrapport för elispklienten\".\n\n")
    
    (buggreport-internals . "LysKOMs interna information:\n\n")
    (buggreport-version . "lyskom-version:")
    (buggreport-emacs-version . "emacs-version:")
    (buggreport-system-id . "system-id:")
    (buggreport-ctl-arrow-doc . "ctrl-doc:")
    (buggreport-unparsed . "\nlyskom-unparsed-buffer:")
    (buggreport-command-keys . "Nyss tryckta tangenter:")
    (buggreport-backtrace . "\n*Backtrace*:\n%#1s\n")
    (buggreport-communications . "\nlyskom-debug-communications-to-buffer-buffer:")
    (buggreport-all-kom-variables . "\n\nAndra variabler:\n***** *********")
    (buggreport-instead-of-byte-comp . "byte-code(\"byte-string\"")
    (buggreport-subject . "Bugg-rapport elisp-klienten version %#1s")


    (not-logged-in . "Du är inte inloggad.  ")

    ;; Used for kom-is-person-member-of-conference:
    (pers-to-check-mship-for . "Vems medlemskap vill du undersöka? ") ;-)
    (conf-to-check-mship-of . "...i vilket möte? ")
    (conf-is-empty . "Mötet %#1M är tomt.\n")
    (pers-is-member-of-conf . "Ja, %#1P är medlem i mötet %#2M.\n")
    (pers-is-passive-member-of-conf . "Nja, %#1P är bara passiv medlem i mötet %#2M.\n")
    (pers-is-not-member-of-conf . "Nej, %#1P är inte medlem i mötet %#2M.\n")
    (pers-is-member-of-conf-2 . "Antal olästa: %#2D (senast inne %#1s)\n")
    (pers-will-receive-async . "Tar %#1?b%[%]%[inte %]emot gruppmeddelanden till mötet\n")
    (pers-mship-priority . "Medlemskapets prioritet: %#1d\n")
    (Unknown-number . "Okänt antal")
    (text-to-check-will-read-for . "...för medlemskap i mottagare till inlägg: ")
    (pers-to-check-will-read-for . "Vilken person vill du kontrollera: ")
    (pers-is-member-of-rcpt . "Ja, %#1P är medlem i minst en mottagare till inlägg %#2n.\n")
    (pers-is-passive-member-of-rcpt . "Nja, %#1P är bara passiv medlem någon mottagare till %#2n.\n")
    (pers-is-not-member-of-rcpt . "Nej, %#1P är inte medlem i någon mottagare till %#2n.\n")

    ;; +++ cant seem to find where these are used:
    (name-is-not-in-conf . "%#1s är inte närvarande i något möte.\n")
    (name-is-in-conf . "%#1s är närvarande i\n%#2s\n")
    (connected-during . "Uppkopplingstid: %#1d sekunder.\n")
    ;; +++


    (conf-to-set-permitted-submitters-q . "Vilket möte vill du sätta tillåtna författare för? ")
    (conf-to-set-super-conf-q . "Vilket möte vill du sätta supermöte för? ")
    (new-super-conf-q . "Vilket möte vill du ha som supermöte? ")
    (new-permitted-submitters-q . "Möte med tillåtna författare till %#1s? (alla) ")
    (super-conf-for-is . "Ändra supermöte för %#1M till %#2M...")
    (permitted-submitters-removed-for-conf . "Tillåt alla författare i möte %#1M...")
    (submitters-conf-for-is . "Ändra tillåtna författare för möte %#1M till\nmedlemmarna i %#2M...")

    (conf-to-set-garb-nice-q . "Vilket möte vill du sätta livslängd för? ")
    (new-garb-nice-q . "Efter hur många dagar ska inlägg utplånas? ")
    (new-keep-commented-q . "Hur många dagar ska nya kommentarer skydda det kommenterade? ")
    (garb-nice-for-is . "Ändrar livslängden för %#1M till %#2d dag%#2?d%[%]%[ar%]...")
    (keep-commented-for-is . "Ändrar så kommentarer skyddar från utplåning i %#2d dag%#2?d%[%]%[ar%] för %#1M...")

    (really-shutdown . "Är du säker på att du vill stänga av servern? ")
    (closing-server . "Stänga av servern...")
    (really-sync . "Är du säker på att du vill spara databasen? ")
    (syncing-server . "Spara databasen...")
    (administrator . "administratör")
    (no-longer-administrator . "en normal användare igen")
    (you-are-now . "Ok, du kör nu som %#1s.\n")
    (setting-motd . "Sätter loginmeddelandet till text %#1n.\n")
    (set-motd-success . "Du har satt ett nytt loginmeddelande.\n")
    (set-motd-failed . "Det gick inte. Du var kanske inte administratör.\n")
    (removing-motd . "Tar bort loginmeddelandet.\n")
    (removed-motd . "Du har tagit bort loginmeddelandet.\n")
    (who-to-throw-out . "Vems session vill du kasta ut? ")
    (throwing-out . "Kastar nu ut session %#1d... ")
    (postpone-prompt . "Hur lite vill du läsa nu? ")
    (set-session-priority . "Sätt läsnivå: ")

    ;; From review.el:
    (no-unread-done . "Du måste markera inlägg olästa innan du kan markera fler olästa.\n")
    (no-review-done . "Du måste återse innan du kan återse mer.\n")
    (not-reviewing . "Du håller inte på att återse något nu.\n")
    (unread-how-many . "Oläsmarkera hur många?")
    (unread-how-many-roots . "Oläsmarkera hur många urinlägg?")
    (review-how-many . "Återse hur många?")
    (review-how-many-roots . "Återse hur många urinlägg?")
    (unread-how-many-more . "Oläsmarkera ytterligare hur många?")
    (review-how-many-more . "Återse ytterligare hur många?")
    (latest-n . "senaste %#1d")
    (first-n . "första %#1d")

    (review-by-whom . "Återse %#1s %#2?b%[%#2s %]%[%]av vem: ")
    (review-to-conf . "Återse %#1s %#2?b%[%#2s %]%[%]till möte: ")
    (unread-by-whom . "Oläsmarkera %#1s %#2?b%[%#2s %]%[%]av vem: ")
    (unread-to-conf . "Oläsmarkera %#1s %#2?b%[%#2s %]%[%]till möte: ")
    (all-confs . "alla möten")

    (unread-info-by-to . "Oläsmarkera %#1s %#4?b%[%#4s %]%[%]av %#2P till %#3M.\n")
    (unread-more-info-by-to . "Oläsmarkera %#1s %#4?b%[%#4s %]%[%]av %#2P till %#3M.\n")
    (unread-rest . "resten")
    (unread-more . "nästa %#1d")

    (review-info-by-to . "Återse %#1s  %#4?b%[%#4s %]%[%]av %#2P till %#3M.\n")
    (review-more-info-by-to . "Återse %#1s %#4?b%[%#4s %]%[%]av %#2P till %#3M.\n")
    (review-filter-roots . "urinlägg")

    (review-rest . "resten")
    (review-more . "nästa %#1d")
    (you-review . "Du återser nu %#1s.\n")
    (read-text-first . "Du måste läsa en text först.\n")
    (cannot-read-last-text . "Du kan inte läsa den senast lästa texten.\n")
    (review-n-texts . "Återse %#1d inlägg.\n")
    (review-marked . "Återse %#1d markerade.\n")
    (review-text-no . "Återse text nummer %#1n\n")
    (review-many-comments . "Återse %#2?d%[en%]%[%#2d%] kommentar%#2?d%[%]%[er%] till inlägg %#1n.\n")
    (view-many-comments . "Läs %#2?d%[en%]%[%#2d%] kommentar%#2?d%[%]%[er%] till %#1n.\n")
    (view-texts-in-conf . "Totalt %#1d inlägg att läsa i %#2M.\n")
    (not-reading-anywhere . "Du läser inte i något möte.\n")
    (read-normally-read . "Hur många vill du se igen? ")

    (review-conf-gone . "Mötet finns inte.\n")
    (review-pers-gone . "Personen finns inte.\n")
    (review-cant-read-conf . "Du kan inte återse inlägg till ett slutet möte du inte är med i.\n")
    (review-cant-read-letterbox . "Du kan inte återse inlägg till någon annans brevlåda.\n")
    (review-cant-read-empty . "Mötet är tomt.\n")
    (cant-review-everything . "Du kan inte återse alla texter i LysKOM.\n")
    (cant-unread-everything . "Du kan inte oläsmarkera alla texter i LysKOM.\n")

    (more-than-one-root . "Inlägg %#1n har mer än ett urinlägg.\n")
    (more-than-one-root-review . 
"Inlägg %#1n har mer än ett urinlägg, men endast ett träd kommer att visas.\n")

    ;; From edit-text.el:
    (press-C-c-C-c . "Tryck C-c C-c för att skicka in texten.")
    (recipient . "Mottagare")
    (carbon-copy . "Extra kopia")
    (blank-carbon-copy . "Dold kopia")
    (recipient-prefix . "\\([Mm]\\)")
    (carbon-copy-prefix . "\\([Ee]\\)")
    (blank-carbon-copy-prefix . "\\([Dd]\\)")
    (add-recipient . "Addera mottagare")
    (add-recipient-or-xref . "Addera...")

    (secret-aux-flag . "hemlig")
    (anonymous-aux-flag . "anonym")
    (inherit-aux-flag  . "ärvd")
    (aux-item-prefix . "[*]")
    (aux-item-prefix-regexp . "\\[\\*\\]\\s-*")
    (comment-item-prefix . "#\\s-*")
    (text-no-comment . "%#1d %#2s /%#3d rad%#3?d%[%]%[er%]/ %#4P %#5?b%[ [anonymt]%]%[%]\n")
    (cant-fcc-text-file-error . "Kan inte spara inlägg %#1n till \"%#2s\" (%#3s: %#4s).\n")
    (cant-fcc-text . "Kan inte spara inlägg %#1n till \"%#2s\" (%#3s).\n")

    (header-subject . "Ärende: ")
    (header-separator . "\\<lyskom-edit-mode-map>\
--- Skriv nedan. \
Skicka in=\\[kom-edit-send], \
Avbryt=\\[kom-edit-quit], \
Annat se \\[describe-mode] ---")
    (text-mass . "%#4s%#1s%[%#6@\n%]%[%#5@%#2s%]%[%#7@\n%]%#3s")
    (comment-to-by . "%#1s till text %#2n%#3s.\n")
    (already-sent . "Du har redan skickat denna text en gång. Sänd ändå? ")
    (subject . "Ärende: ")
    (subject-prefix . "[Ää\\{\\[]")
    (enter-subject-idi . "Skriv ett ärende.")
    (which-text-include . "Vilken text skall vi inkludera? ")
    (added-recipient . "Mottagare som skall adderas: ")
    (added-carbon-copy . "Extra kopia till möte: ")
    (added-blank-carbon-copy . "Dold kopia till: ")
    (text-to-comment-q . "Vilket inlägg vill du kommentera? ")
    (conf-has-motd-no . "Mötet har en lapp på dörren. (%#1d)\n\n%#2s")
    (still-want-to-add . "Vill du fortfarande addera mötet? ")
    (could-not-create-text . "\nTexten kunde ej skapas. %#2s.\n")
    (no-get-text . "Du fick inte hämta texten.")
    (unknown-header . "Okänd information på raden")
    (transform-error . "Skicka in oformatterat (%#1s)? ")
    (cant-find-info-node . "Hittar inte info-bufferten")

    (link-type . "Vad vill du länka till? ")
    (which-text-to-link . "Lägg in länk till text nummer: ")
    (which-text-to-link-err . "Hittar inte texten. Lägg in länk till text nummer: ")
    (which-pers-to-link . "Lägg in länk till person: ")
    (which-conf-to-link . "Lägg in länk till möte: ")

    ;; From view-text.el:
    (view-text-first-line . "%#7$%#2@%#1n %#3s /%#4d rad%#4?d%[%]%[er%]/ %#5P%#6?b%[%#6s%]%[%]\n")

    (marked-by-you . "Markerad av dig (typ: %#1s).\n")
    (marked-by-you-and-others . "Markerad av dig (typ: %#2s) och %#1?d%[någon annan%]%[%#1d andra%].\n")
    (marked-by-several . "Markerad av %#1d person%#1?d%[%]%[er%].\n")

    (timeformat-day-yyyy-mm-dd-hh-mm-ss . "%#7s %4#1d-%02#2d-%02#3d %02#4d:%02#5d:%02#6d")
    (timeformat-yyyy-mm-dd-hh-mm-ss . "%4#1d-%02#2d-%02#3d %02#4d:%02#5d:%02#6d")
    (timeformat-yyyy-mm-dd-hh-mm . "%4#1d-%02#2d-%02#3d %02#4d:%02#5d")
    (timeformat-yyyy-mm-dd . "%4#1d-%02#2d-%02#3d")
    (timeformat-hh-mm-ss . "%02#4d:%02#5d:%02#6d")
    (timeformat-hh-mm . "%02#4d:%02#5d")

    (format-time-date-and-time . "%#1s %#2s")
    (format-time-just-date . "%#1s")
    (format-time-just-time . "%#2s")
    (today . "idag")
    (yesterday . "igår")

    (no-such-text-no . "Det finns inget sådant inlägg. (%#1:n)\n")
    (text-created-at . "Skapad: %#1s\n")
    (text-imported-at . "Importerad: %#1s\n")
    (text-imported-at-by . "Importerad: %#1s av %#2P\n")

    (head-Subject . "Ärende: ")
    (Recipient . "Mottagare")
    (Extra-recipient . "Extra kopia")
    (Hidden-recipient . "Dold kopia")
    (mx-Recipient . "Extern mottagare")
    (mx-Extra-recipient . "Extern kopiemottagare")
    (mx-Extern-reply-to . "Externa svar till")
    (Strange-recipient . "Underlig mottagare")
    (send-at . "    Sänt:     %#1s\n")
    (sent-by . "    Sänt av %#1P\n")
    (received-at . "    Mottaget: %#1s\n")

    (comment-to-text . "Kommentar till text %#1n")
    (footnote-to-text . "Fotnot till text %#1n")
    (comment-in-text . "Kommentar i text %#1n")
    (footnote-in-text . "Fotnot i text %#1n")

    (comment-to-text-by . "Kommentar till text %#1n av %#2P")
    (footnote-to-text-by . "Fotnot till text %#1n av %#2P")
    (comment-in-text-by . "Kommentar i text %#1n av %#2P")
    (footnote-in-text-by . "Fotnot i text %#1n av %#2P")

    (attachment-to-text . "Bilaga %#3still text %#1n")
    (attachment-in-text . "Bilaga %#3si text %#1n")
    
    (envelope-sender . "Sänt av: %#1s\n")
    (attachment-filename . "Bilagans filnamn: \"%#1s\"\n")
    (message-id . "Meddelande-ID: %#1s\n")

    (written-by . " av %#1P\n")

    ;; From async.el:

    (name-has-changed-to-name . "%#1:P har nu bytt namn till %#2:P")
    (name-has-changed-to-name-r . "%#4@%[%#3@%#1:P%] har nu bytt namn till %[%#3@%#2:P%]\n")
    (you-changed-name-to . "Nu har du bytt namn till %[%#2@%#1:P%].\n")
    (database-sync . "Databasen synkas.")

    (lyskom-is-full . "\
===========================================================
Meddelande från LysKOM-systemet: Någon försökte koppla upp,
men misslyckades eftersom alla tillgängliga förbindelser är
upptagna. Logga ut och kom tillbaks senare om du väntar nu.
===========================================================\n")
    (lyskom-is-full-long . "\
===============================================================================
Meddelande från LysKOM-systemet: Någon försökte koppla upp,
men misslyckades eftersom alla tillgängliga förbindelser är
upptagna. Logga ut och kom tillbaks senare om du väntar nu.
===============================================================================
")
    (has-entered . "Nu har %#1:P gått in i %#2s.")
    (has-entered-r . "%#2@Nu har %#1P gått in i %#3s%#6?b%[ från %#6s%]%[%]%#4?b%[ (%#4s %#5s)%]%[%].\n")
    (has-left . "Nu har %#1:P gått ur %#2s.")
    (has-left-r . "%#2@Nu har %#1P gått ur %#3s (%#4s %#5s).\n")
    (Unknown . "Okänd")
    (unknown . "okänd")
    (Unknown2 . "Okänt")
    (unknown-person . "Okänd person")

    (no-longer-member . "Du är inte längre medlem i %#1M.\n")
    (no-longer-member-n . "Du är inte längre medlem i möte %#1m (finns inte).\n")

    (have-become-member . "Du har blivit medlem i %#1M.\n")
    (have-become-invited-member . "Du har bjudits in till %#1M.
Gå till mötet för att acceptera eller tacka nej till inbjudan. Du kan även
använda kommandot 'Bli medlem i möte' för att acceptera inbjudan.\n")
    (have-become-passive-member . "Du har blivit passiv medlem i %#1M.\n")

    (message-broadcast . "\
%[%#4$\
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#5$\
Alarmmeddelande från %#1P (%#3s):

%#2t
%]%[%#4$\
----------------------------------------------------------------
%]")
    (message-broadcast-long . "\
%[%#4$\
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#5$\
Alarmmeddelande från %#1P (%#3s):

%#2t
%]%[%#4$\
-------------------------------------------------------------------------------
%]")
    (message-from . "\
%[%#4$\
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#5$\
Personligt meddelande från %#1P (%#3s):

%#2t
%]%[%#4$\
----------------------------------------------------------------
%]")
    (message-from-long . "\
%[%#4$\
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#5$\
Personligt meddelande från %#1P (%#3s):

%#2t
%]%[%#4$\
-------------------------------------------------------------------------------
%]")
    (message-from-to . "\
%[%#5$\
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#6$\
Gruppmeddelande till %#3M\nfrån %#2P (%#4s):

%#1t
%]%[%#5$\
----------------------------------------------------------------
%]")
    (message-from-to-long . "\
%[%#5$\
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#6$\
Gruppmeddelande till %#3M\nfrån %#2P (%#4s):

%#1t
%]%[%#5$\
-------------------------------------------------------------------------------
%]")
    (text-is-created . "Text %#1n är skapad!")

    ;; Used in mode-line-process
    (mode-line-waiting . ": väntar")
    (mode-line-working . ": arbetar")
    (mode-line-saving . ": sparar")
    (mode-line-down . ": nerkopplad")

    ;; From completing-read.el:
	   
    (person-or-conf-no-regexp . "\\`[ \t]*[mpMP]\\w*[ \t]+\\([0-9]+\\)\\'")
    (session-no-regexp . "\\`[ \t]*[sS]\\w*[ \t]+\\([0-9]+\\)\\'")
    (conf-prompt . "Vilket möte/person? ")

    ;; From flags.el:
    (saving-settings . "Sparar inställningarna...")
    (saving-settings-done . "Sparar inställningarna...klart")
    (save-options-failed-internal . "\
Det gick inte att spara inställningarna på grund av ett internt fel.

Globala inställningar:         %#1?b%[Kodningen misslyckades%]%[OK%]
Elispklientens inställningar:  %#2?b%[Kodningen misslyckades%]%[OK%]
Andra klienters inställningar: %#3?b%[Kodningen misslyckades%]%[OK%]

Rapportera gärna detta fel till elispklientens utvecklare.
")
    (hang-on . "Vänta ett tag...\n")
    (no-changes . "Ingenting behövde sparas eftersom inga variabler hade ändrats.\n")
    (could-not-save-options . "Kunde ej spara inställningarna.\n")
    (could-not-create-area . "Kunde ej skapa texten.\n")
    (could-not-set-user-area . "Kunde ej ställa om user-arean. Servern säger felmeddelande: %#1d\n")
    (you-dont-exist . "Du finns inte.\n")
    (error-in-options . "Det fanns ett fel i en av dina variabler (%#1s)
Det stod \"%#2s\" i user-arean. Den sätts till nil istället.
Skicka en bugrapport.\n")
    (error-in-options-short . "Det fanns ett fel i dina inställningar. Vissa inställningar har inte lästs in.")
    (reading-settings-from-server . "Dina sparade inställningar har ändrats på servern. Läser om inställningarna.\n")

    ;; From elib-string.el:
    ;; No entries.
		
    ;; From lyskom-rest.el:
		
    (mode-line-unread . " Olästa ")
    (mode-line-letters . "brev ")
    (frame-title-unread . "Olästa")
    (frame-title-letters . " brev")
    (sessions-with-unreads . "Sessioner med olästa")
    (unread-letters . "olästa brev")

    (bad-text-no-prefix . "Kan inte översätta prefix `%s' till inläggsnummer")
    (prefix-arg-try-again . "Ange en annan text eller tryck control-g för att avbryta.\n")
    (error-code . "%#2?+%[Felkod %#2d/%#3S: %]%[%]%#1s.\n")
    (error-in-kom-do-when-done . "Variabeln kom-do-when-done har ett felaktigt värde.
Du bör sätta den till ett bättre värde.\n")
    (extended-command . "LysKOM: ")
    (wait-for-server . "LysKOM väntar på svar från servern. Vänta tills du får en prompt.\n")
    (review-text-q . "Återse text nummer: ")

    (completely-read-conf . "Du har sett alla texter i detta möte.\n")
    (not-in-any-conf . "Du läser inte något möte just nu.\n")

    (all-conf-unread-r . "Du har läst ut alla möten.\n")
    (all-conf-unread-s . "Du har läst ut alla möten. ")
    (enter-conf-unread . "%#1d oläst%#1?d%[%]%[a%]")
    (enter-conf-unread-faq . "%#1d oläst%#1?d%[%]%[a%] FAQ")

    (save-one-on-file-q . "Arkivera inlägg %#1n till fil: ")
    (save-many-on-file-q . "Arkivera %#1d inlägg till fil: ")
    (saving-one-on-file . "Arkiverar inlägg %#1n i %#2s.\n")
    (saving-many-on-file . "Arkivera %#1d inlägg i %#2s.\n")
    (save-text-to-file-q . "Spara inläggstext %#1n på fil: ")
    (save-text-confirm . "Filen %#1s finns redan. Vill du skriva över den? ")
    (saving-text . "Sparar inlägg %#1n som %#2s...")
    (what-save-no . "Vilket inlägg vill du spara? ")
    (wait-for-prompt . "Vänta på prompten.")

    (conference-no . "<möte %#1d>")
    (person-no . "<person %#1d>")
    (prompt-several-messages . "(%d meddelanden)")
    (prompt-single-message   . "(%d meddelande)")

    (text-buffer-missing . "Inläggsbufferten existerar inte längre.\n")

    (re-edit-text-prompt . "Redigera texten som inte kunde skapas")
    (go-to-pri-conf-prompt . "Gå till nästa prioriterade möte")
    (go-to-pri-mailbox-prompt . "Gå till din brevlåda (prioriterad)")
    (read-pri-text-conf . "Läsa nästa prioriterade text")
    (read-pri-letter-prompt . "Läsa nästa brev (prioriterat)")
    (review-next-text-prompt . "Återse nästa text")
    (review-next-comment-prompt . "Återse nästa kommentar")
    (review-next-marked-prompt . "Återse nästa markerade")
    (review-next-faq-prompt . "Återse nästa FAQ")
    (read-next-letter-prompt . "Läsa nästa brev")
    (read-next-footnote-prompt . "Läsa nästa fotnot")
    (read-next-comment-prompt . "Läsa nästa kommentar")
    (read-next-text-prompt . "Läsa nästa text")
    (read-next-attachment-prompt . "Läsa nästa bilaga")
    (go-to-conf-of-marked-prompt . "Återuppta återse markerade")
    (go-to-conf-of-review-tree-prompt . "Återuppta återse kommentarer")
    (go-to-conf-of-review-prompt . "Återuppta återse")
    (go-to-conf-of-review-faq-prompt . "Återuppta återse FAQ")
    (go-to-next-conf-prompt . "Gå till nästa möte")
    (go-to-your-mailbox-prompt . "Gå till din brevlåda")
    (next-pri-session-prompt . "Gå till prioriterat LysKOM \"%#1s\"")
    (next-unread-session-prompt . "Gå till LysKOM \"%#1s\"")

    (no-such-kom-session . "Det finns ingen sådan session med olästa.\n")
    (the-command . "Kommandot: %#1C")
    (error-in-login-hook . "Det fanns ett fel i din kom-login-hook: %#1s\n")

    (give-a-number . "Mata in ett tal: ")

    (yes-regexp . "\\`[jJ][aA]\\'")
    (no-regexp . "\\`[nN][eE][jJ]\\'")
    (yes-string . "Ja")
    (no-string . "Nej")
    (yes-or-no-nag . "Svara bara ja eller nej.")
    (yes-or-no . "(ja eller nej) ")

    (y-or-n-instring . "jJnN ")
    (j-or-n-nag . "Svara bara j eller n. ")
    (j-or-n . "(j eller n) ")
    (y-instring . "jJ ")

    (a-or-b-or-c-nag . "Ge ett giltigt alternativ. ")

    (person-does-not-exist . "Person %#1d (finns inte).")
    (conference-does-not-exist . "Möte %#1d (finns inte).")
    (conf-no-does-not-exist-r . "Möte %#1d finns inte.")
    (person-is-anonymous . "Anonym person")

    (process-signal . "Signal från processen.")
    (closed-connection . "
**************************************************
%#2s
LysKOM-sessionen onormalt stängd.
Felmeddelande: %#1s**************************************************")
    (closed-connection-other-buf . "\
**************************************************
LysKOM-sessionen %#1s stängdes onormalt.
**************************************************
")
    (dead-session . "LysKOM-sessionen är inte aktiv.")
    (resurrect-session . "LysKOM-sessionen har kopplats ner, vill du ansluta igen? ")
    (not-lyskom-buffer . "Detta är inte en aktiv LysKOM-session.")
    (error-not-found . "Fel nummer %#1d. Ingen klartextförklaring finns.")

    ;; Useful in more place than one:
    (illegal-command . "Otillåtet kommando.\n")
    (no-such-text . "Det finns inget sådant inlägg.\n")
    (no-such-text-m . "Det finns inget sådant inlägg.")
    (nobody . "ingen")
    (everybody . "alla")
    (everything . "allt")
    (anybody . "vem som helst")
    (forward . "framåt")
    (backward . "bakåt")
    (wait . "Vänta ett tag...\n")
    (comment . "Kommentar")
    (comment-prefix . "\\([Kk]\\)")
    (footnote . "Fotnot")
    (footnote-prefix . "\\([Ff]\\)")

    (by . " av %#1P")
    (text-created .  "Text nummer %#1n är skapad.\n")
    (text-created-anonymous .  "\
Text nummer %#1n är skapad (anonymt). För att göra det svårare för 
andra att ta reda på att du skapade inlägget bör du vänta ett tag med
att läsa det.\n")

    (resolve-session . "Ange vilken session: ")

    (starting-program . "Startar %#1s...")
    (super-jump . "Filtrerar ärende \"%#1r\" i möte \"%#2M\"\n")
    (no-recipient . "Inlägget har ingen mottagare.\n")
    (filtered . "[Filtrerad]")
    (filter-error-specification . "Fel i filterspecifikationen")
    (filter-error-bad-not . "Fel i filterspecifikation efter 'not'")
    (filter-error-unknown-key . "Filternyckeln '%S' är okänd.")
    (filter-error-key-arg . "Fel filterdata (%S %S)")
    (filter-tree . "Hoppar över text %#1n \"%#2r\" av %#3P och dess kommentarsträd.\n")
    (filter-text . "Hoppar över text %#1n \"%#2r\" av %#3P.\n")
    (filter-permanent . "Permanent? ")
    (filter-action . "Hur vill du filtrera? ")
    (filter-in-conf . "I vilket möte? (alla) ")
    (filter-subject . "Filtrera vilket ärende? ")
    (filter-which-text . "Filtrera inlägg som innehåller: ")
    (filter-author . "Filtrera vilken författare? ")
    (filter-recipient . "Vilken mottagare vill du filtrera? ")
    (permanent . "(permanent)")
    (temporary . "(tillfällig)")
    (filter-edit-buffer-name . "*LysKOM Filter Edit*")
    (filter-edit-empty-list . "Listan är tom")
    (filter-edit-start-of-list . "Listans början")
    (filter-edit-end-of-list . "Listans slut")
    (filter-edit-filter-how . "Hur vill du filtrera? ")
    (filter-edit-filter-what . "Vad vill du filtrera? ")
    (filter-edit-bad-argument . "Felaktig inmatning: %s")
    (filter-edit-outside-entry . "Kan inte utföra kommandot utanför ett filter")
    (filter-edit-outside-list . "Kan inte utföra operationen utanför listan")
    (filter-edit-end-of-pattern . "Filtrets slut")
    (filter-edit-save-p . "Spara förändringar? ")
    (filter-edit-remove-empty . "Tomma filter gör att alla texter filtreras. Vill du ta bort dessa? ")
    (filter-edit-restart-p . "Du har gjort ändringar. Vill du verkligen börja om? ")
    (filter-edit-help . 
		      "p,n Upp/ned, i/M-i Ny rad/filter, d/M-d Radera rad/filter, C-h m Mer hjälp")
    (filter-edit-header . "Ändra filter för \"%s\"\n")
    (filter-edit-footer . "
 Upp rad:    p  Ny rad:          i    Ta bort rad:     d
 Upp filter: P  Nytt filter:     M-i  Ta bort filter:  M-d
 Ned rad:    n  Spara ändringar: s    Ändra handling:  a
 Ned filter: N  Ångra ändringar: g    Ändra permanens: t
 Avsluta:    q                        Mer hjälp:       C-h m
")
    (filter-edit-saving . "Sparar ändringarna...")
    (filter-edit-saving-done . "Sparar ändringarna...klart")
    (filter-edit-saving-error . "Kunde inte spara ändringarna!")
    (filter-edit-insert-pred . "%#1s (=,!=): ")
    (filter-edit-insert-arg . "%#1s %#2s (vad): ")
    (no-filters . "Inga filter har definierats.\n")
    (view-filters-header . "\nAktiva filter:\n\n")
    (view-filters-footer . "")

    (ansaphone-new-message . "Nytt automatsvar: ")
    (ansaphone-message . "Svarsmeddelande:
----------------------------------------------------------------------
%#1t
----------------------------------------------------------------------
")
    (ansaphone-message-long . "Svarsmeddelande:
-------------------------------------------------------------------------------
%#1t
-------------------------------------------------------------------------------
")
    (ansaphone-state . "Automatsvar är nu %#1s.")
    (ansaphone-state-r . "Automatsvar är nu %#1s.\n")
    (ansaphone-messages-gone . "Sparade meddelanden raderade.")
    (ansaphone-no-messages . "Inga meddelanden.\n")
    (ansaphone-message-list-start . "Sparade meddelanden:\n\n")
    (ansaphone-message-list-end . "\n\n")
    (ansaphone-message-header . "Automatiskt svar (satt %#1s):\n")

    (remote-erase-messages . "Fjärrstyrning (%#1P %#2s): Sparade meddelanden raderade\n")
    (remote-set-message . "Fjärrstyrning (%#1P %#2s): Svarsmeddelande:
----------------------------------------------------------------------
%#3t
----------------------------------------------------------------------
")
    (remote-set-message-long . "Fjärrstyrning (%#1P %#2s): Svarsmeddelande:
-------------------------------------------------------------------------------
%#3t
-------------------------------------------------------------------------------
")    (remote-set-ansaphone . "Fjärrstyrning (%#1P %#2s): Automatsvar är nu %#3s\n")
    (remote-list-messages . "Fjärrstyrning (%#1P %#2s): Meddelanden listade\n")
    (remote-quit . "Fjärrstyrning(%#1P %#2s): Avsluta\n")

    (illegal-remote . 
		    "Otillåten fjärrstyrning:
Tid: %#1s
Från: %#2P <%#2p>
Till: %#3P <%#3p>
Text: 
%#4t")
    (illegal-remote-reply . "Fjärrstyrning inte accepterad: %#1s") 
    (remote-not-in-list . "Otillåten person")
    (remote-bad-command . "Felaktigt kommando")
    (remote-unknown-error . "Okänt fel")

    (remote-control-who . "Kontrollera vilken session? ")
    (remote-control-autoreply . "Automatsvar på eller av? ")

    (state-on . "påslaget")
    (state-off . "avslaget")

    (text-popup-title . "Inlägg %#1s")
    (conf-popup-title . "Möte %#1s")
    (pers-popup-title . "Person %#1s")
    (url-popup-title  . "URL %#1s")
    (aux-popup-title  . "Tilläggsinformation")
    (timestamp-popup-title . "Tidsstämpel %#1s")
    (recpt-type-popup-title . "Mottagartyp: %#1s")
    (add-recpt-button-text . "[Addera...]")
    (add-recpt-button-text-regex . "\\[Addera\\.\\.\\.\\]")
    (generic-popup-title . "%#1s")

    (who-i-am-not-present . "%#1P är inte närvarande i något möte\n")
    (who-i-am-present . "%#1P är närvarande i %#2M\n")
    (who-i-am-client . "Programmet heter lyskom.el, version %#1s%#2?b%[ (MULE)%]%[%].\n")
    (who-i-am-server . "Detta är %#1s, version %#2s.\n")
    (who-i-am-emacs . "Det körs under %#1s%#2?b%[ (MULE)%]%[%].\n")

    (no-such-session-r . "Det finns ingen sådan session. Personen kanske inte är inloggad.\n")
    (person-not-logged-in-r . "%#1P %#2?b%[har inte varit inloggad sedan %#2s%]%[är inte inloggad%].\n")
    (session-status . "Session %#1d är %#2P <%#2p>
%#5s %#7s %#4M
Kör %#6D från %#3s\n")
    (session-status-9 . "Session %#1d är %#2P <%#2p>
%#5s %#7s %#4M
Kör %#6D från %#3s
Uppkopplad sedan %#8s%#9s")
    (session-status-inactive . "\nHar inte varit aktiv på %#1s\n")
    (one-day . "en dag")
    (one-hour . "en timme")
    (one-minute . "en minut")
    (years . "år")
    (year . "år")
    (months . "månader")
    (month . "månad")
    (days . "dagar")
    (day . "dag")
    (hours . "timmar")
    (minutes . "minuter")
    (and . "och")
    (session-is-active . " och är aktiv.\n")
    (session-is-invisible . "Denna session är osynlig.\n")
    (status-for-session . "Sessionsstatus för vilken person? ")
    (unknown-doing-what . "Existerar")
    (doing-where-conn . "i")
    (doing-nowhere-conn . "men är")
    (waiting-for-membership . "Väntar på att medlemskapslistan ska läsas in...%d/%d")

    ;; From slow.el
    (no-such-command . "Det finns inget sådant kommando.\n")
    (command-completions . "Du kan mena någon av följande:\n %#1s\n")
    (which-language . "Ändra språk till (change language to): ")
    (send-formatted . "Skicka in som formatterad text? ")
    (changing-language-to . "Byter till %#1_s.\n")
    (language-set-to . "Språket är %#1_s%#2?b%[ %#3@[%#2s]%]%[%].\n")
    (language-not-loaded . "%#1s finns inte tillgängligt.\n")

    (reformat-html . "HTML")
    (reformat-enriched . "enriched")
    (reformat-filled . "ombruten")
    (reformat-image . "bild")
    (image-no-show . "< Bilden (%#1s) kan inte visas. Testa:\n  %#2t\n  %#3t >")
    (reformat-video . "video")
    (video-no-show . "< Video (%#1s) stöds inte. >")
    (reformat-audio . "ljud")
    (audio-no-show . "< Ljud (%#1s) stöds inte. >")
    (reformat-multipart . "flerdelat")
    (multipart-no-show . "< Flerdelade inlägg (%#1s) stöds inte. >")
    (reformat-message . "meddelande")
    (message-no-show . "< Meddelandetyp (%#1s) stöds inte. >")
    (reformat-model . "modell")
    (model-no-show . "< Modeller (%#1s) stöds inte. >")
    (reformat-application . "tillämpningsdata")
    (application-no-show . "< Tillämpningsdata (%#1s) stöds inte. >")
    (reformat-unknown . "okänd typ")
    (unknown-no-show . "< Okänd typ (%#1s) stöds inte. >")

    (reformat-truncated . "nedkortad")

    (reformat-signature . "avsignerad")

    (reformat-deswascii . "var swascii")

    (reformat-rot13 . "rot13")

    (reformat-utf-8 . "var UTF-8")

    (pdf-no-show . "< PDF-filer stöds inte. Emacs doc-view krävs för att visa i ny buffert. >")
    (view-pdf . "%#1@< PDF-filer stöds inte. > [Visa i ny buffert]")
    (reformat-pdf . "pdf")
    (doc-view-buffer-name . "Inlägg %#1n: %#2s")

    (need-library . "Paketet \"%#1s\" behövs för att utföra detta kommando.\n")
    (calc-expression . "Uttryck: ")

    (do-send-anonymous . "Skicka in texten anonymt? ")
    (remove-self-sending-anonymous . "Vill du ta bort dig själv som mottagare från inlägget? ")
    (anonymous . "anonym")
    (Anonymous . "Anonym")

    (secret-conf-letter . "H")
    (protected-conf-letter . "S")
    (created-conf-letter . "S")
    (superconf-conf-letter . "M")
    (supervisor-conf-letter . "O")

    ;; Some Help

    (where-is-doesnt-exist . "Kommandot %#1s finns inte")
    (where-is-on-no-key . "%#1s finns inte på någon tangent")
    (where-is-on-key . "%#1s finns på %#2s")

    ;; From aux-items.el

    (content-type-aux . "Innehållstyp: %#1s")
    (content-type-regexp . "Innehållstyp: \\(\\S-+\\)")
    
    (agreeing . "Håller med om inlägg %#1n...")
    (fast-replying . "Anmärkning till inlägg %#1n...")
    (author-fast-replies . "Anmärkningar av författaren:")
    (author-fast-reply-aux . "  \"%#1t\"")
    (other-fast-replies . "Anmärkningar:")
    (other-fast-reply-aux . "  \"%#1t\" /%#2P/")
    (fast-reply-too-long . "Kan inte skapa anmärkningar som består av mer än en rad.\n")
    
    (conf-to-list-faqs . "Lista FAQ för vilket möte: ")
    (faq-for-conf-aux . "Texten är FAQ för %#1M <%#1m>")
    (bad-faq-for-conf-aux . "Texten är FAQ: \"%<20...#1s\" %[%#2@[ogiltigt]%] %#3s")
    (faq-for-server-aux . "Texten är FAQ för LysKOM-servern") 
    (faq-in-text-aux . "FAQ i inlägg:                   %10#1n %#2D")
    (bad-faq-in-text-aux . "FAQ i inlägg:                            \"%<20...#1s\" %[%#2@[ogiltigt]%] %#3s")
    (conf-to-add-faq . "Vilket möte vill du lägga till en FAQ för? ")
    (text-to-add-as-faq . "Vilken text vill du lägga till som FAQ? ")
    (text-to-change-as-faq . "Vilken FAQ vill du ändra? ")
    (adding-faq . "Lägger till inlägg %#1n som FAQ för %#2?b%[%#2M%]%[servern%]...")
    (conf-to-del-faq . "Vilket möte vill du ta bort en FAQ från? ")
    (text-to-del-as-faq . "Vilken text vill du ta bort som FAQ? ")
    (deleting-faq . "Tar bort inlägg %#1n som FAQ för %#2?b%[%#2M%]%[servern%]...")
    (conf-has-no-faq . "%#1?b%[%#1M%]%[Servern%] har ingen FAQ\n")
    (unread-which-faq . "Oläsmarkera FAQ för vilket möte? ")
    (view-which-faq . "Återse FAQ för vilket möte? ")
    (review-faq-for-r . "Återse FAQ för %#1?b%[%#1M%]%[servern%].\n")
    (set-faq-for-conf-done . "Text %#3n är nu FAQ för %#1?b%[%#1M%]%[servern%].\n")
    (set-faq-for-conf-failed . "Kunde inte sätta FAQ för %#1?b%[%#1M%]%[servern%] till text %#3n.\n")
    (changed-faq-for-conf-done . "FAQ i text %#2n för %#1?b%[%#1M%]%[servern%] ändrad till text %#3n.\n")
    (changed-faq-for-conf-failed . "Kunde inte ändra FAQ för %#1?b%[%#1M%]%[servern%] till text %#3n.
%#2?b%[Text %#2n är fortfarande FAQ.
%]%[%]%#4s")
    (faq-in-text . "FAQ i text %#1n %#3s%#4s")
    (faq-in-text-by . "FAQ i text %#1n %#5s %#3sav %#2P %#4s")
    (server-has-new-faq . "\nDet finns %#1d ny%#1?d%[%]%[a%] FAQ till servern.\n")
    (unread-faqs-header . "\nOläst%#1?d%[%]%[a%] FAQ till %#2?b%[%#2M%]%[servern%]:\n")
    (all-faqs-header . "Alla FAQ till %#1?b%[%#1M%]%[servern%]:\n")

    (too-many-languages . "För många teckenuppsättningar för att koda. Skicka in okodat? ")
    (too-many-languages-error . "För många teckenuppsättningar för att koda. Kan inte skicka in inlägget.")
    (too-many-content-types . "Kan inte avgöra innehållstyp. Förenkla inlägget.")

    (cross-reference-text-status-aux . "Se även inlägg:                 %10#1n /%#2P/")
    (cross-reference-conf-status-aux . "Se även möte:                           <%#1m> %#1M /%#2P/")
    (cross-reference-pers-status-aux . "Se även person:                         <%#1p> %#1P /%#2P/")
    (strange-cross-reference-status .  "Se även:                                 %#1s (vad nu det betyder)")
    (cross-reference-text-aux . "Se även inlägg %#1n /%#2P/")
    (cross-reference-conf-aux . "Se även möte <%#1m> %#1M /%#2P/")
    (cross-reference-pers-aux . "Se även person <%#1p> %#1P /%#2P/")
    (cross-reference-text-regexp . "Se även inlägg \\([0-9]+\\)")
    (cross-reference-conf-regexp . "Se även möte <\\([0-9]+\\)>")
    (cross-reference-pers-regexp . "Se även person <\\([0-9]+\\)>")
    (strange-cross-reference . "Se även %#1s, vad nu det betyder")
    
    (no-comments-aux . "Författaren har begärt att inte få kommentarer")
    (no-comments-edit-aux . "Begär att inte få kommentarer")
    (no-comments-regexp . "Begär att inte få kommentarer")
    
    (personal-comment-aux . "Författaren har begärt endast personliga svar")
    (personal-comment-edit-aux . "Begär endast personliga svar")
    (personal-comment-regexp . "Begär endast personliga svar")
    
    (request-confirmation-aux . "%#1P har begärt läsbekräftelse")
    (request-confirmation-edit-aux . "Begär bekräftelse om läsning")
    (request-confirmation-regexp . "Begär bekräftelse om läsning")
    (confirm-read-q . "Bekräfta läsning av inlägg %#1n? ")
    
    (read-confirm-aux . "Läst: %#1P %#2s")

    (redirect-email-aux . "Skicka sladdpost till %#1s")
    (redirect-lyskom-aux . "Skicka inlägg till möte %#1M")

    (label-what-kind . "Sätt personligt namn på vad (inlägg, möte, person)? ")
    (label-what-text . "Sätt personligt namn på inlägg nummer: ")
    (label-what-conf . "Sätt personligt namn på vilket möte? ")
    (label-what-pers . "Sätt personligt namn på vilken person? ")
    (label-what-label . "Personligt namn: ")
    (label-secret    . "Skall andra kunna se namnet? ")

    (creating-software-aux . "Skapad med %#1s")
    (world-readable-text-aux . "Inlägget kan läsas utan inloggning")
    (world-readable-text-edit-aux . "Gör inlägget läsbart utan inloggning")

    (cant-get-aux-item . "Hittar inte tilläggsinformationen\n")
    (aux-item-no-info . "Ingen information tillgänglig\n")
    (aux-item-info . "\
Nummer:       %#1d %#6s
Typ:          %#2d (%#3s)
Skapad av:    %#4P <%#4p>
Skapad:       %#5s
Flaggor:      %#7s
Arvsgräns:    %#8s
Innehåll:    \"%#9s\"
")
    (secret . "Hemlig")
    (deleted . "Raderad")
    (hide-creator . "Dold skapare")
    (inherit . "Ärvs")
    (unlimited-inherit . "Obegränsat arv")
    (no-inheritance . "Arv är avslaget")
    (no-more-inheritance . "Gränsen uppnådd; inget mer arv")
    (inherit-steps . "%#1d steg")

    (aux-item-for . "Tilläggsinformation för ")
    (aux-item-for-conference-no . "möte <%#1m> %#1M")
    (aux-item-for-text-no . "inlägg %#1n")
    (aux-item-for-server . "servern")

    (what-fast-reply-no . "Anmärkning till vilket inlägg? ")
    (fast-reply-prompt . "Anmärkning: ")
    (agree-prompt . "Text: ")
    (default-agree-string . "Håller med")
    (what-agree-no . "Håll med vilket inlägg? ")

    (what-kind-to-add-aux-to . "Koppla tilläggsinformation till vad? ")
    (which-conf-to-add-aux-to . "Vilket möte vill du koppla till? ")
    (which-text-to-add-aux-to . "Vilket inlägg vill du koppla till? ")
    (which-aux-item-tag . "Aux-item tag: ")
    (which-aux-item-inherit . "Sätt inherit-flaggan? ")
    (which-aux-item-secret . "Sätt secret-flaggan? ")
    (which-aux-item-anonymous . "Sätt anonymous-flaggan? ")
    (which-aux-item-rsv1 . "Sätt dont-garb-flaggan? ")
    (which-aux-item-rsv2 . "Sätt reserved2-flaggan? ")
    (which-aux-item-rsv3 . "Sätt reserved3-flaggan? ")
    (which-aux-item-rsv4 . "Sätt reserved4-flaggan? ")
    (which-aux-item-inherit-limit . "Arvsgräns: ")
    (which-aux-item-data . "Data: ")

    ;; Cross-reference stuff

    (Person . "Person")
    (person . "person")
    (persons . "personer")
    (Conference . "Möte")
    (conference . "möte")
    (conferences . "möten")
    (Text . "Inlägg")
    (text . "inlägg")
    (texts . "inlägg")
    (Server . "Server")
    (server . "server")
    (servers . "servrar")
    (abc-conference . "mMöte")
    (abc-person . "pPerson")
    (abc-text . "iInlägg")

    (xref-type . "Vad vill du referera till? ")
    (which-text-to-xref . "Lägg till referens till text nummer: ")
    (which-text-to-xref-err . "Hittar inte texten. Lägg till referens till text nummer: ")
    (which-pers-to-xref . "Lägg till referens till person: ")
    (which-conf-to-xref . "Lägg till referens till möte: ")

    ;; Aux-item stuff from edit-text

    (no-comments-q . "Författaren vill inte ha kommentarer. Kommentera ändå? ")
    (private-answer-q . "Författaren har bett om personliga svar. Skriv personligt svar? ")

    (your-invited . "%#2P har bjudit in dig till %#1M.
Genom att acceptera inbjudan fortsätter du vara medlem i %#1M.
Du kan även tacka nej, och då kommer du automatiskt att gå ur mötet.
")

    (accept-invitation . "Vill du acceptera inbjudan och fortsätta vara medlem? ")
    (enter-passive . "Du är passiv medlem i %#1M.
Du måste bli aktiv medlem för att gå till mötet.\n")
    (convert-passive . "Vill du bli aktiv medlem i %#1M? ")
    (bug-secret-mship . "Du är hemlig medlem i %#1M\n")

    (invitation-mt-type . "inbjuden")
    (passive-mt-type . "passiv")
    (secret-mt-type . "hemlig")
    (message-flag-off-mt-type . "ej gruppmeddelanden")
    (message-flag-on-mt-type . "gruppmeddelanden")

    (Invitation-mt-type . "Inbjuden")
    (Passive-mt-type . "Passiv")
    (Secret-mt-type . "Hemlig")
    (Message-flag-mt-type . "Gruppmeddelanden")

    (not-author . "Du är inte författare till text %#1n.")
    (not-author-try-anyway-p . "Du är inte författare till inlägget. Vill du försöka ändå? ")
    (what-no-comments-no . "Vilken text vill du inte ha kommentarer till: ")
    (adding-no-comments . "Begär att inte få kommentarer till inlägg %#1n...")
    (already-no-comments . "Inlägg %#1n har redan begäran om inga kommentarer.\n")

    (what-private-answer-no . "Vilken text vill du ha personligt svar till: ")
    (adding-private-answer . "Begär att endast få personligt svar till inlägg %#1n...")
    (already-private-answer . "Inlägg %#1n har redan begäran om enbart personliga svar.\n")

    (what-request-confirm-no . "Vilken text vill du ha läsbekräftelse till: ")
    (adding-request-confirm . "Begär läsbekräftelse till inlägg %#1n...")
    (already-request-confirm . "Inlägg %#1n har redan begäran om läsbekräftelse.\n")

    (review-mail-headers-to-what . "Vilken texts brevhuvud vill du se? ")
    (no-mail-headers . "Inlägg %#1n har inget brevhuvud\n")
    (mail-headers-for . "Brevhuvud för inlägg %#1n:\n")
    (email-name-prefix . "")
    (email-name-suffix . "")

    (you-are-anonymous . "Du är nu någorlunda anonym.\n")
    (you-are-nonanonymous . "Du är inte längre anonym.\n")
    (you-are-already-anonymous . "Du är redan någorlunda anonym.\n")
    (you-are-already-nonanonymous . "Du är redan icke-anonym.\n")

    (start-keep-alive . "Sänder data med %#1d sekunders mellanrum för att hålla förbindelsen aktiv.")
    (stop-keep-alive . "Data sänds inte längre för att hålla förbindelsen aktiv.")

    (review-converted-q . "Återse vilket inlägg konverterat?")
    (review-noconversion-q . "Återse vilket inlägg omodifierat?")
    (review-rot13-q . "Återse vilket inlägg i rot13?")
    (unread-commented-q . "Oläsmarkera vilket inläggs kommenterade?")
    (review-commented-q . "Återse vilket inlägg kommenterade?")
    (unread-tree-q . "Oläsmarkera alla kommentarer rekursivt för vilket inlägg?")
    (review-tree-q . "Återse alla kommentarer rekursivt för vilket inlägg?")
    (unread-root-q . "Oläsmarkera urinlägget för vilken text?")
    (find-root-q . "Återse urinlägget för vilken text?")
    (unread-root-review-q . "Oläsmarkera träd för vilket inlägg?")
    (find-root-review-q . "Återse träd för vilket inlägg?")
    (unread-comments-q . "Oläsmarkera alla kommentarer till vilket inlägg?")
    (review-comments-q . "Återse alla kommentarer till vilket inlägg?")
    (review-xrefs-q . "Återse alla korsreferenser från vilket inlägg?")

    (confusion-who-to-reply-to . "Jag förstår inte vems inlägg du vill skriva ett privat svar till.\n")
    (confusion-what-to-answer-to . "Jag förstår inte vilken text du vill besvara.\n")
    (confusion-what-to-view . "Jag förstår inte vilken text du vill återse.\n")

    (confusion-what-to-reply-to . "Jag förstår inte vilken text du vill skriva en anmärkning till.\n")
    (confusion-what-to-agree-to . "Jag förstår inte vilken text du vill hålla med om.\n")
    (confusion-what-to-request-confirmation . "Jag förstår inte vilken text du vill ha läsbekräftelser på.\n")
    (confusion-what-to-comment . "Jag förstår inte vilken text du vill kommentera.\n")
    (confusion-what-to-footnote . "Jag förstår inte vilken text du vill skriva en fotnot till.\n")

    (confusion-what-to-delete . "Jag förstår inte vilken text du vill radera.\n")
    (confusion-what-to-add-sub-recipient . "Jag förstår inte vilken text du vill %#1s.\n")
    (add-rcpt-action-name . "addera mottagare till")
    (add-copy-action-name . "addera extra-kopiemottagare till")
    (add-bcc-action-name . "addera dold kopia till")
    (sub-action-name . "subtrahera mottagare från")
    (move-action-name . "flytta")

    (confusion-what-to-add-comment-to . "Jag förstår inte vilken text du vill addera en kommentar till.\n")
    (confusion-what-to-sub-comment-from . "Jag förstår inte vilken text du vill subtrahera en kommentar från.\n")
    (confusion-what-to-add-footnote-to . "Jag förstår inte vilken text du vill addera en fotnot till.\n")
    (confusion-what-to-sub-footnote-from . "Jag förstår inte vilken text du vill subtrahera en fotnot från.\n")
    
    (confusion-what-to-mark . "Jag förstår inte vilket inlägg du vill markera.\n")
    (confusion-what-to-unmark . "Jag förstår inte vilket inlägg du vill avmarkera.\n")
    (confusion-what-to-save . "Jag förstår inte vilket inlägg du vill spara.\n")

    (confusion-what-to-review-mail-headers . "Jag förstår inte vilket inläggs brevhuvud du vill se.\n")
    (confusion-what-to-unread-root . "Jag förstår inte vilket inläggs urinlägg du vill markera oläst.\n")
    (confusion-what-to-find-root . "Jag förstår inte vilket inläggs urinlägg du vill återse.\n")
    (could-not-find-root . "Kan inte identifiera urinlägget. Kommentarsstrukturen kanske är cirkulär.\n")
    (confusion-what-to-unread-root-review . "Jag förstår inte vilket inläggsträd du vill markera oläst.\n")
    (confusion-what-to-find-root-review . "Jag förstår inte vilket inläggsträd du vill återse.\n")


    ;; Help

    (help-with-what . "Vad vill du ha hjälp med? ")
    (help-for . "Hjälp för \"%#1s\"\n")

    ;; Button actions

    (lyskom-button-view-text-action . "Återse texten")
    (lyskom-button-unread-text-action . "Oläsmarkera texten")
    (lyskom-button-copy-text-no-action . "Kopiera inläggsnumret")
    (lyskom-button-review-converted-action . "Återse konverterat")
    (lyskom-button-review-noconversion-action . "Återse omodifierat")
    (lyskom-button-review-rot13-action . "Återse rot13")
    (lyskom-button-find-root-review-action . "Återse träd")
    (lyskom-button-find-root-action . "Återse urinlägget")
    (lyskom-button-comment-text-action . "Kommentera texten")
    (lyskom-button-private-comment-text-action . "Personligt svar")
    (lyskom-button-mark-text-action . "Markera texten")
    (lyskom-button-unmark-text-action . "Avmarkera texten")
    (lyskom-button-save-text-action . "Arkivera inlägg")
    (lyskom-button-save-text-body-action . "Spara inläggstext")
    (lyskom-button-review-comments-action . "Återse alla kommentarer")
    (lyskom-button-review-tree-action . "Återse alla kommentarer rekursivt")
    (lyskom-button-write-footnote-action . "Fotnotera")
    (lyskom-button-fast-reply-action . "Anmärk")
    (lyskom-button-view-conf-presentation-action . "Visa presentation")
    (lyskom-button-view-conf-status-action . "Visa mötesstatus")
    (lyskom-button-goto-conf-action . "Gå till mötet")
    (lyskom-button-send-message-action . "Skicka gruppmeddelande")
    (lyskom-button-add-self-action . "Bli medlem i mötet")
    (lyskom-button-sub-self-action . "Utträd ur mötet")
    (lyskom-button-view-pers-presentation-action . "Visa presentation")
    (lyskom-button-view-pers-status-action . "Visa personstatus")
    (lyskom-button-view-session-status-action . "Visa sessionsstatus")
    (lyskom-button-mail-action . "Skicka brev")
    (lyskom-button-send-message-action . "Sänd meddelande")
    (lyskom-button-moronify-action . "Lägg på idiot/knäppskalle-listan")
    (lyskom-button-befriend-action . "Lägg på vänner/bekanta-listan")
    (lyskom-button-open-url-action . "Öppna")
    (lyskom-button-copy-url-action . "Kopiera")
    (lyskom-button-goto-info-node-action . "Öppna")
    (lyskom-button-open-email-action . "Skicka mail")
    (lyskom-button-copy-email-action . "Kopiera")
    (lyskom-button-info-aux-action . "Information")
    (lyskom-button-delete-aux-action . "Radera")
    (lyskom-button-copy-timestamp-action . "Kopiera")
    (lyskom-button-recpt-type-recipient . "Mottagare")
    (lyskom-button-recpt-type-copy . "Extra kopia")
    (lyskom-button-recpt-type-bcc . "Dold kopia")
    (lyskom-button-recpt-type-sub . "Subtrahera")
    (lyskom-button-recpt-add-recipient . "Mottagare")
    (lyskom-button-recpt-add-copy . "Extra-kopiemottagare")
    (lyskom-button-recpt-add-bcc . "Dold-kopiamottagare")
    (lyskom-button-aux-type-xref . "Referens till möte/person/inlägg")
    (lyskom-button-aux-type-no-comments . "Begäran om inga kommentarer")
    (lyskom-button-aux-type-personal-comments . "Begäran om personligt svar")

    (lyskom-edit-toggle-secret-aux-action . "Växla \"hemlig\"")
    (lyskom-edit-toggle-anonymous-aux-action . "Växla \"anonym\"")
    (lyskom-edit-toggle-inherit-aux-action   . "Växla \"ärvd\"")
    (lyskom-edit-delete-aux-action          . "Ta bort")
    (lyskom-prioritize-flag-toggle-action . "Växla")
    (lyskom-prioritize-flag-set-action . "Sätt på")
    (lyskom-prioritize-flag-clear-action . "Stäng av")

    (server-status-header   . "Status för LysKOM-server %#1s%#2?b%[ (%#2s:%#3d)%]%[%]\n\n")
    (server-status-server   . "Kanonisk server:                         %#1s%#2?b%[:%#2s%]%[%]\n")
    (server-status-version  . "Programversion:                          %#1s %#2s\n")
    (server-status-protocol . "Protokollversion:          %15#1d\n")
    (server-status-sessions . "\
Antal sessioner:     %21#1d (totalt)
                     %21#2d aktiva under de senaste %#7d minuterna
                     %21#3d inaktiva sessioner
                     %21#4d okänd aktivitet
                     %21#5d osynliga sessioner
                     %21#6d ej inloggade/hemliga/zombies\n")
    (server-status-first-text . "Äldsta befintliga text:     %14#1n\n")
    (server-status-last-text  . "Yngsta befintliga text:     %14#1n%#2?b%[ (%#2n vid start)%]%[%]\n")
    (server-status-texts .      "Antal existerande inlägg:   %14#1d (%#2s just nu, %#3d vid start)\n")
    (server-status-confs .      "Antal existerande möten:    %14#1d (%#2s just nu, %#3d vid start)\n")
    (server-status-pers .       "Antal existerande personer: %14#1d (%#2s just nu, %#3d vid start)\n")
    (server-status-has-motd . "\nServern har en lapp på dörren:\n")
    (server-status-time . "Serverns tid:                   %#1s\n")
    (server-status-boot-time . "Servern startades:              %#1s\n")
    (server-status-save-time . "Initial databas sparades:       %#1s%#2?b%[ (%#2s)%]%[%]\n")

    (mship-type-invitation-q . "Inbjudan till medlemskap? ")
    (mship-type-passive-q . "Passivt medlemskap? ")
    (mship-type-secret-q . "Hemligt medlemskap? ")
    (recommend-which-conf . "Vilket möte vill du rekommendera? ")
    (recommend-set-priority-q . "Rekommendera prioritet? ")
    (recommend-set-mship-type-q . "Rekommendera mötestyp? ")
    (recommending-conf . "Rekommenderar %#1M%#2?b%[ (prioritet %#2d)%]%[%]%#3?b%[ %#3s%]%[%]...")
    (has-recommended-conf . "%#1P har rekommenderat möte %#2M. Vill du bli medlem?\n")
    (accept-recommendation . "Gå med i %#1M? ")
    (accepting-recommendation . "Gå med i rekommenderat möte %#2M...")

    (redirect-for-whom . "Dirigera om kommentarer för: ")
    (redirect-to-which-conf . "Dirigera om kommentarer till: ")
    (redirecting-comments-to . "Dirigerar om kommentarer för %#1P till %#2M%#3?b%[ (förr %#1M)%]%[%]...")

    (kom-redirect-comments-e49 . "Du har inte rätt att ändra denna omdirigering")
    (kom-redirect-comments-e48 . "Omdirigering av kommentarer stöds inte av servern")

    (external-program-missing . "Hittar inte det externa programmet \"%#1s\" som krävs för att genomföra kommandot.")
    (ignored-user-area-var . "\

Ignorerar följande variabler i dina inställningar:

    %#1s

Variablerna är inte registrerade LysKOM-variabler och kan därför inte
läsas från servern. Du kan ta bort variablerna från servern genom att
ge kommandot \"Spara inställningar\" med prefixargument.

%#2?b%[%#5F\
Okända variabler kan bero på att dina inställningar sparades med
version %#3s av elispklienten, medan detta är version %#4s.

%]%[%]")

    (unknown-aux-item . "Okänd tilläggsinformation")
    (text-header-aux-item . "%#1s: <%#2d> \"%#3s\" %#4s")

    (aux-content-type-name . "Innehållstyp")
    (aux-fast-reply-name . "Anmärkning")
    (aux-cross-reference-name . "Referens")
    (aux-no-comments-name . "Begäran om inga kommentarer")
    (aux-personal-comment-name . "Begäran om personliga svar")
    (aux-request-confirmation-name . "Begäran om läsbekräftelse")
    (aux-read-confirm-name . "Läsbekräftelse")
    (aux-redirect-name . "Omdirigering")
    (aux-x-face-name . "Bild")
    (aux-alternate-name-name . "Alternativt namn")
    (aux-pgp-signature-name . "PGP-signatur")
    (aux-pgp-public-key-name . "Publik PGP-nyckel")
    (aux-e-mail-address-name . "E-postadress")
    (aux-faq-text-name . "FAQ i text")
    (aux-creating-software-name . "Skapande klient")
    (aux-mx-author-name . "E-mailförfattare")
    (aux-mx-from-name . "E-mail från")
    (aux-mx-reply-to-name . "E-mail svar sänds till")
    (aux-mx-to-name . "E-mail till")
    (aux-mx-cc-name . "E-mail CC")
    (aux-mx-date-name . "E-maildatum")
    (aux-mx-message-id-name . "E-mail-meddelande-ID")
    (aux-mx-in-reply-to-name . "E-mail svar till")
    (aux-mx-misc-name . "E-mailheader")
    (aux-mx-allow-filter-name . "E-mail allow-filter")
    (aux-mx-reject-forward-name . "E-mail reject-forward")
    (aux-mx-allow-envelope-sender-regexp . "Tillåten avsändare vid import")
    (aux-notify-comments-name . "Meddela kommentarer")
    (aux-faq-for-conf-name . "FAQ för möte")
    (aux-recommended-conf-name . "Mötesrekommendation")
    (aux-allowed-content-type-name . "Tillåten innehållstyp")
    (aux-canonical-name-name . "Kanoniskt namn")
    (aux-mx-list-name-name . "E-maillistnamn")
    (aux-send-comments-to-name . "Sänd kommentarer till")
    (aux-world-readable-name . "Läsbart utan inloggning")
    (aux-mx-refuse-import . "Vägra inläggsimport")
    (aux-elisp-client-read-faq-name . "Läst FAQ")
    (aux-elisp-client-rejected-invitation-name . "Avvisad inbjudan")

    (review-marking-as-read . "Återse läsmarkerar")
    (review-not-marking-as-read . "Återse läsmarkerar inte")

    (review-using-cache . "Återse använder cache")
    (review-not-using-cache . "Återse använder inte cache")

    (using-ssh-connection . "Använder ssh-uppkoppling till %#1s...")
    (opening-ssh-connection . "Öppnar ssh-uppkoppling till %#1s...")
    (ssh-cant-connect . "Kan inte göra ssh-uppkopplingen: %s")
    (ssh-closing . "Stänger ssh-uppkopplingen till %#1s")
    (ssh-unknown-host . "okänd värd")

    (keyboard-cancel . "Avbryt")
    (keyboard-menu-help . "(välj: C-n, C-p; bekräfta: RET)")
    (customize-help . "Se början av bufferten för mer information")

    (no-mule-warning . "\

%#1@Du har stödet för multibytetecken (MULE) avslaget i emacs. Detta beror
antagligen på att du har ett anrop till standard-display-european
eller motsvarande i din .emacs, eller i någon av systemets startfiler.

Elispklienten är gjord för att köras med stödet för multibytetecken
påslaget. Utan detta stöd kan inlägg visas fel, och du kan få problem
med att skapa inlägg. Du bör därför slå på stödet för multibytetecken
i emacs.

")
    (coding-system-mismatch-warning . "\

%#3@Din emacs är inställd på att i första hand koda tecken enligt
\"%#1s\", men den LysKOM-server du kör mot rekommenderar
\"%#2s\". Det innebär att kommentarer du skriver till
andras inlägg, och kommentarer till dina inlägg kan få en blandning av
tecken som inte går att visa ordentligt. Du bör byta språkomgivning i
emacs till en som använder \"%#2s\" som förstahandsval för
teckenkodning (se Emacs-manualen och kommandot set-language-environment
för mer information).

")

    (has-nameday-1 . "%#1s har namnsdag i dag%#2?b%[ (%#2s)%]%[%].")
    (has-nameday-2 . "%#1s och %#2s har namnsdag i dag%#3?b%[ (%#3s)%]%[%].")
    (no-nameday . "Ingen namnsdag i dag%#2?b%[ (%#2s)%]%[%]")

    (invalid-date-entry . "Ogiltigt datum")
    (number-out-of-range . "Otillåtet tal")
    (or-date . "eller datum")

    (set-unread-n . "Endast läsa %#1?d%[det senaste inlägget%]%[de senaste %#1d inläggen%].\n")
    (set-unread-date . "Endast läsa inlägg sedan %#3d %#2s %#1d.\n")

    (jump-from-text . "Hoppa över kommentarer till vilken text? ")
    (jumping-from-text . "Hoppar över kommentarer till inlägg %#1n.\n")
    (jumping-from-text-review . "Hoppar över återsedda kommentarer.\n")
    
    (session-to-copy-options-from . "Kopiera inställningar från session: ")
    (reading-settings-from . "Läser inställningar från %s...")
    (reading-settings-from-done . "Läser inställningar från %s...klart")

    (super-jump-q . "Superhoppa vilket inlägg? ")
    (conf-list-legend . "* Ej medlem; - Passiv medlem\n")

    (unread-text-q . "Gör vilket inlägg oläst? ")
    (marking-text-unread . "Markerar inlägg %#1n oläst...")
    (cant-mark-text-unread . "Kunde inte markera inlägg %#1n som oläst (%#2s)\n")
    (confusion-what-to-mark-unread . "Jag förstår inte vilken text du vill markera oläst.\n")
    (no-comment-to-to-unread . "Det finns inget kommenterat inlägg att markera som oläst.\n")

    (mx-refuse-import-html . "HTML")
    (mx-refuse-import-spam . "Spam")
    (mx-refuse-import-all . "Alla texter")
    (abc-html . "hHTML")
    (abc-spam . "sSpam")
    (abc-everything . "aAlla texter")
    (abc-regexp . "tTillåt avsändare")
    (limit-import-regexp-q . "Tillåt endast vilken avsändare (regexp)? ")
    (limit-import-to-conf . "Begränsa import till vilket möte? ")
    (limit-import-of-what . "Begränsa import av vad? ")
    (limit-import-not-super . "Du verkar inte vara organisatör för mötet. Försök ändå? ")
    (limiting-import . "Begränsar import av %#1s i %#2M...")
    (limiting-import-regexp . "Begränsar import till %#2M till avsänare %#1s...")

    (set-message-flag-for-conf . "Ändra meddelandeflagga för vilket möte? ")
    (set-message-flag-to-what . "Vill du ta emot gruppmeddelanden till %#1M? ")
    (setting-message-flag . "%#1?b%[Slår på%]%[Stänger av%] mottagning av gruppmeddelanden till %#2M...")

    (text-has-no-comments . "Inlägg %#1n har inga kommentarer\n")
    (text-has-no-footnotes . "Inlägg %#1n har inga fotnoter\n")
    (set-message-flag-q . "Ta emot gruppmeddelanden till %#1M? ")
    (why-you-got-no-presentation . "\

%#1@\
========================================================================
Du har ingen presentation%#2?z%[ men har skrivit %#2d inlägg%]%[%]

Använd kommandot \"Ändra presentation\" för att skriva eller ändra din
presentation.

%[%#4F\
Din presentation låter andra i %#3s veta lite om vem du är, och det
bidrar till känslan av samhörighet i %#3s. Det här meddelandet kommer
att upprepas då och då tills du har skrivit en presentation.%]
========================================================================

")

    (delete-user-area-warning . "\
%#1@\
========================================================================
Om du raderar user-arean så kommer alla dina sparade inställningar att
försvinna. Du kan naturligtvis spara dina aktuella inställningar, och 
då kommer en ny user-area att skapas med elispklientens (men inga andra
klienters) inställningar.

Är du helt säker på att du vet var du håller på med och att du verkligen
vill radera din user-area?
========================================================================
")
    (delete-user-area-confirm . "Radera din user-area och alla sparade inställningar? ")
    (removing-user-area . "Tar bort hänvisningen till user-arean från din personstatus...")
    (deleting-user-area . "Raderar din user-area...")

    (status-server-stats . "Statistik om servern:\n\n")
    (current-average . "nu")

    (unit-second . "sekund")
    (unit-minute . "minut")
    (unit-hour . "timme")
    (unit-day . "dag")
    (unit-mongth . "månad")
    (unit-year . "år")

    (db-status-backup . "informationsförlust kan ha förekommit")
    (no-support-in-server . "Servern saknar nödvändig funktionalitet för detta kommando.\n")
    (mark-confs-as-known . "Markera dessa  %#2d %#1s som kända? ")
    (no-new-conferences . "Inga nya %#2s%#1?b%[ sedan %#1s%]%[%].\n")
    (new-conferences-since . "Nya %#2s%#1?b%[ sedan %#1s%]%[%]:\n")
    (list-confs-from-date . "Lista %#1s skapade sedan vilket datum (tomt för alla)? ")

    (privs-wheel . "operatör")
    (privs-admin . "administratör")
    (privs-statistic . "statistik")
    (privs-create-conf . "skapa möten")
    (privs-create-pers . "skapa personer")
    (privs-change-name . "ändra namn")
    (privs-flg7 . "okänd (7)")
    (privs-flg8 . "okänd (8)")
    (privs-flg9 . "okänd (9)")
    (privs-flg10 . "okänd (10)")
    (privs-flg11 . "okänd (11)")
    (privs-flg12 . "okänd (12)")
    (privs-flg13 . "okänd (13)")
    (privs-flg14 . "okänd (14)")
    (privs-flg15 . "okänd (15)")
    (privs-flg16 . "okänd (16)")
    (lyskom-no-privileges . "inga rättigheter")

    (what-pers-privs-to-change . "Vems rättigheter vill du ändra? ")
    (change-pers-privs-prompt . "\
Nuvarande rättigheter för %#1P (%#1p):
    %#2s
Ändra rättigheter för %#1P (%#1p)...")

    (set-wheel-priv-q . "Aktivera operatörsrättigheter? ")
    (set-admin-priv-q . "Aktivera administratörsrättigheter? ")
    (set-statistic-priv-q . "Aktivera statistikrättigheter? ")
    (set-create-conf-priv-q . "Aktivera rätt att skapa möten? ")
    (set-create-pers-priv-q . "Aktivera rätt att skapa personer? ")
    (set-change-name-priv-q . "Aktivera rätt att ändra namn? ")
    (set-flg7-priv-q . "Aktivera okänd rättighet 7? ")
    (set-flg8-priv-q . "Aktivera okänd rättighet 8? ")
    (set-flg9-priv-q . "Aktivera okänd rättighet 9? ")
    (set-flg10-priv-q . "Aktivera okänd rättighet 10? ")
    (set-flg11-priv-q . "Aktivera okänd rättighet 11? ")
    (set-flg12-priv-q . "Aktivera okänd rättighet 12? ")
    (set-flg13-priv-q . "Aktivera okänd rättighet 13? ")
    (set-flg14-priv-q . "Aktivera okänd rättighet 14? ")
    (set-flg15-priv-q . "Aktivera okänd rättighet 15? ")
    (set-flg16-priv-q . "Aktivera okänd rättighet 16? ")

    (canceling-command . "Abryter kommandot...")

    (no-selection . "Ingen markerad")
    (selection . "%#1d markerad%#1?d%[%]%[e%]")
    (lp-no-hidden . "")
    (lp-hidden . "(%#1d dold%#1?d%[%]%[a%])")
    (priority-prompt . "Ny prioritet för %#1M: ")
    (priority-prompt-marked . "Ny prioritet på markerade möten: ")
    (lp-no-creation-info . "Ingen information om när medlemskapet skapades")
    (lp-invited . "Inbjuden")
    (lp-added . "Adderad")
    (lp-nope . "Det gick inte: %#1s")
    (lp-no-entry . "Det finns inget medlemskap här")
    (lp-no-active-filter . "(inga filter aktiva)")
    (lp-active-filters . "Aktiva filter: %#1s")
    (lp-mark-mship-with-prio . "Markera medlemskap med prioritet: ")
    (lp-unmark-mship-with-prio . "Avmarkera medlemskap med prioritet: ")
    (lp-no-selection . "Inga medlemskap är markerade")
    (lp-at-min-prio . "Medlemskapet har redan lägsta möjliga prioritet")
    (lp-at-max-prio . "Medlemskapet har redan högsta möjliga prioritet")
    (lp-beginning-of-list . "Listans början")
    (lp-end-of-list . "Listans slut")
    (lp-goto-priority . "Gå till prioritet: ")
    (lp-mode-name . "Medlemskap")
    (lp-header-main . "Medlemskap för %#1M på %#2s")
    (lp-list-header . " Prio   %#1s  Senast inne  Oläst  IHPM\n")
    (lp-help-footer . "
 Markera medlemskap: SPC      Markera område: C-w      Flytta markerade:   C-y
 Sätt prioritet:     p        Öka prioritet:  +        Minska prioritet:   -
 Flytta upp:         M-p      Flytta ned:     M-n      Ändra flaggor:  I,H,P,M
 Uppskjuta läsning:  u        Endast:         e
 Avsluta:            C-c C-c                           Mer hjälp:        C-h m
")
    (lp-hide-read-after . "Dölj medlemskap lästa efter: ")
    (lp-hide-read-since . "Dölj medlemskap ej lästa sedan: ")
    (lp-skipping-missing-meeting . "Mötet %#1M finns inte längre, hoppar över.")
    ))

(lyskom-language-var local lyskom-month-names sv
  '(("januari" . 1)   ("jan" . 1)
    ("februari" . 2)  ("feb" . 2)
    ("mars" . 3)      ("mar" . 3)
    ("april" . 4)     ("apr" . 4)
    ("maj" . 5)       ("maj" . 5)
    ("juni" . 6)      ("jun" . 6)
    ("juli" . 7)      ("jul" . 7)
    ("augusti" . 8)   ("aug" . 8)
    ("september" . 9) ("sep" . 9)
    ("oktober" . 10)  ("okt" . 10)
    ("november" . 11) ("nov" . 11)
    ("december" . 12) ("dec" . 12)))


;;; ================================================================
;;;              The commands and their associated functions



;;; The Alist formerly known as lyskom-commands
(lyskom-language-strings global lyskom-command sv
  '(
    (kom-help                 . "Hjälp")
    (kom-slow-mode            . "Långsamma kommandon")
    (kom-quick-mode           . "Snabba kommandon")
    (kom-moronify             . "Lägg på idiot/knäppskalle-listan")
    (kom-befriend             . "Lägg på vänner/bekanta-listan")
    (kom-send-message         . "Sända meddelande")
    (kom-send-alarm           . "Skicka alarmmeddelande")
    (kom-create-conf	      . "Skapa möte")
    (kom-delete-conf          . "Utplåna (möte/person)")
    (kom-delete-text          . "Radera inlägg")
    (kom-display-time	      . "Se tiden")
    (kom-go-to-conf	      . "Gå till möte")
    (kom-go-to-next-conf      . "Gå till nästa möte")
    (kom-jump		      . "Hoppa över alla kommentarer")
    (kom-list-created-conferences . "Lista ägda möten")
    (kom-list-conferences     . "Lista möten") 
    (kom-list-persons	      . "Lista personer")
    (kom-list-news            . "Lista nyheter")
    (kom-list-sessions        . "Lista sessioner")
    (kom-list-re              . "Lista (med) regexpar")
    (kom-membership	      . "Lista medlemskap")
    (kom-postpone	      . "Uppskjuta läsning")
    (kom-set-session-priority . "Sätt läsnivå")
    (kom-prioritize	      . "Prioritera möten")
    (kom-status-person	      . "Status (för) person")
    (kom-status-conf	      . "Status (för) möte")
    (kom-add-self	      . "Bli medlem i möte")
    (kom-change-priority      . "Ändra prioritet")
    (kom-list-summary	      . "Lista ärenden")
    (kom-sub-self             . "Utträda ur möte")
    (kom-quit		      . "Sluta")
    (kom-recover	      . "Starta om klienten") 
    (kom-start-anew	      . "Börja med nytt namn")
    (kom-view		      . "Återse inlägg")
    (kom-find-root-review     . "Återse träd")
    (kom-review-comments      . "Återse alla kommentarer")
    (kom-review-more-comments . "Återse fler kommentarer")
    (kom-review-tree	      . "Återse alla kommentarer rekursivt")
    (kom-review-clear         . "Återse hoppa")
    (kom-review-last-normally-read
     			      . "Återse igen")
    (kom-review-cross-references
     			      . "Återse alla korsreferenser")
    (kom-review-converted     . "Återse konverterat")
    (kom-review-noconversion  . "Återse omodifierat")
    (kom-review-rot13         . "Återse rot13")
    (kom-review-next          . "Återse nästa")
    (kom-find-root	      . "Återse urinlägget")
    (kom-review-by-to         . "Återse senaste")
    (kom-review-roots-by-to   . "Återse senaste urinlägg")
    (kom-review-more          . "Återse fler inlägg")
    (kom-review-first         . "Återse första")
    (kom-review-first-roots   . "Återse första urinlägg")
    (kom-review-all           . "Återse alla")
    (kom-review-all-roots     . "Återse alla urinlägg")
    (kom-view-commented-text  . "Återse det kommenterade")
    (kom-view-previous-commented-text
     			      . "Återse det föregående kommenterade")
    (kom-review-stack         . "Återse lista")
    (kom-review-presentation  . "Återse presentation")
    (kom-review-backward      . "(Återse) Baklänges")
    (kom-view-next-text	      . "Läsa nästa inlägg")
    (kom-who-is-on	      . "Vilka (är) inloggade") 
    (kom-who-is-on-in-conference
     			      . "Vilka (är inloggade i) möte") 
    (kom-who-is-present-in-conference
     			      . "Vilka (är) närvarande (i möte)")
    (kom-who-is-on-and-friend . "Vilka vänner (är inloggade)")
    (kom-who-am-i             . "Var (är) jag")
    (kom-list-clients	      . "Lista klienter")
    (kom-busy-wait	      . "Vänta på ett inlägg")
    (kom-write-comment	      . "Kommentera inlägg")
    (kom-comment-previous     . "Kommentera föregående inlägg")
    (kom-write-footnote       . "Fotnot till inlägg")
    (kom-private-answer       . "Personligt svar")
    (kom-private-answer-previous
     . "Personligt svar på föregående inlägg")
    (kom-set-unread	      . "Endast läsa senaste")
    (kom-write-text	      . "Skriva ett inlägg")
    (kom-send-letter	      . "Skicka brev")
    (kom-change-name	      . "Ändra namn")
    (kom-change-parenthesis   . "Ändra parentes")
    (kom-change-password      . "Ändra lösenord")
    (kom-change-supervisor    . "Ändra organisatör")
    (kom-change-presentation  . "Ändra presentation")
    (kom-get-appreciation     . "Få uppmuntran")
    (kom-get-abuse            . "Få skäll")
    (kom-mark-text            . "Markera (inlägg)")
    (kom-unmark-text          . "Avmarkera (inlägg)")
    (kom-review-marked-texts  . "Återse markerade")
    (kom-review-all-marked-texts . "Återse alla markerade")
    (kom-add-recipient        . "Addera mottagare")
    (kom-add-copy	      . "Addera extra kopiemottagare")
    (kom-add-bcc	      . "Addera dold kopia")
    (kom-sub-recipient        . "Subtrahera mottagare")
    (kom-move-text            . "Flytta inlägg")
    (kom-add-comment	      . "Addera kommentar")
    (kom-sub-comment	      . "Subtrahera kommentar")
    (kom-move-comment	      . "Flytta kommentar")
    (kom-add-cross-reference  . "Addera referens")
    (kom-add-member	      . "Addera medlem")
    (kom-sub-member           . "Uteslut medlem")
    (kom-change-conf-motd     . "Sätt lapp på dörren")
    (kom-set-garb-nice        . "Ändra livslängd")
    (kom-set-super-conf       . "Ändra supermöte")
    (kom-set-permitted-submitters . "Ändra tillåtna författare")
    (kom-unset-conf-motd      . "Ta bort lapp på dörren")
    (kom-save-text	      . "Arkivera inlägg (till fil)")
    (kom-save-text-body	      . "Spara inläggstext (på fil)")
    (kom-save-options         . "Spara inställningar")
    (kom-shutdown-server      . "Stäng av servern")
    (kom-sync-database        . "Spara databasen")
    (kom-enable-adm-caps      . "Övergå till administratörsmod")
    (kom-disable-adm-caps     . "Övergå till normalmod")
    (kom-set-motd             . "Sätt loginmeddelande")
    (kom-remove-motd          . "Ta bort loginmeddelande")
    (kom-force-logout         . "Kasta ut en session")
    (kom-filter-author        . "Filtrera författare")
    (kom-filter-subject       . "Filtrera ärende")
    (kom-filter-text          . "Filtrera innehåll")
    (kom-filter-recipient     . "Filtrera mottagare")
    (kom-super-jump           . "Superhoppa")
    (kom-filter-edit          . "Ändra filter")
    (kom-list-filters         . "Lista filter")
    (kom-show-user-area       . "Visa user-arean")
    (kom-delete-user-area     . "Ta bort user-arean")
    (kom-change-conf-type     . "Ändra mötestyp")

    (kom-change-auto-reply    . "Ändra svarsmeddelande")
    (kom-toggle-auto-reply    . "Automatsvar")
    (kom-list-messages        . "Lista meddelanden")
    (kom-erase-messages       . "Radera meddelanden")

    (kom-remote-autoreply     . "Fjärrkontrollera automatsvar")
    (kom-remote-set-message   . "Fjärrkontrollera ändra svarsmeddelande")
    (kom-remote-list-messages . "Fjärrkontrollera lista meddelanden")
    (kom-remote-erase-messages . "Fjärrkontrollera radera meddelanden")
    (kom-remote-quit          . "Fjärrkontrollera avsluta")

    (kom-status-session       . "Status (för) session")
    (kom-customize            . "Inställningar (för) LysKOM")
    (kom-next-kom             . "Nästa LysKOM")
    (kom-previous-kom         . "Föregående LysKOM")
    (kom-next-unread-kom      . "Nästa olästa LysKOM")
    (kom-change-language      . "Ändra språk")
    (kom-calculate            . "Beräkna")
    (kom-list-marks           . "Lista markeringar")
    (kom-where-is             . "Var finns kommandot")
    (kom-fast-reply           . "Anmärkning (till inlägg)")
    (kom-agree                . "Hålla med")
    (kom-add-faq              . "Addera FAQ")
    (kom-del-faq              . "Ta bort FAQ")
    (kom-review-faq           . "Återse FAQ")

    (kom-add-footnote	      . "Addera fotnot")
    (kom-sub-footnote	      . "Subtrahera fotnot")

    (kom-add-no-comments      . "Förhindra kommentarer")
    (kom-add-private-answer   . "Begär personligt svar")
    (kom-add-request-confirm  . "Begär läsbekräftelse")

    (kom-review-mail-headers  . "Återse brevhuvud")
    
    (kom-compare-texts        . "Jämför två texter")
    (kom-diff-texts           . "Se diff")

    (kom-view-url-in-text     . "Visa URL")

    (kom-become-anonymous     . "Bli anonym")
    (kom-become-nonanonymous  . "Bli icke-anonym (träd fram ur skuggorna)")

    (kom-keep-alive           . "Håll förbindelsen aktiv")
    (kom-stop-keep-alive      . "Avbryt håll förbindelsen aktiv")

    (kom-is-person-member-of-conference . "Kontrollera (om person är) medlem (i möte)")
    (kom-will-person-read-text . "Kontrollera (om person kommer att) läsa (inlägg)")

    (kom-change-conf-faq      . "Ändra FAQ")

    (kom-make-review-mark-as-read . "Återse läsmarkerar")
    (kom-make-review-not-mark-as-read . "Återse läsmarkerar inte")

    (kom-set-presentation     . "Addera presentation")
    (kom-remove-presentation  . "Ta bort presentation")
    (kom-set-motd-text        . "Addera lapp på dörren")
    (kom-create-aux-item      . "Skapa tilläggsinformation")
    (kom-status-server        . "Status (för) servern")
    (kom-add-server-faq       . "Addera server-FAQ")
    (kom-del-server-faq       . "Ta bort server-FAQ")
    (kom-review-server-faq    . "Återse server-FAQ")
    (kom-change-server-faq    . "Ändra server-FAQ")
    (kom-recommend-conference . "Rekommendera möte")
    (kom-redirect-comments    . "Dirigera om kommentarer")
    (kom-move-text-tree       . "Flytta träd")

    (kom-copy-options         . "Kopiera inställningar")
    (kom-mark-unread          . "Oläsmarkera inlägg")
    (kom-unread-by-to         . "Oläsmarkera senaste")
    (kom-unread-roots-by-to   . "Oläsmarkera senaste urinlägg")
    (kom-unread-more          . "Oläsmarkera fler inlägg")
    (kom-unread-commented-text . "Oläsmarkera det kommenterade")
    (kom-unread-previous-commented-text . "Oläsmarkera det föregående kommenterade")
    (kom-unread-comments      . "Oläsmarkera alla kommentarer")
    (kom-unread-tree          . "Oläsmarkera alla kommentarer rekursivt")
    (kom-unread-root          . "Oläsmarkera urinlägget")
    (kom-unread-root-review   . "Oläsmarkera träd")
    (kom-unread-last-normally-read
     			      . "Oläsmarkera igen")
    (kom-unread-all           . "Oläsmarkera alla")
    (kom-unread-all-roots     . "Oläsmarkera alla urinlägg")
    (kom-unread-first         . "Oläsmarkera första")
    (kom-unread-first-roots   . "Oläsmarkera första urinlägg")
    (kom-unread-presentation  . "Oläsmarkera presentation")
    (kom-unread-server-faq    . "Oläsmarkera server-FAQ")
    (kom-unread-faq           . "Oläsmarkera FAQ")
    (kom-unread-marked-texts  . "Oläsmarkera markerade")
    (kom-unread-all-marked-texts . "Oläsmarkera alla markerade")

    (kom-join-all-conferences . "Bli medlem i alla möten")
    (kom-leave-all-conferences . "Utträd ur (nästan) alla möten")
    (kom-limit-import         . "Begränsa import av texter")
    (kom-change-message-flag  . "Ändra mottagning (av) gruppmeddelanden")
    (kom-list-faqs            . "Lista FAQ")
    (kom-list-server-faqs     . "Lista server-FAQ")
    (kom-list-new-conferences . "Lista nya möten")
    (kom-list-new-persons     . "Lista nya personer")
    (kom-change-privileges    . "Ändra rättigheter")
    ))

(lyskom-language-var local lyskom-language-codes sv
  '((aa . "Afar")
    (ab . "Abkhasianska")
    (af . "Afrikaans")
    (am . "Amhariska")
    (ar . "Arabiska")
    (as . "Assamesiska")
    (ay . "Aymara")
    (az . "Azerbajanska")
    (ba . "Bashkiriska")
    (be . "Vitryska")
    (bg . "Bulgariska")
    (bh . "Bihariska")
    (bi . "Bislamska")
    (bn . "Bengaliska")
    (bo . "Tibetanska")
    (br . "Bretangneska")
    (ca . "Katalanska")
    (co . "Korsikanska")
    (cs . "Tjeckiska")
    (cy . "Walesiska")
    (da . "Danska")
    (de . "Tyska")
    (dz . "Bhutanska")
    (el . "Grekiska")
    (en . "Engelska")
    (eo . "Esperanto")
    (es . "Spanska")
    (et . "Estniska")
    (eu . "Baskiska")
    (fa . "Persiska")
    (fi . "Finska")
    (fj . "Fiji")
    (fo . "Faröiska")
    (fr . "Franska")
    (fy . "Frisiska")
    (ga . "Irländska")
    (gd . "Skotsk Gäliska")
    (gl . "Galiciska")
    (gn . "Guarani")
    (gu . "Gujaratiska")
    (ha . "Hausa")
    (he . "Hebreiska")
    (hi . "Hindi")
    (hr . "Kroatiska")
    (hu . "Ungerska")
    (hy . "Armeniska")
    (ia . "Interlingua")
    (id . "Indonesiska")
    (ie . "Interlingue")
    (ik . "Inupiak")
    (is . "Isländska")
    (it . "Italienska")
    (iu . "Inuktitut")
    (ja . "Japanska")
    (jw . "Javanesiska")
    (ka . "Georgiska")
    (kk . "Kazakhstanska")
    (kl . "Grönländska")
    (km . "Kambodianska")
    (kn . "Kannada")
    (ko . "Koreanska")
    (ks . "Kashmiriska")
    (ku . "Kurdiska")
    (ky . "Kirghiz")
    (la . "Latinska")
    (ln . "Lingala")
    (lo . "Laotesiska")
    (lt . "Litauiska")
    (lv . "Lettiska")
    (mg . "Malagasiska")
    (mi . "Maori")
    (mk . "Makedonska")
    (ml . "Malayalam")
    (mn . "Mongolska")
    (mo . "Moldaviska")
    (mr . "Marathi")
    (ms . "Malaysiska")
    (mt . "Maltesiska")
    (my . "Burmesiska")
    (na . "Nauruiska")
    (ne . "Nepalska")
    (nl . "Holländska")
    (no . "Norska")
    (oc . "Occitanska")
    (om . "Oromo")
    (or . "Oriya")
    (pa . "Pundjabiska")
    (pl . "Polska")
    (ps . "Pashtu")
    (pt . "Protugisiska")
    (qu . "Quechua")
    (rm . "Rhätoromanska")
    (rn . "Kirundiska")
    (ro . "Rumänska")
    (ru . "Ryska")
    (rw . "Kiyarwanda")
    (sa . "Sanskrit")
    (sd . "Sindhi")
    (sg . "Sangho")
    (sh . "Serbokroatiska")
    (si . "Singhalesiska")
    (sk . "Slovakiska")
    (sl . "Slovenska")
    (sm . "Samoanska")
    (sn . "Shoniska")
    (so . "Somaliska")
    (sq . "Albanska")
    (sr . "Serbiska")
    (ss . "Siswatiska")
    (st . "Sesothiska")
    (su . "Sudanesiska")
    (sv . "Svenska")
    (sw . "Swahili")
    (ta . "Tamilska")
    (te . "Telugu")
    (tg . "Tajikiska")
    (th . "Thailändska")
    (ti . "Tigrinya")
    (tk . "Turkmenistanska")
    (tl . "Tagalog")
    (tn . "Sichuanska")
    (to . "Tongiska")
    (tr . "Turkiska")
    (ts . "Tsongiska")
    (tt . "Tatariska")
    (tw . "Twi")
    (ug . "Uiguriska")
    (uk . "Ukrainska")
    (ur . "Urdu")
    (uz . "Uzbekistanska")
    (vi . "Vietnamesiska")
    (vo . "Volapük")
    (wo . "Wolof")
    (xh . "Xhosa")
    (yi . "Yiddish")
    (yo . "Yorouba")
    (za . "Zhuang")
    (zh . "Kinesiska")
    (zu . "Zulu")
    (-- . "Okänt språk (%#1s)")))


(lyskom-language-ending-mismatch lyskom-menu language sv en)
(lyskom-language-ending-mismatch lyskom-menu kom-change-language sv en)
(lyskom-language-ending-mismatch lyskom-menu kom-change-global-language sv en)
(lyskom-language-ending-mismatch lyskom-menu kom-change-local-language sv en)

(lyskom-language-strings global lyskom-menu sv
  '((lyskom    . "LysKOM")
    (read      . "Läs")
    (dont-read . "Hoppa")
    (write     . "Skriv")
    (conference . "Möte")
    (person    . "Person")
    (server    . "Server")
    (other     . "Annat")
    (move      . "Gå")
    (info      . "Om")
    (send      . "Sänd")
    (unread    . "Markera oläst")
    (review    . "Återse inlägg")
    (marks     . "Markerade inlägg")
    (filters   . "Filter")
    (receivers . "Mottagare")
    (commented . "Kommenterar")
    (aux-items . "Tilläggsinformation")
    (conf-admin . "Mötesadministration")
    (server-admin . "Serveradministration")
    (membership . "Medlemskap")
    (pers-admin . "Personadministration")
    (autoreply . "Automatsvar")
    (remote-control . "Fjärrstyrning")
    (language . "Ändra språk (Change Language)")
    (kom-help . "Hjälp (Help)")
    (kom-change-language . "Ändra språk (Change language)")
    (kom-change-global-language . "Visning och kommandon (display and commands)")
    (kom-change-local-language . "Endast visning (display language only)")
    (kom-edit-send . "Skicka in")
    (kom-edit-send-anonymous . "Skicka anonymt")
    (kom-edit-quit . "Kasta bort") 
    (kom-ispell-message . "Stavningskontroll")
    (kom-edit-add-recipient . "Addera mottagare")
    (kom-edit-add-copy . "Addera extra kopiemottagare")
    (kom-edit-show-commented . "Återse det kommenterade")
    (kom-edit-insert-commented . "Citera det kommenterade")
    (kom-edit-insert-buglist . "Klistra in kommenterad buglista")
    (kom-edit-add-bcc . "Addera dold kopia")
    (kom-edit-add-cross-reference . "Addera korsreferens")
    (kom-edit-add-no-comments . "Begär inga kommentarer")
    (kom-edit-add-personal-comments . "Begär personliga svar")
    (kom-edit-add-read-confirm-request . "Begär läsbekräftelse")
    (kom-edit-move-text . "Flytta till en ny mottagare")

    (lp--toggle-membership-selection . "Växla markering på medlemskap")
    (lp--select-region . "Markera område")
    (lp--select-priority . "Markera prioritet")
    (lp--deselect-priority . "Avmarkera prioritet")
    (lp--deselect-all . "Avmarkera alla")
    (lp--membership-expansion . "Expandera medlemskap")
    (lp--toggle-entry-expansion . "Växla expansion")
    (lp--expand-entry . "Expandera medlemskap")
    (lp--contract-entry . "Avexpandera medlemskap")
    (lp--membership-priority . "Prioritet")
    (lp--set-priority . "Ändra prioritet")
    (lp--move-up . "Flytta uppåt")
    (lp--move-down . "Flytta nedåt")
    (lp--increase-priority . "Öka prioritet")
    (lp--decrease-priority . "Minska prioritet")
    (lp--yank . "Flytta markerade")
    (lp--filter . "Urval")
    (lp--show-hide-memberships-read-before . "Växla visning av medlemskap lästa före...")
    (lp--show-hide-memberships-read-after . "Växla visning av medlemskap lästa efter...")
    (lp--show-hide-read-memberships . "Växla visning av medlemskap utan olästa")
    (lp--show-hide-passive-memberships . "Växla visning av passiva medlemskap")
    (lp--show-all . "Visa alla medlemskap")
    (lp--membership-type . "Medlemskapstyp")
    (lp--toggle-invitation . "Växla inbjudan")
    (lp--toggle-passive . "Växla passiv")
    (lp--toggle-message-flag . "Växla meddelanden")
    (lp--toggle-secret . "Växla hemlig")
    (lp--set-unread . "Endast")
    (lp--quit . "Avsluta")
))

(lyskom-language-var local lyskom-onoff-table sv
  '(("på" . on) ("av" . off)))

(lyskom-language-var local lyskom-filter-predicate-list sv
      '(("=" . nil) ("!=" . t)))

(lyskom-language-var local lyskom-filter-what sv
      '((author . "Författare")
        (author-no . "Författare (nummer)")
        (author-re . "Författare (regexp)")
        (subject . "Ärende")
        (subject-re . "Ärende (regexp)")
        (recipient . "Mottagare")
        (recipient-no . "Mottagare (nummer)")
        (recipient-re . "Mottagare (regexp)")
        (text . "Innehåll")
        (text-re . "Innehåll (regexp)")))

(lyskom-language-var local lyskom-filter-actions sv
      '((skip-text . "Hoppa över")
        (dontshow . "Visa inte")
        (read . "Undanta från filtrering")
        (skip-tree . "Hoppa över kommentarer")
	(obliterate . "Utplåna")))

(defconst lyskom-keybindings-missing nil)

(defvar lyskom-sv-mode-map nil)
(lyskom-language-keymap lyskom-mode-map sv lyskom-sv-mode-map)

(defvar lyskom-sv-review-prefix)
(defvar lyskom-sv-change-prefix)
(defvar lyskom-sv-next-prefix)
(defvar lyskom-sv-list-prefix)
(defvar lyskom-sv-S-prefix)
(defvar lyskom-sv-fast-reply-prefix)
(defvar lyskom-sv-filter-get-prefix)
(defvar lyskom-sv-who-prefix)
(defvar lyskom-sv-A-prefix)
(defvar lyskom-sv-unread-prefix)

(if lyskom-sv-mode-map
    nil
  (setq lyskom-sv-mode-map (make-keymap))
  (suppress-keymap lyskom-sv-mode-map)
  (define-prefix-command 'lyskom-sv-review-prefix)
  (define-prefix-command 'lyskom-sv-change-prefix)
  (define-prefix-command 'lyskom-sv-next-prefix)
  (define-prefix-command 'lyskom-sv-list-prefix)
  (define-prefix-command 'lyskom-sv-S-prefix)
  (define-prefix-command 'lyskom-sv-fast-reply-prefix)
  (define-prefix-command 'lyskom-sv-filter-get-prefix)
  (define-prefix-command 'lyskom-sv-who-prefix)
  (define-prefix-command 'lyskom-sv-A-prefix)
  (define-prefix-command 'lyskom-sv-unread-prefix)

  (define-key lyskom-sv-mode-map (kbd "f") 'lyskom-sv-filter-get-prefix)
  (define-key lyskom-sv-mode-map (kbd "n") 'lyskom-sv-next-prefix)
  (define-key lyskom-sv-mode-map (kbd "l") 'lyskom-sv-list-prefix)
  (define-key lyskom-sv-mode-map (kbd "s") 'lyskom-sv-S-prefix)
  (define-key lyskom-sv-mode-map (kbd "r") 'lyskom-sv-fast-reply-prefix)
  (define-key lyskom-sv-mode-map (kbd "v") 'lyskom-sv-who-prefix)
  (define-key lyskom-sv-mode-map (kbd "a") 'lyskom-sv-A-prefix)
  (define-key lyskom-sv-mode-map (kbd "o") 'lyskom-sv-unread-prefix)

  (lyskom-try-define-key lyskom-sv-mode-map (kbd "ä")    'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map (kbd "{")    'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map (kbd "[")    'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [adiaeresis] 'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [Adiaeresis] 'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [ä]          'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [Ä]          'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [228]        'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [196]        'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [2276]       'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [2244]       'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [3908]       'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [3940]       'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [195 164]    'lyskom-sv-change-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [195 132]    'lyskom-sv-change-prefix)

  (lyskom-try-define-key lyskom-sv-mode-map (kbd "å") 'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map (kbd "}") 'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map (kbd "]") 'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [aring]     'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [Aring]     'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [å]       'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [Å]       'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [229]     'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [197]     'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [2277]    'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [2245]    'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [3909]    'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [3941]    'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [195 165] 'lyskom-sv-review-prefix)
  (lyskom-try-define-key lyskom-sv-mode-map [195 133] 'lyskom-sv-review-prefix)

  ; XEmacs 21.4 UTF-8 on terminal compatibility
  (lyskom-try-define-key lyskom-sv-mode-map [(Ã)]      (let ((k (make-sparse-keymap)))
                                                           (define-key k (vector (intern "\205")) 'lyskom-sv-review-prefix)
                                                           (define-key k [¥] 'lyskom-sv-review-prefix)
                                                           (define-key k (vector (intern "\204")) 'lyskom-sv-change-prefix)
                                                           (define-key k [(¤)] 'lyskom-sv-change-prefix)
							   k))

  (define-key lyskom-sv-mode-map (kbd "M-m") 'kom-toggle-mark-as-read-prefix)
  (define-key lyskom-sv-mode-map (kbd "M-c") 'kom-toggle-cache-prefix)
  (define-key lyskom-sv-mode-map (kbd "M-u") 'kom-toggle-topic-prefix)

  (define-key lyskom-sv-mode-map (kbd (lyskom-keys 'button2)) 'kom-mouse-null)
  (define-key lyskom-sv-mode-map (kbd (lyskom-keys 'button2up)) 'kom-button-click)
  (define-key lyskom-sv-mode-map (kbd (lyskom-keys 'button3))   'kom-popup-menu)
  (define-key lyskom-sv-mode-map (kbd (lyskom-keys 'button3up)) 'kom-mouse-null)
  (define-key lyskom-sv-mode-map (kbd "*")     'kom-button-press)
  (define-key lyskom-sv-mode-map (kbd "=")     'kom-menu-button-press)
  (define-key lyskom-sv-mode-map (kbd "TAB")   'kom-next-link)
  (define-key lyskom-sv-mode-map (kbd "M-TAB") 'kom-previous-link)
  (define-key lyskom-sv-mode-map (kbd "<S-tab>") 'kom-previous-link)
  (define-key lyskom-sv-mode-map (kbd "C-i")   'kom-next-link)
  (define-key lyskom-sv-mode-map (kbd "M-C-i") 'kom-previous-link)

  ;; These should be first in order to be last in the menu of alternatives.
  (define-key lyskom-sv-change-prefix     (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-review-prefix     (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-unread-prefix     (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-filter-get-prefix (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-next-prefix       (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-list-prefix       (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-S-prefix          (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-who-prefix        (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-fast-reply-prefix (kbd "?") 'lyskom-help)
  (define-key lyskom-sv-A-prefix          (kbd "?") 'lyskom-help)
  

  (define-key lyskom-sv-mode-map (kbd "e")   'kom-set-unread)
;;  (define-key lyskom-sv-mode-map (kbd "a")   'kom-obsolete-extended-command-binding)
;;  (define-key lyskom-sv-mode-map (kbd "l v") 'kom-obsolete-who-is-on-in-conference)
  (define-key lyskom-sv-mode-map (kbd "x")   'kom-extended-command)
  (define-key lyskom-sv-mode-map (kbd "<SPC>") 'kom-next-command)
  (define-key lyskom-sv-mode-map (kbd "<RET>") 'kom-line-next-command)
  (define-key lyskom-sv-mode-map (kbd "<LFD>") 'kom-page-next-command)
  (define-key lyskom-sv-mode-map (kbd "C-j") 'kom-page-next-command)
  (define-key lyskom-sv-mode-map (kbd "j")   'kom-page-next-command)
  (define-key lyskom-sv-mode-map (kbd "C-m") 'kom-line-next-command)

  (define-key lyskom-sv-mode-map (kbd "?")   'kom-help)
  (define-key lyskom-sv-mode-map (kbd "b")   'kom-send-letter)
  (define-key lyskom-sv-mode-map (kbd "g")   'kom-go-to-conf)
  (define-key lyskom-sv-mode-map (kbd "i")   'kom-write-text)
  (define-key lyskom-sv-mode-map (kbd "k")   'kom-write-comment)
  (define-key lyskom-sv-mode-map (kbd "K")   'kom-comment-previous)
  (define-key lyskom-sv-mode-map (kbd "F")   'kom-write-footnote)
  (define-key lyskom-sv-mode-map (kbd "p")   'kom-private-answer)
  (define-key lyskom-sv-mode-map (kbd "P")   'kom-private-answer-previous)
  (define-key lyskom-sv-mode-map (kbd "h")   'kom-jump)
  (define-key lyskom-sv-mode-map (kbd "H")   'kom-super-jump)
  (define-key lyskom-sv-mode-map (kbd "m")   'kom-add-self)
  (define-key lyskom-sv-mode-map (kbd "M")   'kom-mark-text)
  (define-key lyskom-sv-mode-map (kbd "A")   'kom-unmark-text)
  (define-key lyskom-sv-mode-map (kbd "S")   'kom-quit)
  (define-key lyskom-sv-mode-map (kbd "q")   'kom-quit)
  (define-key lyskom-sv-mode-map (kbd "z")   'kom-bury)
  (define-key lyskom-sv-mode-map (kbd "R")   'kom-recover)
  (define-key lyskom-sv-mode-map (kbd "t")   'kom-display-time)
  (define-key lyskom-sv-mode-map (kbd "J")   'kom-who-am-i)
  (define-key lyskom-sv-mode-map (kbd "V")   'kom-busy-wait)

  (define-key lyskom-sv-next-prefix (kbd "i") 'kom-view-next-new-text)
  (define-key lyskom-sv-next-prefix (kbd "m") 'kom-go-to-next-conf)
  (define-key lyskom-sv-next-prefix (kbd "l") 'kom-next-kom)
  (define-key lyskom-sv-next-prefix (kbd "o") 'kom-next-unread-kom)

  (define-key lyskom-sv-list-prefix (kbd "k") 'kom-list-sessions)
  (define-key lyskom-sv-list-prefix (kbd "M") 'kom-list-marks)
  (define-key lyskom-sv-list-prefix (kbd "m") 'kom-list-conferences)
  (define-key lyskom-sv-list-prefix (kbd "n") 'kom-list-news)
  (define-key lyskom-sv-list-prefix (kbd "p") 'kom-list-persons)
  (define-key lyskom-sv-list-prefix (kbd "r") 'kom-list-re)
  (define-key lyskom-sv-list-prefix (kbd "s") 'kom-membership)
  (define-key lyskom-sv-list-prefix (kbd "f") 'kom-list-filters)
  (define-key lyskom-sv-list-prefix (kbd "q") 'kom-list-faqs)
  (define-key lyskom-sv-list-prefix (kbd "Q") 'kom-list-server-faqs)

  (lyskom-try-define-key lyskom-sv-list-prefix (kbd "ä") 'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix (kbd "{") 'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix (kbd "[") 'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [adiaeresis]     'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [Adiaeresis]     'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [ä]       'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [Ä]       'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [228]     'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [196]     'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [2276]    'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [2244]    'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [3908]    'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [3940]    'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [195 164] 'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [195 132] 'kom-list-summary)
  (lyskom-try-define-key lyskom-sv-list-prefix [(Ã)]        (let ((k (make-sparse-keymap)))
                                                           (define-key k (vector (intern "\204")) 'kom-list-summary)
                                                           (define-key k [¤] 'kom-list-summary)
							   k))

  (define-key lyskom-sv-filter-get-prefix (kbd "u") 'kom-get-appreciation)
  (define-key lyskom-sv-filter-get-prefix (kbd "s") 'kom-get-abuse)
  (define-key lyskom-sv-filter-get-prefix (kbd "t") 'kom-move-text)
  (define-key lyskom-sv-filter-get-prefix (kbd "T") 'kom-move-text-tree)
  (define-key lyskom-sv-filter-get-prefix (kbd "f") 'kom-filter-author)
  (define-key lyskom-sv-filter-get-prefix (kbd "i") 'kom-filter-text)
  (define-key lyskom-sv-filter-get-prefix (kbd "m") 'kom-filter-recipient)
  (define-key lyskom-sv-filter-get-prefix (kbd "l") 'kom-previous-kom)
  (define-key lyskom-sv-filter-get-prefix (kbd "k") 'kom-move-comment)

  (lyskom-try-define-key lyskom-sv-filter-get-prefix (kbd "ä")    'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix (kbd "{")    'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix (kbd "[")    'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [adiaeresis] 'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [Adiaeresis] 'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [ä]          'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [Ä]          'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [228]        'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [196]        'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [2276]       'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [2244]       'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [3908]       'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [3940]       'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [195 164]    'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [195 132]    'kom-filter-subject)
  (lyskom-try-define-key lyskom-sv-filter-get-prefix [(Ã)]        (let ((k (make-sparse-keymap)))
                                                           (define-key k (vector (intern "\204")) 'kom-filter-subject)
                                                           (define-key k [¤] 'kom-filter-subject)
							   k))

  (define-key lyskom-sv-who-prefix (kbd "i") 'kom-who-is-on)
  (define-key lyskom-sv-who-prefix (kbd "m") 'kom-who-is-on-in-conference)
  (define-key lyskom-sv-who-prefix (kbd "n") 'kom-who-is-present-in-conference)
  (define-key lyskom-sv-who-prefix (kbd "u") 'kom-view-url-in-text)
  (define-key lyskom-sv-who-prefix (kbd "v") 'kom-who-is-on-and-friend)

  (define-key lyskom-sv-change-prefix (kbd "p") 'kom-change-presentation)
  (define-key lyskom-sv-change-prefix (kbd "q") 'kom-change-conf-faq)
  (define-key lyskom-sv-change-prefix (kbd "f") 'kom-filter-edit)
  (define-key lyskom-sv-change-prefix (kbd "m") 'kom-change-auto-reply)

  (define-key lyskom-sv-unread-prefix (kbd "SPC") 'kom-mark-unread)
  (define-key lyskom-sv-unread-prefix (kbd "0") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "1") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "2") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "3") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "4") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "5") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "6") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "7") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "8") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "9") 'kom-initial-digit-unread)
  (define-key lyskom-sv-unread-prefix (kbd "k") 'kom-unread-commented-text)
  (define-key lyskom-sv-unread-prefix (kbd "K") 'kom-unread-previous-commented-text)
  (define-key lyskom-sv-unread-prefix (kbd "a ?") 'lyskom-help)
  (define-key lyskom-sv-unread-prefix (kbd "a k") 'kom-unread-comments)
  (define-key lyskom-sv-unread-prefix (kbd "a r") 'kom-unread-tree)
  (define-key lyskom-sv-unread-prefix (kbd "i") 'kom-unread-last-normally-read)
  (define-key lyskom-sv-unread-prefix (kbd "r") 'kom-unread-root)
  (define-key lyskom-sv-unread-prefix (kbd "u") 'kom-unread-root)
  (define-key lyskom-sv-unread-prefix (kbd "s") 'kom-unread-by-to)
  (define-key lyskom-sv-unread-prefix (kbd "S") 'kom-unread-roots-by-to)
  (define-key lyskom-sv-unread-prefix (kbd "y") 'kom-unread-more)
  (define-key lyskom-sv-unread-prefix (kbd "t") 'kom-unread-root-review)
  (define-key lyskom-sv-unread-prefix (kbd "A") 'kom-unread-all-roots)
  (define-key lyskom-sv-unread-prefix (kbd "f") 'kom-unread-first)
  (define-key lyskom-sv-unread-prefix (kbd "F") 'kom-unread-first-roots)
  (define-key lyskom-sv-unread-prefix (kbd "p") 'kom-unread-presentation)
  (define-key lyskom-sv-unread-prefix (kbd "q") 'kom-unread-faq)
  (define-key lyskom-sv-unread-prefix (kbd "Q") 'kom-unread-server-faq)
  (define-key lyskom-sv-unread-prefix (kbd "m") 'kom-unread-marked-texts)
  (define-key lyskom-sv-unread-prefix (kbd "a m") 'kom-unread-all-marked-texts)
  (define-key lyskom-sv-unread-prefix (kbd "a SPC") 'kom-unread-all)

  (define-key lyskom-sv-review-prefix (kbd "SPC") 'kom-view)
  (define-key lyskom-sv-review-prefix (kbd "0") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "1") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "2") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "3") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "4") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "5") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "6") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "7") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "8") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "9") 'kom-initial-digit-view)
  (define-key lyskom-sv-review-prefix (kbd "k") 'kom-view-commented-text)
  (define-key lyskom-sv-review-prefix (kbd "K") 'kom-view-previous-commented-text)
  (define-key lyskom-sv-review-prefix (kbd "a ?") 'lyskom-help)
  (define-key lyskom-sv-review-prefix (kbd "a k") 'kom-review-comments)
  (define-key lyskom-sv-review-prefix (kbd "a y") 'kom-review-more-comments)
  (define-key lyskom-sv-review-prefix (kbd "a r") 'kom-review-tree)
  (define-key lyskom-sv-review-prefix (kbd "a x") 'kom-review-cross-references)
  (define-key lyskom-sv-review-prefix (kbd "h") 'kom-review-clear)
  (define-key lyskom-sv-review-prefix (kbd "i") 'kom-review-last-normally-read)
  (define-key lyskom-sv-review-prefix (kbd "n") 'kom-review-next)
  (define-key lyskom-sv-review-prefix (kbd "o") 'kom-review-noconversion)
  (define-key lyskom-sv-review-prefix (kbd "v") 'kom-review-converted)
  (define-key lyskom-sv-review-prefix (kbd "3") 'kom-review-rot13)
  (define-key lyskom-sv-review-prefix (kbd "r") 'kom-find-root)
  (define-key lyskom-sv-review-prefix (kbd "u") 'kom-find-root)
  (define-key lyskom-sv-review-prefix (kbd "s") 'kom-review-by-to)
  (define-key lyskom-sv-review-prefix (kbd "S") 'kom-review-roots-by-to)
  (define-key lyskom-sv-review-prefix (kbd "y") 'kom-review-more)
  (define-key lyskom-sv-review-prefix (kbd "A") 'kom-review-all-roots)
  (define-key lyskom-sv-review-prefix (kbd "f") 'kom-review-first)
  (define-key lyskom-sv-review-prefix (kbd "F") 'kom-review-first-roots)
  (define-key lyskom-sv-review-prefix (kbd "l") 'kom-review-stack)
  (define-key lyskom-sv-review-prefix (kbd "p") 'kom-review-presentation)
  (define-key lyskom-sv-review-prefix (kbd "t") 'kom-find-root-review)
  (define-key lyskom-sv-review-prefix (kbd "m") 'kom-review-marked-texts)
  (define-key lyskom-sv-review-prefix (kbd "q") 'kom-review-faq)
  (define-key lyskom-sv-review-prefix (kbd "Q") 'kom-review-server-faq)
  (define-key lyskom-sv-review-prefix (kbd "a m") 'kom-review-all-marked-texts)
  (define-key lyskom-sv-review-prefix (kbd "a SPC") 'kom-review-all)
  (define-key lyskom-sv-review-prefix (kbd "b") 'kom-review-mail-headers)
  (define-key lyskom-sv-review-prefix (kbd "B")   'kom-review-backward)

  (define-key lyskom-sv-S-prefix (kbd "m") 'kom-status-conf)
  (define-key lyskom-sv-S-prefix (kbd "p") 'kom-status-person)
  (define-key lyskom-sv-S-prefix (kbd "s") 'kom-status-session)
  (define-key lyskom-sv-S-prefix (kbd "k") 'kom-status-server)
  (define-key lyskom-sv-S-prefix (kbd "u ?") 'lyskom-help)
  (define-key lyskom-sv-S-prefix (kbd "u m") 'kom-sub-recipient)
  (define-key lyskom-sv-S-prefix (kbd "u k") 'kom-sub-comment)
  (define-key lyskom-sv-S-prefix (kbd "u q") 'kom-del-faq)
  (define-key lyskom-sv-S-prefix (kbd "u f") 'kom-sub-footnote)
  (define-key lyskom-sv-S-prefix (kbd "t") 'kom-save-text)

  (lyskom-try-define-key lyskom-sv-S-prefix (kbd "ä")    'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix (kbd "{")    'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix (kbd "[")    'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [adiaeresis] 'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [Adiaeresis] 'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [ä]          'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [Ä]          'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [228]        'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [196]        'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [2276]       'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [2244]       'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [3908]       'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [3940]       'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [195 164]    'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [195 132]    'kom-send-message)
  (lyskom-try-define-key lyskom-sv-S-prefix [(Ã)]        (let ((k (make-sparse-keymap)))
                                                           (define-key k (vector (intern "\204")) 'kom-send-message)
                                                           (define-key k [¤] 'kom-send-message)
							   k))

  (define-key lyskom-sv-A-prefix (kbd "m") 'kom-add-recipient)
  (define-key lyskom-sv-A-prefix (kbd "e") 'kom-add-copy)
  (define-key lyskom-sv-A-prefix (kbd "d") 'kom-add-bcc)
  (define-key lyskom-sv-A-prefix (kbd "k") 'kom-add-comment)
  (define-key lyskom-sv-A-prefix (kbd "r") 'kom-add-cross-reference)
  (define-key lyskom-sv-A-prefix (kbd "q") 'kom-add-faq)
  (define-key lyskom-sv-A-prefix (kbd "f") 'kom-add-footnote)
  (define-key lyskom-sv-A-prefix (kbd "Q") 'kom-add-server-faq)

  (define-key lyskom-sv-fast-reply-prefix (kbd "a") 'kom-fast-reply)
  (define-key lyskom-sv-fast-reply-prefix (kbd "r") 'kom-fast-reply)
  (define-key lyskom-sv-fast-reply-prefix (kbd "h") 'kom-agree)

  ;; Running in buffer

  (define-key lyskom-sv-mode-map (kbd "M-p") 'kom-backward-text)
  (define-key lyskom-sv-mode-map (kbd "M-n") 'kom-forward-text)
  (define-key lyskom-sv-mode-map (kbd "C-M-p") 'kom-prev-prompt)
  (define-key lyskom-sv-mode-map (kbd "C-M-n") 'kom-next-prompt)

  (define-key lyskom-sv-mode-map (kbd "C-?") 'scroll-down)
  (define-key lyskom-sv-mode-map (kbd "<DEL>") 'scroll-down)
  (define-key lyskom-sv-mode-map (kbd "<delete>") 'scroll-down)
  (define-key lyskom-sv-mode-map (kbd "<BS>") 'scroll-down)

  (define-key lyskom-sv-mode-map (kbd "<") 'beginning-of-buffer)
  (define-key lyskom-sv-mode-map (kbd ">") 'end-of-buffer)
  )


;;; ================================================================
;;; Prioritization

(defvar lyskom-sv-prioritize-mode-map nil "Keymap used in lyskom-prioritize-mode")
(lyskom-language-keymap lyskom-prioritize-mode-map sv lyskom-sv-prioritize-mode-map)

(if lyskom-sv-prioritize-mode-map
    nil
  (setq lyskom-sv-prioritize-mode-map (make-keymap))
  (suppress-keymap lyskom-sv-prioritize-mode-map)
  (define-key lyskom-sv-prioritize-mode-map (kbd "SPC") 'lp--toggle-membership-selection)
  (define-key lyskom-sv-prioritize-mode-map (kbd "C-k") 'lp--toggle-membership-selection)
  (define-key lyskom-sv-prioritize-mode-map (kbd "p")   'lp--set-priority)
  (define-key lyskom-sv-prioritize-mode-map (kbd "C-w") 'lp--select-region)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-w") 'lp--select-region)
  (define-key lyskom-sv-prioritize-mode-map (kbd "C-y") 'lp--yank)
  (define-key lyskom-sv-prioritize-mode-map (kbd "#")   'lp--select-priority)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-#")   'lp--deselect-priority)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-DEL") 'lp--deselect-all)
  (define-key lyskom-sv-prioritize-mode-map (kbd "C-p") 'lp--previous-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd "<up>") 'lp--previous-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd "C-n") 'lp--next-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd "<down>") 'lp--next-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd "<home>") 'lp--first-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd "<end>") 'lp--last-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-<") 'lp--first-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M->") 'lp--last-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd "g") 'lp--goto-priority)
  (define-key lyskom-sv-prioritize-mode-map (kbd "RET") 'lp--toggle-entry-expansion)
  (define-key lyskom-sv-prioritize-mode-map (kbd "+") 'lp--increase-priority)
  (define-key lyskom-sv-prioritize-mode-map (kbd "-") 'lp--decrease-priority)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-p") 'lp--move-up)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-n") 'lp--move-down)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-<up>") 'lp--move-up)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-<down>") 'lp--move-down)
  (define-key lyskom-sv-prioritize-mode-map (kbd "I") 'lp--toggle-invitation)
  (define-key lyskom-sv-prioritize-mode-map (kbd "H") 'lp--toggle-secret)
  (define-key lyskom-sv-prioritize-mode-map (kbd "P") 'lp--toggle-passive)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M") 'lp--toggle-message-flag)
  (define-key lyskom-sv-prioritize-mode-map (kbd "e") 'lp--set-unread)
  (define-key lyskom-sv-prioritize-mode-map (kbd "u") 'lp--postpone)
  (define-key lyskom-sv-prioritize-mode-map (kbd "C-c C-c") 'lp--quit)
  (define-key lyskom-sv-prioritize-mode-map (kbd "q") 'lp--quit)
  (define-key lyskom-sv-prioritize-mode-map (kbd "(") 'lp--expand-entry)
  (define-key lyskom-sv-prioritize-mode-map (kbd ")") 'lp--contract-entry)

  (define-key lyskom-sv-prioritize-mode-map (kbd (lyskom-keys 'button2up)) 'kom-button-click)
  (define-key lyskom-sv-prioritize-mode-map (kbd (lyskom-keys 'button2)) 'kom-mouse-null)
  (define-key lyskom-sv-prioritize-mode-map (kbd (lyskom-keys 'button3)) 'kom-popup-menu)
  (define-key lyskom-sv-prioritize-mode-map (kbd (lyskom-keys 'button3up)) 'kom-mouse-null)
  (define-key lyskom-sv-prioritize-mode-map (kbd "*") 'kom-button-press)
  (define-key lyskom-sv-prioritize-mode-map (kbd "=") 'kom-menu-button-press)
  (define-key lyskom-sv-prioritize-mode-map (kbd "TAB") 'kom-next-link)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-TAB") 'kom-previous-link)
  (define-key lyskom-sv-prioritize-mode-map (kbd "C-i") 'kom-next-link)
  (define-key lyskom-sv-prioritize-mode-map (kbd "M-C-i") 'kom-previous-link)
  )



;;; ==============================================================
;;; Keymap for filter editing
;;;

(defvar lyskom-sv-filter-edit-map nil)
(lyskom-language-keymap lyskom-filter-edit-map sv lyskom-sv-filter-edit-map)

(if lyskom-sv-filter-edit-map ()
  (setq lyskom-sv-filter-edit-map (make-keymap))
  (suppress-keymap lyskom-sv-filter-edit-map)
  (define-prefix-command 'lyskom-sv-filter-edit-prefix)
  (define-key lyskom-sv-filter-edit-map (kbd "C-c") 'lyskom-sv-filter-edit-prefix)
  (define-key lyskom-sv-filter-edit-map (kbd "p")   'lyskom-filter-edit-prev-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "P")   'lyskom-filter-edit-prev-entry)
  (define-key lyskom-sv-filter-edit-map (kbd "n")   'lyskom-filter-edit-next-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "N")   'lyskom-filter-edit-next-entry)
  (define-key lyskom-sv-filter-edit-map (kbd "C-p") 'lyskom-filter-edit-prev-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "C-n") 'lyskom-filter-edit-next-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "C-b") 'lyskom-filter-edit-prev-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "C-f") 'lyskom-filter-edit-next-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "M-p") 'lyskom-filter-edit-prev-entry)
  (define-key lyskom-sv-filter-edit-map (kbd "M-n") 'lyskom-filter-edit-next-entry)
  (define-key lyskom-sv-filter-edit-map (kbd "d")   'lyskom-filter-edit-delete-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "M-d") 'lyskom-filter-edit-delete-entry)
  (define-key lyskom-sv-filter-edit-map (kbd "D")   'lyskom-filter-edit-delete-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "C-d") 'lyskom-filter-edit-delete-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "i")   'lyskom-filter-edit-insert-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "I")   'lyskom-filter-edit-insert-pattern)
  (define-key lyskom-sv-filter-edit-map (kbd "M-i") 'lyskom-filter-edit-insert-entry)
  (define-key lyskom-sv-filter-edit-map (kbd "<")   'lyskom-filter-edit-beginning-of-list)
  (define-key lyskom-sv-filter-edit-map (kbd ">")   'lyskom-filter-edit-end-of-list)
  (define-key lyskom-sv-filter-edit-map (kbd "M-<") 'lyskom-filter-edit-beginning-of-list)
  (define-key lyskom-sv-filter-edit-map (kbd "M->") 'lyskom-filter-edit-end-of-list)
  (define-key lyskom-sv-filter-edit-map (kbd "q")   'lyskom-filter-edit-quit)
  (define-key lyskom-sv-filter-edit-map (kbd "C-c C-c")   'lyskom-filter-edit-quit)
  (define-key lyskom-sv-filter-edit-map (kbd "x")   'lyskom-filter-edit-expunge)
  (define-key lyskom-sv-filter-edit-map (kbd "s")   'lyskom-filter-edit-save)
  (define-key lyskom-sv-filter-edit-map (kbd "g")   'lyskom-filter-edit-revert)
  (define-key lyskom-sv-filter-edit-map (kbd "t")   'lyskom-filter-edit-toggle-permanent)
  (define-key lyskom-sv-filter-edit-map (kbd "a")   'lyskom-filter-edit-toggle-action)
  (define-key lyskom-sv-filter-edit-map (kbd "?")   'lyskom-filter-edit-brief-help)
  (define-key lyskom-sv-filter-edit-map (kbd "h")   'lyskom-filter-edit-brief-help)
  )


;;;; ============================================================
;;;; Strings and things for the customize mode
;;;;

(defvar lyskom-sv-customize-map nil)
(lyskom-language-keymap lyskom-customize-map sv lyskom-sv-customize-map)


(if lyskom-sv-customize-map
    nil
  (setq lyskom-sv-customize-map (make-sparse-keymap))
  (define-key lyskom-sv-customize-map (kbd "TAB") 'widget-forward)
  (define-key lyskom-sv-customize-map (kbd "M-TAB") 'widget-backward)
  (define-key lyskom-sv-customize-map (kbd "<S-tab>") 'widget-backward)
  (define-key lyskom-sv-customize-map (kbd "C-i") 'widget-forward)
  (define-key lyskom-sv-customize-map (kbd "M-C-i") 'widget-backward)
  (define-key lyskom-sv-customize-map (kbd "C-m") 'widget-button-press)
  (define-key lyskom-sv-customize-map (kbd "C-j") 'widget-button-press)
  (define-key lyskom-sv-customize-map (kbd "<RET>") 'widget-button-press)
  (define-key lyskom-sv-customize-map (kbd "<LFD>") 'widget-button-press)
  (define-key lyskom-sv-customize-map (kbd (lyskom-keys 'button1)) 'widget-button-click)
  (define-key lyskom-sv-customize-map (kbd (lyskom-keys 'button2)) 'widget-button-click)
  (define-key lyskom-sv-customize-map (kbd (lyskom-keys 'button3)) 'lyskom-widget-click)
  (define-key lyskom-sv-customize-map (kbd (lyskom-keys 'button1up)) 'kom-mouse-null)
  (define-key lyskom-sv-customize-map (kbd (lyskom-keys 'button2up)) 'kom-mouse-null)
  (define-key lyskom-sv-customize-map (kbd (lyskom-keys 'button3up)) 'kom-mouse-null)
  (define-key lyskom-sv-customize-map (kbd "C-c C-c") 'lyskom-customize-save-and-quit)
  (define-key lyskom-sv-customize-map (kbd "C-c C-k") 'lyskom-customize-quit)
  (define-key lyskom-sv-customize-map (kbd "C-c C-s") 'lyskom-customize-save)
  (define-key lyskom-sv-customize-map (kbd "C-c C-a") 'lyskom-customize-apply)
  (define-key lyskom-sv-customize-map (kbd "?") 'lyskom-customize-help)
)

(lyskom-language-strings local lyskom-custom-strings sv
  '(
    ;;
    ;; Widget strings
    ;;

    (which-person . "Ange en person: ")
    (which-conf . "Ange ett möte: ")
    (which-conf-or-person . "Ange en person eller ett möte: ")
    (which-name . "Ange ett namn: ")
    (some-person . "Person %#1d")
    (invalid-value . "Otillåtet värde (%#1S)")
    (unknown-command . "Okänt kommando (%#1s)")
    (ding . "Ljudsignal")

    ;;
    ;; Help messages
    ;;

    (variable-type-help . "Kryssa i för att spara inställningen i servern.")
    (default-help-echo . "Ändra värdet på %#1s.")
    (change-this-name . "Ändra namnet på denna rad.")
    (show-doc . "Visa hjälptexten.")
    (hide-doc . "Dölj hjälptexten.")
    (select-command . "Välj kommando.")
    (select-what-to-execute . "Exekvera kommando eller tangentbordsmakro.")
    (select-url-viewer . "Välj en WWW-läsare.")
    (select-number . "Ange hur många gånger.")
    (select-audio-file . "Ange en ljudfil.")
    (select-priority . "Ange prioritet.")
    (select-buffer . "Ange buffert.")
    (select-buffer-size . "Ange buffertstorlek.")

    ;;
    ;; Strings that are used in types and so forth
    ;;

    (buffer-name . "%#1s-inställningar")

    (other-window     . "Något annat fönster            ")
    (other-frame      . "I en annan frame               ")
    (new-frame        . "I en ny frame                  ")
    (lyskom-window    . "LysKOM-buffertens fönster      ")
    (window-on-buffer . "Ett fönster som visar bufferten")

    (on . "På")
    (off . "Av")
    (yes . "Ja ")
    (no . "Nej")
    (to-file . "Till fil:")
    (max-text-length . "För inlägg kortare än: ")
    (autowrap-timeout . "Antal sekunder: ")
    (no-timeout . "Ingen tidsbegränsning")

    (session-lost-all-buffers . "Visa notifiering i alla aktiva LysKOM buffrar")
    (session-lost-beep . "Pip")

    (friends . "Vänner och bekanta")

    (turned-off      . "Avslaget            ")
    (number-of-times . "Några gånger")
    (specific-spec . "Per mottagare/avsändare")
    (sound-file . "Ljudfil")
    (other-persons . "Alla andra")
    (selected-mark . "Markering")
    (conf-or-person . "Person eller möte")
    (ask . "Fråga varje gång")
    (before . "Före texten")
    (after . "Efter texten")
    (first . "Först")
    (last . "Sist ")
    (depth-first . "I kommentarsordning")
    (time-order . "I tidsordning")
    (ancestor . "Läs olästa kommenterade först")
    (relative . "Läs olästa relaterade först")
    (limited-number . "Ett begränsat antal")
    (unlimited-number . "Obegränsat")

    (ar-message-type . "Meddelandetyp")
    (ar-personal     . "Personligt meddelande")
    (ar-group        . "Gruppmeddelande")
    (ar-alarm        . "Alarmmeddelande")
    (ar-any-type     . "Alla meddelandetyper")
    (ar-sender       . "Avsändare")
    (ar-any-sender   . "Alla avsändare")
    (ar-specified-sender . "Specifika avsändare")
    (ar-pers-or-conf . "Person eller möte")
    (ar-recipient    . "Mottagare")
    (ar-any-recipient . "Alla mottagare")
    (ar-specified-recipient . "Specifika mottagare")
    (ar-person       . "Person")
    (ar-message-text . "Meddelandetext")
    (ar-any-message  . "Alla meddelanden")
    (ar-matching-regexp . "Meddelanden som matchar")
    (ar-reply        . "Automatsvar")
    (ar-no-reply     . "Inget svar")
    (ar-reply-text   . "Svarstext")

    (express-break . "Omedelbart efter de har skapats")
    (break         . "Efter aktuell kommentarskedja")
    (no-break      . "Efter aktuellt möte")

    (express-server-break . "Omedelbart")
    (express-server-letters-break . "Omedelbart om det kommer brev")
    (server-break . "Efter aktuell kommentarskedja")
    (letters-server-break . "Efter aktuell kommentarskedja om det kommer brev")
    (after-conf-server-break . "Efter aktuellt möte")
    (after-conf-letters-server-break . "Efter aktuellt möte när det kommer brev")
    (when-done-server-break . "När allt är utläst")
    (no-server-break . "Aldrig")

    (command . "Kommando")
    (command-list . "Kommandolista")
    (some-persons . "För vissa personer:")
    (name . "Namn")

    (page-none . "Aldrig                 ")
    (page-all  . "Före varje kommando    ")
    (page-some . "Före följande kommandon")

    (ask-every-time . "Fråga varje gång      ")
    (fixed-priority . "Fast prioritet")

    (messages-in-lyskom-buffer . "I LysKOM-bufferten  ")
    (discard-messages .          "Ingenstans          ")
    (in-named-buffer .           "I namngiven buffert:")

    (automatic-session-name . "Automatiskt")
    (given-session-name . "Använd angivet:")

    (simple-unread-mode-line . "Enkel")
    (explicit-unread-mode-line . "Visa alla sessioner med olästa")

    (everybody-rcpt .       "Alla                              ")
    (group-rcpt .           "Senaste gruppmeddelandes mottagare")
    (sender-rcpt .          "Senaste meddelandes avsändare     ")
    (last-recipient-recpt . "Senaste mottagaren                ")

    (viewer-program . "WWW-läsare")
    (no-viewer            . "(ingenting valt)")
    (default-viewer       . "Browse-URL (alla)")
    (netscape-viewer      . "Firefox/Mozilla/Opera/Netscape (alla)")
    (windows-viewer       . "Windows standard eller Netscape-liknande (alla)")
    (emacs-w3-viewer      . "Emacs W3-mode (HTTP, Gopher, FTP)")
    (emacs-general-viewer . "Emacs (FTP, Telnet, Mail)")
    (emacs-dired-viewer   . "Emacs Dired (FTP)")
    (emacs-mail-viewer    . "Emacs Mail-mode (Mail)")
    (emacs-telnet-viewer  . "Emacs Telnet-mode (telnet)")
    (mosaic-viewer        . "NCSA Mosaic (alla)")
    (lynx-viewer          . "Lynx (alla)")
    (galeon-viewer        . "Galeon (alla)")

    (dont-check . "Ingen bekräftelse")
    (check-before-open . "Bekräfta innan inlägget skrivs")
    (check-before-send . "Bekräfta innan inlägget sänds")
    (no-size-limit . "Ingen begränsning")
    (max-size-in-bytes . "Begränsning (i bytes)")
    (execute . "Utför")
    (kbd-macro . "Tangentbordmakro")
    (command . "Kommando")
    (enter-kbd-macro . "Mata in tangentbordsmakro. Avsluta med %#1s")

    (long-format  . "Visa hjälptexter")
    (short-format . "Göm hjälptexter ")

    (truncate-threshold-lines . "Max rader")

    (first . "Först")
    (last . "Sist")
    (specific-placement . "Specifik plats")
    (priority . "Prioritet")
    (same-as-conf . "Samma som aktuellt möte")
    (custom-format . "Eget format:")
    (default-format . "Standardformat")
    (a-string . "En text:")
    (some-string . "En slumpmässig text")
    (unspecified . "Ospecificerat")

    (symbolic-mark-association . "Associering")
    (symbolic-mark-name . "Symboliskt namn: ")
    (mark-type-to-assoc . "Markeringstyp att associera till: ")

    (prompt-for-text-no . "Fråga efter textnummer")

    (all-others . "Alla andra")
    (some-person . "Person")
    (format-html . "Formattera HTML:")

    (url-transform-regexp . "Från (regexp):")
    (url-transform-newtext . "Till:")

    (default-namedays . "Namnsdagar för aktuellt språk")
    (specific-namedays . "Specifik namnlängd")
    (several-name-lists . "Flera namnlängder")

    (link-pattern-for . "Mönster för")
    (all-conferences . "Alla mottagare")
    (link-specific-rcpt . "Specifik mottagare")
    (link-pattern . "Länkmönster")
    (link-replace . "Länksubstitution")
    (link-highlight-match . "Visa grupp")
    (link-fold-case . "Versaler och gemener lika")

    (generated-face . "Automatgenererat teckensnitt")
    (predefined-fonts . "Teckensnitt avsedda för specifika tillämpningar\n")
    (other-fonts . "Teckensnitt utan specifika tillämpningar\n")
    (other-face . "Annat")

    (ext-simple-command . "Enkelt kommando")
    (ext-complex-command . "Kommando med argument")
    (ext-complex-component . "Komanndo/argument")

    (lynx-xterm . "Lynx i xterm")
    (lynx-emacs . "Lynx i emacs terminal-mode")

    (morons . "Idioter och knäppskallar")
    (friends . "Vänner och bekanta")
    (me . "Mig själv och ingen annan")
    (i-am-supervisor . "Möten jag organiserar")
    (has-no-presentation . "Möten utan presentationer")
    (highlight-conferences . "Möten:")
    (highlight-face . "Utseende:")
    (highlight-conflist . "Möteslista")

    (yes-dont-pop . "Ja, men avikonifiera inte fönstret")
    (language . "Språk")

    (specific-extended-info . "Enbart vald information")
    (extended-info-show . "Visa:")
    (extended-info-type . "Informationstyp:")

    (extended-server-info . "All utökad serverinformation")
    (extended-conf-info . "All utökad mötesinformation")
    (extended-pers-info . "All utökad personinformation")
    (extended-read-faq-info . "Lästa FAQ")
    (extended-statistics-info . "Fullständig serverstatistik")
    (extended-all-other-info . "Annan information")

    (show-presence . "Visa närvaromeddelanden")
    (conflist . "Mötes/Personlista")
    (pers-list-1 . "Mötes/personlista 1")
    (pers-list-2 . "Mötes/personlista 2")
    (pers-list-3 . "Mötes/personlista 3")
    (pers-list-4 . "Mötes/personlista 4")
    (other-list . "Annan lista")
    (always-show-presence . "Visa alla närvaromeddelanden")
    (never-show-presence . "Visa inga närvaromeddelanden")
    (obsolete-setting . "Föråldrad inställning")
    (detailed-setting . "Detaljerad inställning")

    ;;
    ;; Misc doc strings
    ;;

    (lyskom . "Inställningar för LysKOM")
    (lyskom-doc . "\
Dessa tangenter fungerar utanför redigerbara textfält.
\\[lyskom-customize-save-and-quit] för att spara och avsluta,
\\[lyskom-customize-apply] för att applicera utan att spara eller avsluta,
\\[lyskom-customize-save] för att spara utan att avsluta,
\\[lyskom-customize-quit] för att avsluta utan att spara.
\\[widget-forward] flyttar till nästa inställning
\\[widget-button-press] ändrar värdet
Tryck C-h m för en fullständig lista av kommandon.

Hjälptexter: [?] Visa hjälptext    [!] Göm hjälptext
Listor mm.:  [INS] Lägg till rad   [DEL] Ta bort rad    [*] Ändra värde

Om rutan före inställningens namn är ikryssad kommer värdet att sparas
i servern. Annars sparas det i din .emacs.

Klicka med musens mittenknapp på ledtexterna för att ändra värde eller
ta fram menyer.")

    (section . "------------------------------------------------------------------------------\n")
    (look-and-feel-misc . "Diverse utseende och beteende\n")
    (window-locations   . "Fönster\n")
    (windows-where      . "Hur skall fönster skapas:\n")
    (reading .            "Beteende vid läsning\n")
    (writing .            "Beteende vid skrivning\n")
    (urls .               "Hantering av URLer\n")
    (personal-messages .  "Hantering av personliga meddelanden\n")
    (remote-control .     "Fjärrstyrning av LysKOM\n")
    (hooks .              "Hook-funktioner\n")

    (audio-cues .         "Ljudsignaler\n")
    (audio-cues-when    . "Ge ljudsignal:\n")
    (automatic-replies .  "Automatiskt svar\n")

    (new-window-netscape . "Öppna URLer i nytt fönster")
    (new-tab-netscape . "Öppna URLer i ny tab")
    (default-netscape . "Öppna URLer där de råkar hamna")
    
    (audio-cues-doc . "\
  Efterföljande inställningar bestämmer vilken ljudsignal LysKOM skall ge 
  i olika situationer. Följande alternativ går att välja:

    Avslaget
        Ingen ljudsignal alls kommer att ges.

    Några gånger
        Emacs kommer att pipa en eller flera gånger när händelsen
        inträffar. Antal pip anges också. 

    Ljudfil
        Emacs kommer att spela upp den angivna ljudfilen. Programmet som
        angavs ovan som ljudspelarprogram används för att spela ljudet.")
    

    (sending-doc . "\
  Följande inställningar slår på eller av vissa kontroller vid inskickning
  av inlägg. Kontrollerna är avsedda att hindra att man gör någonting dumt.

    Bekräfta multipla mottagare
        Om ett inlägg eller kommentar har flera mottagare så kan LysKOM
        fråga vilka av mottagarna som är relevanta för inlägget. Antingen
        görs detta innan man skriver inlägget (då får man en fråga för
        varje defaultmottagare), eller när man skickar in inlägget (då får
        man bekräfta alla mottagare i klump). Det går också att slå av
        helt.

    Kontroll av kommenterad författares medlemskap
        Kontrollera att författaren till det kommenterade inlägget är
        medlem i någon av mottagarna för kommentaren. Om så inte är fallet,
        erbjuder LysKOM att lägga till författaren som mottagare till
        inlägget. 

    Författare som inte kontrolleras
        En lista av författare som inte skall kontrolleras även om
        kontroll av kommenterad författares medlemskap är påslaget.

    Kontroll av olästa kommentarer
        När detta är påslaget så kontrollerar LysKOM att det inlägg man
        kommenterar inte har några kommentarer man inte har läst. Detta är
        främst användbart för att undvika att man skriver någonting som
        någon annan redan har skrivit.")


    (windows-doc . "\
  Följande inställningar bestämmer hur fönster skapas i LysKOM. Alternativen
  som finns är följande:

    Något annat fönster 
        I ett annat fönster i samma frame som LysKOM. Om det bara finns ett
        fönster så kommer ett nytt att skapas (och tas bort när man är klar).

    I en annan frame
        I en annan frame än LysKOM. Om det bara finns en frame så kommer en
        ny frame att skapas, och (tas bort när man är klar).

    I en ny frame
        En ny frame skapas för ändamålet, och tas bort när man är klar.

    LysKOM-buffertens fönster 
        LysKOM-buffertens fönster kommer att användas, och LysKOM kommer
        att återställas till fönstret när man är färdig.

    Ett fönster som visar bufferten 
        Om det finns ett fönster någonstans som visar den angivna bufferten
        så kommer detta fönster att användas. Det kan till exempel vara
        användbart om man hela tiden har ett fönster öppet, men inte beöver
        det just medan man utför något visst kommando.")

    ;;
    ;; Doc strings for variables
    ;;

    (kom-ansaphone-replies-doc . "\
  Den här inställningen styr automatsvar i detalj. Det går att ställa in 
  svar för specifika avsändare, mottagare och till och med meddelanden. Om
  meddelandet som skall besvaras inte stämmer in på något i den här listan
  så används svarsmeddelandet ovan istället.

  För varje mönster går det att ställa in meddelandetyp, avsändare som 
  meddelandet skall ha, mottagare som meddelandet skall ha, ett mönster
  som meddelandet skall innehålla och vilket svar som skall skickas.

  Det är lämpligt att ställa in att inget svar skall skickas på något
  gruppmeddelande eller alarmmeddelande.")

    (kom-bury-buffers-doc . "\
  Bestämmer hur bufferten hanteras när man går till ett annat KOM med Nästa
  LysKOM och liknande funktioner. Påslaget innebär att den aktuella bufferten
  läggs sist i buffertlistan när man byter LysKOM.")

    (kom-personal-messages-in-window-doc . "\
  Bestämmer i vilket fönster som bufferten med personliga meddelanden visas.
  För att den här inställningen skall spela någon roll så måste personliga
  meddelanden visas i en separat buffert, och bufferten måste visas varje
  gång det kommer meddelanden.")

    (kom-write-texts-in-window-doc . "\
  Bestämmer i vilket fönster nya texter skrivs.")

    (kom-prioritize-in-window-doc . "\
  Bestämmer i vilket fönster man prioriterar om möten.")

    (kom-edit-filters-in-window-doc . "\
  Bestämmer i vilket fönster man ändrar filter.")

    (kom-customize-in-window-doc . "\
  Bestämmer i vilket fönster man gör dessa inställningar.")

    (kom-view-commented-in-window-doc . "\
  Bestämmer i vilket fönster man visar kommenterarer medan man skriver nya.")

    (kom-list-membership-in-window-doc . "\
  Bestämmer i vilket fönster man listar medlemskap.")


    (kom-user-prompt-format-doc . "\
  Format för LysKOM-prompten. Vissa teckenkombinationer sätter in speciella
  texter:

    %c - Sätter in kommandot som körs om man trycker på SPC eller RET.
    %[ - Sätter in '[' om automatsvar är påslaget.
    %] - Sätter in ']' om automatsvar är påslaget.     
    %m - Sätter in information om antal inspelade meddelanden.
    %s - Sätter in LysKOM-systemets namn
    %S - Sätter in LysKOM-serverns namn
    %p - Sätter in namnet på den person som är inloggad.
    %w - Sätter in namn på aktuellt möte.
    %a - Sätter in \"anonym\"
    %A - Sätter in \"Anonym\"
    %# - Sätter in sessionsnummer.
    %  - Sätter in mellanslag om det ser ut att behövas.
    %% - Sätter in ett procenttecken.

  Några exempel:

    \"%[%c% %m%] - \"             Standardprompt
    \"%[%s: %c% %m%] - \"         Till exempel \"LysKOM: Se tiden - \"")

    (kom-user-prompt-format-executing-doc . "\
  Format för LysKOM-prompten efter man trycker på SPC eller RET.  Vissa
  teckenkombinationer sätter in speciella texter:

    %c - Sätter in kommandot som körs om man trycker på SPC eller RET.
    %[ - Sätter in '[' om automatsvar är påslaget.
    %] - Sätter in ']' om automatsvar är påslaget.     
    %m - Sätter in information om antal inspelade meddelanden.
    %s - Sätter in LysKOM-systemets namn
    %S - Sätter in LysKOM-serverns namn
    %p - Sätter in namnet på den person som är inloggad.
    %w - Sätter in namn på aktuellt möte.
    %a - Sätter in \"anonym\"
    %A - Sätter in \"Anonym\"
    %# - Sätter in sessionsnummer.
    %  - Sätter in mellanslag om det ser ut att behövas.
    %% - Sätter in ett procenttecken.

  Några exempel:

    \"%[%c% %m%].\"             Standardprompt
    \"%[%s: Kör %c% %m%]...\"   Till exempel \"LysKOM: Kör Se tiden...\"")

    
    (kom-anonymous-prompt-format-doc . "\
  Format för LysKOM-prompten när man är anonym. Vissa teckenkombinationer 
  sätter in speciella texter:

    %c - Sätter in kommandot som körs om man trycker på SPC eller RET.
    %[ - Sätter in '[' om automatsvar är påslaget.
    %] - Sätter in ']' om automatsvar är påslaget.     
    %m - Sätter in information om antal inspelade meddelanden.
    %s - Sätter in LysKOM-systemets namn
    %S - Sätter in LysKOM-serverns namn
    %p - Sätter in namnet på den person som är inloggad.
    %w - Sätter in namn på aktuellt möte.
    %a - Sätter in \"anonym\"
    %A - Sätter in \"Anonym\"
    %# - Sätter in sessionsnummer.
    %  - Sätter in mellanslag om det ser ut att behövas.
    %% - Sätter in ett procenttecken.

  Några exempel:

    \"%[%c% %m%] - \"             Standardprompt
    \"%[%s: %c% %m%] - \"         Till exempel \"LysKOM: Se tiden - \"")

    (kom-anonymous-prompt-format-executing-doc . "\
  Format för LysKOM-prompten efter man trycker på SPC eller RET och är
  anonym.  Vissa teckenkombinationer sätter in speciella texter:

    %c - Sätter in kommandot som körs om man trycker på SPC eller RET.
    %[ - Sätter in '[' om automatsvar är påslaget.
    %] - Sätter in ']' om automatsvar är påslaget.     
    %m - Sätter in information om antal inspelade meddelanden.
    %s - Sätter in LysKOM-systemets namn
    %S - Sätter in LysKOM-serverns namn
    %p - Sätter in namnet på den person som är inloggad.
    %w - Sätter in namn på aktuellt möte.
    %a - Sätter in \"anonym\"
    %A - Sätter in \"Anonym\"
    %# - Sätter in sessionsnummer.
    %  - Sätter in mellanslag om det ser ut att behövas.
    %% - Sätter in ett procenttecken.

  Några exempel:

    \"%[%c% %m%].\"             Standardprompt
    \"%[%s: Kör %c% %m%]...\"   Till exempel \"LysKOM: Kör Se tiden...\"")

    (kom-enabled-prompt-format-doc . "\
  Format för LysKOM-prompten när man är i administratörsmod. Vissa
  teckenkombinationer sätter in speciella texter:

    %c - Sätter in kommandot som körs om man trycker på SPC eller RET.
    %[ - Sätter in '[' om automatsvar är påslaget.
    %] - Sätter in ']' om automatsvar är påslaget.     
    %m - Sätter in information om antal inspelade meddelanden.
    %s - Sätter in LysKOM-systemets namn
    %S - Sätter in LysKOM-serverns namn
    %p - Sätter in namnet på den person som är inloggad.
    %w - Sätter in namn på aktuellt möte.
    %a - Sätter in \"anonym\"
    %A - Sätter in \"Anonym\"
    %# - Sätter in sessionsnummer.
    %  - Sätter in mellanslag om det ser ut att behövas.
    %% - Sätter in ett procenttecken.

  Några exempel:

    \"%[%c% %m%] - \"             Standardprompt
    \"%[%s: %c% %m%] - \"         Till exempel \"LysKOM: Se tiden - \"")

    (kom-enabled-prompt-format-executing-doc . "\
  Format för LysKOM-prompten efter man trycker på SPC eller RET och är i
  administratörsmod.  Vissa teckenkombinationer sätter in speciella texter:

    %c - Sätter in kommandot som körs om man trycker på SPC eller RET.
    %[ - Sätter in '[' om automatsvar är påslaget.
    %] - Sätter in ']' om automatsvar är påslaget.     
    %m - Sätter in information om antal inspelade meddelanden.
    %s - Sätter in LysKOM-systemets namn
    %S - Sätter in LysKOM-serverns namn
    %p - Sätter in namnet på den person som är inloggad.
    %w - Sätter in namn på aktuellt möte.
    %a - Sätter in \"anonym\"
    %A - Sätter in \"Anonym\"
    %# - Sätter in sessionsnummer.
    %  - Sätter in mellanslag om det ser ut att behövas.
    %% - Sätter in ett procenttecken.

  Några exempel:

    \"%[%c% %m%].\"             Standardprompt
    \"%[%s: Kör %c% %m%]...\"   Till exempel \"LysKOM: Kör Se tiden...\"")

    (kom-cite-string-doc . "\
  Text som sätts in före varje rad i ett citerat inlägg. Normalt brukar
  någonting i stil med \"> \" användas. Notera dock att det inte är brukligt
  att citera KOM-inlägg som man gör med sladdpost och News eftersom det
  kommenterade inlägget oftast finns tillgängligt.")


    (kom-created-texts-are-read-doc . "\
  Om detta är påslaget kommer alla inlägg man själv skriver att läsmarkeras
  automatiskt. Om inställningen är avslagen så får man läsa sådant man
  själv skriver.")


    (kom-default-mark-doc . "\
  Den markeringstyp som används för nya markeringar. En markeringstyp är ett
  heltal mellan 0 och 255 (inklusive). Om ingen defaultmarkeringstyp är vald
  frågar LysKOM efter markeringstyp varje gång man markerar en text. Detta
  kan användas för att hålla isär olika sorters markeringar. Till exempel
  kan man markera inlägg som innehåller intressant information med ett visst
  tal och inlägg som man ska komma ihåg att kommentera vid ett senare
  tillfälle med ett annat tal.")

    (kom-symbolic-marks-alist-doc . "\
  För att slippa behöva memorera vad man tänkte sig att de heltaliga
  markeringstyperna ska innebära kan man definiera en lista med symboliska
  markeringstyper. En symbolisk markeringstyp är en beskrivande sträng som
  associeras med en heltalig markeringstyp.")

    (kom-reading-puts-comments-in-pointers-last-doc . "\
  Bestämmer om kommentarslänkar visas före eller efter en text. Normalt brukar
  man visa kommentarslänkar efter texten.

  Före:

    398331 1996-09-24  13:22  /2 rader/ George Berkeley
    Mottagare: Filosofimötet <1226>
    Kommentar i text 398374 av John Locke
    Ärende: 
    ------------------------------------------------------------
    En abstrakt idé är en självmotsägelse. 
    (398331) -----------------------------------

  Efter:

    398331 1996-09-24  13:22  /2 rader/ George Berkeley
    Mottagare: Filosofimötet <1226>
    Ärende: 
    ------------------------------------------------------------
    En abstrakt idé är en självmotsägelse. 
    (398331) -----------------------------------
    Kommentar i text 398374 av John Locke
")

    (kom-show-author-at-end-doc . "\
  Om detta är påslaget så visas namnet på författaren efter inläggstexten.
  Naturligtvis så visas även namnet i inläggshuvudet. 

  Påslaget (med streckade linjer också påslaget):

    892342 1996-09-24  19:21  /2 rader/ Claude Shannon
    Mottagare: Presentation (av nya) Medlemmar
    Ärende: Claude Shannon
    ------------------------------------------------------------
    Informationsteoretiker
    (892342) /Claude Shannon/------------------------------

  Avslaget:

    892342 1996-09-24  19:21  /2 rader/ Claude Shannon
    Mottagare: Presentation (av nya) Medlemmar
    Ärende: Claude Shannon
    ------------------------------------------------------------
    Informationsteoretiker
    (892342) -----------------------------------

  Om man har streckade linjer avslaget så visas författaren på samma 
  sätt, men de streckade linjerna finns naturligtvis inte med.")

    (kom-truncate-threshold-doc . "\
  Om detta är påslaget kommer inlägg som är längre än maxantalet att kortas
  ner när man återser kommentarer, återser markerade inlägg eller återser de
  senaste eller första.  Inlägg kortas inte av då man återser inlägg, återser
  träd eller bara läser nästa inlägg.")

    (kom-truncate-show-lines-doc . "\
  Antalet rader som visas när ett inlägg avkortats.")
     
    (kom-dashed-lines-doc . "\
  Om detta är påslaget visas streckade linjer före och efter inläggstexten,
  annars visas en tomrad efter ärenderaden, och ingenting speciellt i slutet.

  Påslaget:

    892343 1996-09-24  19:21  /2 rader/ Tycho Brahe
    Mottagare: Presentation (av nya) Medlemmar
    Ärende: Tycho Brahe
    ------------------------------------------------------------
    Astronom och upptäckare av stjärnor med hemvist på Ven.
    (892343) -----------------------------------

  Avslaget:

    892343 1996-09-24  19:21  /2 rader/ Tycho Brahe
    Mottagare: Presentation (av nya) Medlemmar
    Ärende: Tycho Brahe

    Astronom och upptäckare av stjärnor med hemvist på Ven.
    (892343)

  De flesta brukar ha detta påslaget.")

    (kom-autowrap-doc . "\
  Om detta är påslaget så kommer LysKOM att försöka bryta om raderna i
  stycken vars rader är för långa. Stycken som verkar vara
  förformatterade av författaren bryts dock aldrig om. Det går att ange
  en maximal längd i tecken på inlägg som får brytas om (vilket kan vara
  användbart för att undvika mycket långa inlägg).")

    (kom-autowrap-timeout-doc . "\
  Denna inställning begränsar hur lång tid automatisk ombrytning av
  raderna i ett inlägg får ta (i sekunder). En begränsning är särskilt
  användbar när man läser mycket långa inlägg. Notera att tiden är
  högst ungefärlig. Ombrytningen kan pågå i flera sekunder längre än
  detta värde. Begränsningen gör också att all inläggsvisning går
  aningen långsammare. Om begränsningen är avstängd kan man ändå
  avbryta ombrytningen genom att trycka C-g.") 

    (kom-print-number-of-unread-on-entrance-doc . "\
  Om detta är påslaget så visas antalet olästa i ett möte när man går till 
  mötet:

  Påslaget:

    Gå till nästa möte...
    Presentation (av nya) medlemmar - 3 olästa
    Läsa nästa text - 

  Avslaget:

    Gå till nästa möte...
    Presentation (av nya) medlemmar
    Läsa nästa text - ")


    (kom-presence-messages-in-echo-area-doc . "\

  Om man vill ha meddelanden i ekoarean när någon loggar in, ut eller
  byter namn i LysKOM bör man slå på den här inställningen. Meddelanden
  visas i ekoarean. Om man vill ha information om endast vissa personer
  går det att ange en lista av personer. Vill man inte ha några
  meddelanden alls är det bara att ange en tom lista.")

    (kom-presence-messages-in-buffer-doc . "\
  Om man vill ha meddelanden i LysKOM-bufferten när någon loggar in, ut eller
  byter namn i LysKOM bör man slå på den här inställningen.  Meddelanden
  visas i LysKOM-bufferten. Om man vill ha information om endast vissa 
  personer går det att ange en lista av personer. Vill man inte ha några 
  meddelanden alls är det bara att ange en tom lista.")

    (kom-show-where-and-what-doc . "\
  Information i vilkalistan om varifrån en person är inloggad och vad personen
  gör visas bara om detta är påslaget.

  Påslaget:

        Användare                            Närvarande i möte              
        Kör från                             Gör                             
    --------------------------------------------------------------------------
     6810 George Berkeley                      Filosofimötet                  
          berkeley@emp1.tcd.ie                 (Skriver en kommentar.)        
     7571 John Locke                           Filosofimötet                  
          eridy@cc.ox.ac.uk                    (Väntar.)                      

  Avslaget:

        Användare                            Närvarande i möte              
    --------------------------------------------------------------------------
     6810 George Berkeley                      Filosofimötet                  
     7571 John Locke                           Filosofimötet")

    (kom-show-since-and-when-doc . "\
  Information i vilkalistan om när en person loggade in och hur länge sedan
  personen var aktiv.")

    (kom-remember-password-doc . "\
  Om denna inställning är påslagen sparas lösenordet till LysKOM-servern
  som en lokal variabel i sessionsbufferten och kan på så vis användas
  vid återinloggning ifall sessionen kopplas ner. OBS: Lösenordet sparas
  i klartext, detta gör att om någon får tillgång till din LysKOM-
  sessionsbuffer (även nedkopplad sådan) kan han/hon få reda på ditt
  lösenord ifall du aktiverar detta val.")

    (kom-idle-hide-doc . "\
  I vilkalistan visas normalt enbart de sessioner som har varit aktiva
  under den senaste tiden. Denna inställning bestämmer hur många minuter
  en session får ha varit inaktiv och ändå visas i vilkalistan.")


    (kom-show-footnotes-immediately-doc . "\
  Fotnoter visas normalt på samma sätt som kommentarer, men om detta är
  påslaget så visas fotnoter direkt efter inlägget de hör till.")


    (kom-follow-comments-outside-membership-doc . "\
  Normalt så visar inte LysKOM kommentarer till ett inlägg om man inte är
  medlem i något av mötena kommentaren har som mottagare. Om man vill
  följa kommentarskedjor utanför de möten man är medlem i kan man slå
  på den här inställningen.")


    (kom-read-depth-first-doc . "\
  LysKOM kan visa inlägg antingen i den ordning de skapades, eller i den
  ordning de ligger i kommentarsträdet. Ta till exempel följande inlägg:

  Inlägg 1002 är inte en kommentar till någonting. Inlägg 1003 och 1004
  är kommentarer till inlägg 1002. Inlägg 1005 och 1007 är kommentarer
  till inlägg 1004 och inlägg 1006 är en kommentar till inlägg 1003. Då
  ser kommentarsträdet ut ungefär så här:
        
  1002 +-- 1003 --- 1006
       |
       +-- 1004 +-- 1005
                |
                +-- 1007

  Om man läser inläggen i tidsordning kommer de att visas i ordningen
  1002, 1003, 1004, 1005, 1006 och 1007. I kommentarsordning blir det
  i stället 1002, 1003, 1006, 1004, 1005 och 1007.")


    (kom-read-related-first-doc . "\
  När LysKOM bestämt sig för en text som ska visas enligt reglerna som
  styrs av variabeln kom-read-depth-first, kan den ta hänsyn till att
  en annan relaterad oläst text borde visas först.  Följande värden är
  tillåtna:

    nil (Av)
        Gör ingen mer processning.

    'oldest-ancestor (Läs olästa kommenterade först)
        Om texten är en kommentar eller fotnot till en föräldratext,
        och om användaren är medlem i minst en mottagare av den
        texten, och texten är oläst, så blir texten en kandidat till
        att användas i stället.  Processen upprepas så länge någon
        kandidat har en förälder som uppfyller villkoren.  Den
        kandidat som har det lägsta textnumret kommer att användas.
        Den här inställningen betyder att ett kommentarsträd som
        börjar i ett annat möte än det du just nu läser kommer att
        visas i en mer logisk ordning: du får se hela det olästa
        trädet i kommentarsordning.

    'oldest-relative (Läs olästa relaterade först)
        Den här inställningen är snarlik 'oldest-ancestor, men
        undersöker även rekursivt alla kommentarer till alla
        kandidater.  Nackdelen med denna metod är att den kan behöva
        kontrollera hundratals texter, och det kan få klienten att bli
        långsam.")


    (kom-continuous-scrolling-doc . "\
  Påslaget innebär att klienten scrollar bufferten medan ny text sätts in, så
  man ser det senaste som har satts in. Detta fungerar bra om man har en
  snabb terminal, men kan vara värt att slå av om terminalen är långsam, och
  scrollning i Emacs tar tid.")


    (kom-deferred-printing-doc . "\
  För att snabba upp LysKOM så skrivs vissa saker, som person- och mötesnamn
  inte ut omedelbart, utan skrivs först ut som \"[...]\", vilket sedan byts
  ut mot den rätta utskriften när texten finns tillgänglig. Detta snabbar
  upp svarstiderna i klienten ganska mycket, men går att stänga av med den
  här inställningen om man tycker det ser otrevligt ut.")


    (kom-higher-priority-breaks-doc . "\
  När texter kommer in till möten som har högre prioritet än det man läser
  kan klienten låta en läsa dessa texter omedelbart, efter den aktuella
  kommentarskedjan eller efter man har läst ut mötet.")
    

    (kom-login-hook-doc . "\
  Kommandon som skall köras när man loggar in, innan någon inmatning från
  tangentbordet tas emot. Till exempel kan man göra Lista nyheter här för
  att få se en lista av nyheter varje gång man loggar in.")


    (kom-do-when-done-doc . "\
  Kommandon som skall köras när man har läst ut alla inlägg. Det är ganska
  vanligt att man har Återse alla markerade här. Både tangentbordsmakron
  och regelrätta funktioner går bra.")


    (kom-page-before-command-doc . "\
  LysKOM-bufferten kan scrollas före alla kommandon så att text som sätts in
  av kommandot hamnar överst i fönstret. Denna variabel talar om före vilka
  kommandon detta skall ske.")


    (kom-permissive-completion-doc . "\
  Om detta är påslaget så kommer TAB bara att fylla ut namn på inloggade
  personer när kommandot bara kan utföras på inloggade personer (till
  exempel Status (för) session och Sända meddelande). Om det är avslaget
  kommer TAB att fylla ut även med namn på personer som inte är inloggade.")


    (kom-membership-default-priority-doc . "\
  Detta bestämmer hur prioriteten på möten man går med i sätts. Om det är
  en siffra mellan 1 och 255 så kommer nya medlemskap att få den prioriteten.
  I annat fall kommer klienten att fråga för varje nytt medlemskap vilken
  prioritet det skall ha.")

    (kom-membership-default-message-flag-doc . "\
  Detta bestämmer om gruppmeddelanden tas emot i möten man går med i. `Ja'  
  betyder att gruppmeddelanden tas emot, `Nej' att meddelanden inte tas
  emot. `Fråga varje gång' att klienten frågar när du går med i möten.")

    (kom-show-personal-messages-in-buffer-doc . "\
  Denna inställning bestämmer var personliga meddelanden, gruppmeddelanden
  och alarmmeddelanden visas. Meddelanden kan antingen visas i 
  LysKOM-bufferten, kastas bort helt eller visas i en namngiven buffert.")


    (kom-pop-personal-messages-doc . "\
  Om personliga meddelanden skall visas i en egen buffert och denna inställning
  också är påslagen så kommer den bufferten att visas så fort ett meddelande
  anländer.")


    (kom-audio-player-doc . "\
  Om man vill att LysKOM skall spela ljudfiler i stället för att pipa vid 
  olika tillfällen måste denna inställning peka ut ett program som klarar
  att spela ljudfilerna. Programmet skall ta ett argument: namnet på filen
  som skall spelas.")


    (kom-default-message-recipient-doc . "\
  Denna inställning bestämmer vem som kommer att vara defaultmottagare
  för personliga meddelanden man skickar. Alternativen som finns är
  att meddelanden normalt är allmänna, att avsändaren för det senast
  mottagna meddelandet skall vara default eller att mottagaren för det
  senaste gruppmeddelandet (eller avsändaren av det senaste personliga
  eller allmänna meddelandet) skall vara mottagare.")


    (kom-filter-outgoing-messages-doc . "\
  Om denna inställning är påslagen så kommer meddelanden som skickas 
  automatiskt, till exempel automatiska svar och svar på fjärrkontrollförsök
  även att visas som om man hade skickat det manuellt.")

    (kom-friends-doc . "\
  Namnen på personerna i denna lista kan visas med ett speciellt utseende
  i LysKOM-bufferten. Kommandot Vilka vänner är inloggade (och andra
  liknande kommandon) använder också denna lista.")

    (kom-morons-doc . "\
  Namnen på personerna i denna lista kan visas med ett varnande 
  utseende i LysKOM-bufferten.")

    (kom-person-list-1-doc . "\
  Den här listan har ingen förutbestämd betydelse, utan kan användas för
  att ställa in att personerna ska visas med ett speciellt utseende (se
  kom-highlight-conferences) eller för att konfigurera in- och utloggningar
  (se kom-presence-messages-in-buffer) med mera.")

    (kom-person-list-2-doc . "\
  Den här listan har ingen förutbestämd betydelse, utan kan användas för
  att ställa in att personerna ska visas med ett speciellt utseende (se
  kom-highlight-conferences) eller för att konfigurera in- och utloggningar
  (se kom-presence-messages-in-buffer) med mera.")

    (kom-person-list-3-doc . "\
  Den här listan har ingen förutbestämd betydelse, utan kan användas för
  att ställa in att personerna ska visas med ett speciellt utseende (se
  kom-highlight-conferences) eller för att konfigurera in- och utloggningar
  (se kom-presence-messages-in-buffer) med mera.")

    (kom-person-list-4-doc . "\
  Den här listan har ingen förutbestämd betydelse, utan kan användas för
  att ställa in att personerna ska visas med ett speciellt utseende (se
  kom-highlight-conferences) eller för att konfigurera in- och utloggningar
  (se kom-presence-messages-in-buffer) med mera.")

    (kom-url-viewer-preferences-doc . "\
  Denna inställning bestämmer vilken WWW-läsare som i första hand skall 
  användas för att öppna URLer som förekommer i LysKOM. Om den första läsaren 
  i listan inte klarar den typ av URL som man försöker öppna så används nästa
  läsare och så vidare.")

    (kom-windows-browser-command-doc . "\
  Denna inställning talar om vilket kommando som skall användas för att starta
  en WWW-läsare i Windows. Om fältet är tomt testas ett antal kommandon som
  typiskt brukar fungera i Windows.")

    (kom-mosaic-command-doc . "\
  Denna inställning talar om vilket kommando som skall användas för att starta
  Mosaic.")

    (kom-netscape-command-doc . "\
  Denna inställning anger kommandot för att köra Firefox, Mozilla, Opera 
  eller Netscape.")

    (kom-galeon-command-doc . "\
  Denna inställning anger kommandot för att köra Galeon.")

    (kom-inhibit-typeahead-doc . "\
  Normalt sparas tangenttryckningar som görs medan klienten är upptagen, och
  utförs när det blir möjligt. Om denna inställning är avslagen så kommer
  LysKOM enbart att utföra kommandon som ges när klienten inte arbetar.")

    (kom-max-buffer-size-doc . "\
  Det är möjligt att begränsa LysKOM-buffertens storlek genom att ange hur
  stor den får bli i den här variabeln. Om bufferten blir för stor kommer
  information från buffertens början att tas bort.")

    (kom-ansaphone-record-messages-doc . "\
  LysKOM kan \"spela in\" meddelanden som kommer när funktionen för automatiskt
  svar är påslagen. Denna inställning bestämmer om så sker eller inte.")

    (kom-ansaphone-show-messages-doc . "\
  Om denna inställning är påslagen kommer LysKOM att visa inkomna personliga
  meddelanden även om automatiskt svar är påslaget.")

    (kom-ansaphone-default-reply-doc . "\
  Automatsvararen skickar detta meddelande om inget annat meddelande har
  specificerats via någon annan mekanism (och den är bara till för försiktiga
  experter).")

    (kom-remote-control-doc . "\
  Påslagen innebär att det går att fjärrstyra klienten. Fjärrstyrningen är
  dock begränsad till vissa personer.")

    (kom-remote-controllers-doc . "\
  Personerna i denna lista får fjärrstyra klienten.")

    (kom-self-control-doc . "\
  Om detta är påslaget får användaren som är inloggad styra klienten från
  en annan session. Detta är ett alternativ till att lägga in sig själv i
  listan över tillåtna fjärrstyrare.")

    (kom-customize-format-doc . "\
  Dokumentation till inställningarna kan vara på eller avslagen när 
  inställningsfönstret öppnas. Dokumentationen kan alltid visas eller
  gömmas för varje enskild inställning genom att använda frågetecknet
  eller utropstecknet som står till höger om inställningen.")

    (kom-default-language-doc . "\
  Språk som skall användas i LysKOM. Om du ändrar inställningen här så kommer
  inte inställningen ändras för aktuell session, utan du måste ge kommandot
  Ändra språk också. Det första språk på listan som är tillgängligt kommer
  att väljas av alla klienter med stöd för funktionen.")

    (kom-ispell-dictionary-doc . "\
  Ordlista som skall användas av stavningskontrollen i LysKOM. Om detta
  är satt till kom-ispell-dictionary så kommer variabeln ispell-dictionary
  att användas istället.")

    (kom-show-namedays-doc . "\
  Påslaget innebär att dagens namn visas när man ser tiden. Det går att
  välja att se namnsdagarna från namnsdagslängden som hör till det språk
  man för närvarande använder eller att använda en specifik lista med
  namn.")

    (kom-show-week-number-doc . "\
  Se tiden visar veckonummer om detta är påslaget. Annars visas inte 
  veckonummer.")

    (kom-relogin-behaviour-doc . "\
  Denna inställning styr om klienten ska försöka återansluta om ett 
  kommando ges i en nedkopplad session. På betyder att det sker 
  automatiskt, av att klienten inte frågar om man vill återansluta.")

    (kom-relogin-inhibit-commands-doc . "\
  Kommandon som inte skall orsaka någon återanslutning. Dessa kommandon
  kommer inte att orsaka att en nedkopplad klient återansluter.")

    (kom-membership-default-placement-doc . "\
  Denna inställning styr var nya medlemskap hamnar. Först betyder att nya
  medlemskap hamnar före gamla med samma prioritet. Sist betyder att nya 
  medlemskap hamnar efter gamla med samma prioritet. En siffra är en fix
  placering i medlemskapslistan (men medlemskapet kommer att flyttas
  såsmåningom).")

    (kom-show-imported-importer-doc . "\
  Påslaget betyder visa vilken person som importerade ett importerat e-mail.")

    (kom-show-imported-envelope-sender-doc . "\
  Visa den verkliga avsändaren till ett importerat e-mail om detta är 
  påslaget.")

    (kom-show-imported-external-recipients-doc . "\
  Visa externa mottagare och extra-kopiemottagare om detta är påslaget.")

    (kom-show-imported-message-id-doc . "\
  Påslaget betyder visa meddelande-id.")

    (kom-agree-text-doc . "\
  Text som används av kommandot Hålla med. Det kan antingen vara en text
  eller en lista av texter. Om det är en lista så väljs ett alternativ
  slumpmässigt.")

    (kom-silent-ansaphone-doc . "\
  On detta är avslaget så piper inte klienten när det kommer meddelanden och
  automatsvar är påslaget.")

    (kom-default-session-priority-doc . "\
  Läsnivå för nya sessioner. Detta är den läsnivå som nya sessioner får
  automatiskt.")

    (kom-unsubscribe-makes-passive-doc . "\
  Om detta är påslaget så gör kommandot Utträda ur möte att man först blir
  passiv medlem i mötet. Om man utträder ur mötet igen så går man ur på
  riktigt. Om detta är avslaget så går man ur mötet första gången man försöker
  utträda.")

    (kom-review-priority-doc . "\
  Prioritet för återsekommandon. Detta är den prioritet som inläggen man ser
  när man gör återse får. Det används för att avgöra om nya inlägg skall
  avbryta återsefunktionen. Sätt till 256 eller högre om du inte vill att nya
  inlägg någonsin skall avbryta en återsefunktion.")

    (kom-show-creating-software-doc . "\
  Om detta är påslaget så visas vilken klient som skapade inlägget tillsammans
  med varje inlägg, förutsatt att informationen finns överhuvudtaget.")

    (kom-text-footer-format-doc . "\
  Format för inläggsfoten. I texten ersätts %n med inläggsnumret, %P med
  författarens namn, %p med författarens nummer, %f med information om
  inlägget (ombruten, HTML mm.) och %- med en lagom lång streckad linje.
  En siffra efter procenttecknet är minsta bredd på texten. Ett minustecken
  före siffran betyder att texten skall vänsterjusteras. Ett likhetstecken
  betyder att fältlängden är exakt och texten kanske kortas av.")

    (kom-long-lines-doc . "\
  Om detta är påslaget så görs de flesta streckade linjer i klienten mycket
  längre än normalt.")

    (kom-postpone-default-doc . "\
  Antalet inlägg som inte skall uppskjutas med Uppskjuta läsning.")

    (kom-allow-incompleteness-doc . "\
  Påslaget betyder att klienten inte väntar på att information om alla
  olästa har kommit fram innan den tillåter kommandon som Lista nyheter.
  Avslaget innebär att klienten väntar på information om olästa inlägg om
  den upptäcker att informationen behövs och inte har kommit fram.")

    (kom-smileys-doc . "\
  Gladmän, som :-), visas grafiskt när denna inställning är på, förutsatt
  att Emacs klarar det och paketet smiley.el (en del av Gnus) är installerat.")

    (kom-ignore-message-senders-doc . "\
  Visa inte personliga, grupp- och alarmmeddelanden från dessa personer.")

    (kom-ignore-message-recipients-doc . "\
  Visa inte gruppmeddelanden riktade till dessa möten.")

    (kom-text-footer-dash-length-doc . "\
  Den totala längden på inläggsfoten när streckade linjer, men inte extra
  långa linjer, och inte ett eget format används.")

    (kom-text-header-dash-length-doc . "\
  Den totala längden på den streckade linjen ovanför inläggstexten när
  extra långa linjer inte används.")

    (kom-show-personal-message-date-doc . "\
  När detta är påslaget så visas datum och klockslag för alla personliga,
  grupp- och alarmmeddelanden.")

    (kom-w3-simplify-body-doc . "\
  När detta är påslaget så visas HTML utan de färger som anges i HTML-koden.")

    (kom-mercial-doc . "\
  Denna text visas i vilkalistan då du har läst färdigt alla möten.")

    (kom-session-nickname-doc . "\
  Hur sessionsnamn bestäms. Detta är det namn som visas i mode-raden och
  av Lista sessioner.")

    (kom-server-priority-doc . "\
  Prioritet för sessionen. Sessioner med hög prioritet kan avbryta andra
  sessioner, beroende på inställningen av YYY.")

    (kom-server-priority-breaks-doc . "\
  Denna inställning styr hur prioriterade sessioner avbryter andra
  sessioner. En session är högre prioriterad än en annan om dess
  sessionsprioritet är högre än den andra sessionens.

  Omedelbart            Så fort der kommer ett inlägg i en prioriterad 
                        session så får man prompt om att gå till den
                        sessionen.

  Omedelbart om det kommer brev     Så fort det kommer ett personligt
                        brev i en prioriterad session så får man prompt 
                        om att gå till den sessionen.

  Efter aktuell kommentarskedja     Om det kommer inlägg i en 
                        prioriterad session så får man prompt om att
                        gå till sessionen när man har läst klart den
                        aktuella kommentarskedjan.

  Efter aktuell kommentarskedja om det kommer brev      Om det kommer
                        personliga brev i en prioriterad session så 
                        får man prompt om att gå till sessionen när
                        man har läst klart den aktuella kommentarskedjan.

  Efter aktuellt möte   Om det kommer inlägg i en prioriterad session 
                        så får man prompt om att gå till sessionen när
                        man har läst klart det aktuella mötet.

  Efter aktuellt möte när det kommer brev       Om det kommer brev i en
                        prioriterad session så får man prompt om att gå
                        till sessionen när man har läst klart det 
                        aktuella mötet.

  När allt är utläst    Ge prompt om att byta till nästa LysKOM med 
                        olästa inlägg när allt är klart, oberoende av
                        sessionernas prioritet.

  Aldrig                Ge aldrig prompt om att byta session.")

    (kom-ding-on-no-subject-doc . "")
    (kom-ding-on-personal-messages-doc . "")
    (kom-ding-on-group-messages-doc . "")
    (kom-ding-on-common-messages-doc . "")
    (kom-ding-on-no-subject-doc . "")
    (kom-ding-on-wait-done-doc . "")
    (kom-ding-on-priority-break-doc . "")
    (kom-ding-on-new-letter-doc . "")

    (kom-check-for-new-comments-doc . "")
    (kom-check-commented-author-membership . "")
    (kom-confirm-multiple-recipients-doc . "")
    (kom-check-commented-author-membership-doc . "")
    (kom-complete-numbers-before-names-doc . "\
  När detta är påslaget och man matar in en text som kan tolkas både som
  ett mötesnamn eller ett mötesnummer vid inläsning av mötesnamn, så
  tolkar klienten det i första hand som ett nummer. När detta är avslaget
  så tolkas det i första hand som ett mötesnamn.")
    (kom-keep-alive-interval-doc . "\
  Antalet sekunder som skall gå mellan de meddelanden som används för att
  hålla nätverkskopplingen till LysKOM-servern aktiv. Om din förbindelse
  kopplar ned efter en stunds inaktivitet så kan du sätta detta till ungefär 
  hälften och sedan ge kommandot \"Håll förbindelsen aktiv\".")
    (kom-lost-session-notification-doc . "\
  Hur notifiering av onormalt avslutade session sker. Ett meddelande skrivs
  alltid ut i den avslutade sessionens LysKOM-buffert. Om denna inställning
  är satt till \"Pip\" kommer klienten dessutom att pipa. Om du har valt \"Visa
  notifiering i alla aktiva LysKOM-buffrar\" kommer klienten att pipa och
  skriva ut ett kort meddelande i alla aktiva LysKOM-buffrar.")
    (kom-text-no-prompts-doc . "\
  Den här inställningen styr vilka kommandon som alltid frågar efter 
  textnummer (om man inte anger ett explicit som prefixargument). För 
  kommandon som inte står med här används inbyggda standardinställningar.")
    (kom-saved-file-name-doc . "\
  Anger vilken fil som inlägg skall arkiveras till. Klienten kommer ändå
  att fråga, men detta blir det förifyllda svaret.")
    (kom-follow-attachments-doc . "\
  Anger om bilagor till importerade brev skall läsas som kommentarer. 
  Om detta är avstängt så läsmarkeras bilagor automatiskt, utan att
  man läser dem.")
    (kom-show-unread-in-frame-title-doc . "\
  Om detta är påslaget så visas \"(Olästa)\" i titelraden för de
  fönster vars valda buffert hör till en LysKOM-session med olästa. 
  Det är inte säkert att titelraden ändras om fönstret inte är öppet.")
    (kom-unread-mode-line-type-doc . "\
  Denna inställning bestämmer hur olästa indikeras i mode-raden. `Enkel'
  betyder en gemensam indikering för samtliga sessioner ifall du har 
  olästa texter eller brev. 

  `Visa alla sessioner med olästa' betyder att alla sessioner med olästa 
  texter indikeras separat. Förekomst med brev indikeras genom att 
  sessionsnamnet visas med stora bokstäver (till exempel LYSLYS istället 
  för LysLys). Om sessionsnamnet bara innehåller versaler (exempelvis 
  LYSLYS) visas det mellan asterisker (*LYSLYS*).

  Notera att denna inställning bör vara samma för alla aktiva 
  LysKom-sessioner varför det troligen är bäst att spara den i
  din .emacs.")

   (kom-created-texts-are-saved-doc . "\
  Om detta är ett filnamn så sparas alla inlägg man skriver till den
  filen i ungefär samma format som de har när man läser dem. Inlägg
  som inte kan skapas sparas inte.")
    (kom-confirm-add-recipients-doc . "\
  Om detta är påslaget och man adderar en mottagare till ett inlägg så
  frågar klienten om man vill att kommentarer till inlägget också
  skall skickas till den nya mottagaren. Om detta är avslaget så
  kommer kommentarer alltid att skickas även till den nya mottagaren. 
  Det är oftast bra att ha påslaget.")
    (kom-trim-buffer-minimum-doc . "\
  Om du begränsar LysKOM-buffertens storlek så måste den bli så här mycket
  för stor innan text i bufferten raderas. Variablen ska vara ett heltal,
  default är 4096.")
    (kom-dont-check-commented-authors-doc . "")
    (kom-print-relative-dates-doc . "\
  Tidsangivelsen för inlägg (och en del andra saker) kommer att bli
  \"idag\" alternativt \"igår\" om denna inställning är påslagen och
  inlägget skrevs idag alternativt igår. Om den är avslagen kommer ett
  absolut datum alltid att visas.")
    (kom-show-mx-date-as-local-doc . "\
  Om denna inställning är påslagen visas tidstämplar för importerade
  inlägg i lokal tidzon i stället för tidzonen i tidstämpeln.")
    (kom-print-seconds-in-time-strings-doc . "\
  Om denna inställning är påslagen kommer en del tidsangivelser (till
  exempel för visade inlägg och sända meddelanden) att inkludera
  sekunder.")
    (kom-review-uses-cache-doc . "\
  Om detta är påslaget så kommer kommandon som återser inlägg att 
  använda sparade kopior av inläggen om de finns. Det gör att kommandona
  går lite snabbare, men det finns en viss risk för att ett inlägg kan
  ha ändrats sedan kopian sparades.")
    (kom-review-marks-texts-as-read-doc . "\
  Om detta är påslaget så kommer kommandon som återser inlägg att 
  läsmarkera de inlägg som visas. I annat fall lämnar återsekommandona
  din läshistorik orörd, precis som vanligt.")
    (kom-auto-review-faqs-doc . "\
  Om detta är påslaget så kommer olästa FAQer inte att återses automatiskt
  när du loggar in eller går till ett möte med en oläst FAQ.")
    (kom-auto-list-faqs-doc . "\
  Om detta är påslaget så kommer olästa FAQer att listas automatiskt när
  du loggar in eller går till ett möte med en oläst FAQ.")
    (kom-extended-status-information-doc . "\
  Om detta är påslaget så visas extra statusinformation i kommandona
  Status (för) person, Status (för) möte och Status (för) servern 
  (om sådan information finns tillgänglig).")
    (kom-highlight-first-line-doc . "\
  Om detta är påslaget så visas första raden i varje inlägg (den med
  inläggsnummer, datum och författare) med ett annorlunda utseende än
  normal text.")
    (kom-highlight-dashed-lines-doc . "\
  Om detta är påslaget så visas raderna före och efter inläggstexten med
  ett annorlunda utseende än normal text.")
    (kom-highlight-text-body-doc . "\
  Om detta är påslaget så visas inläggstexter med ett annorlunda utseende
  än normal text.")
    (kom-async-highlight-dashed-lines-doc . "\
  Om detta är påslaget så visas raderna före och efter personliga, grupp-
  och alarmmeddelanden med ett annorlunda utseende än normal text.")
    (kom-async-highlight-text-body-doc . "\
  Om detta är påslaget så visas personliga, grupp- och alarmmeddelanden 
  med ett annorlunda utseende än normal text.")
    (kom-edit-hide-add-button-doc . "\
  Om detta är påslaget så kommer knappen [Addera...] att visas efter
  mottagare och tilläggsinformation när man skriver inlägg.")
    (kom-format-html-authors-doc . "\
  Den här inställningen styr vilka författares HTML-meddelanden som
  elispklienten kommer att försöka formattera.")
    (kom-format-show-images-doc . "\
  Den här inställningen styr om inlägg uppmärkta som bilder specialhanteras
  eller visas som traditionella inlägg.")
    (kom-keyboard-menu-immediate-selection-doc . "\
  Den här inställningen styr huruvida snabbtangenter automatiskt bekräftar
  val i textbaserade menyer eller inte. Om detta är påslaget så måste man
  alltid trycka retur för att välja i textbaserade menyer.")
    (kom-max-overlays-doc . "\
  Med denna inställning kan man begränsa antalet färgmarkeringar (som
  används bland annat för färgade inläggsbakgrunder). Genom att begränsa
  antalet markeringar kan scrollning gå mycket fortare. Varje inlägg
  behöver mellan 1 och 4 markeringar beroende på andra inställningar.")
    (kom-mark-read-texts-as-read-in-new-recipient-doc . "\
  Om detta är påslaget så läsmarkeras redan lästa inlägg automatiskt i
  möten de adderas till medan man är inloggad. Om man inte är inloggad
  så sker inte denna läsmarkering. Inlägg som adderas till ens brevlåda
  läsmarkeras dock aldrig automatiskt.")
    (kom-url-transformation-rules-doc . "\
  Regler för att transformera URLer innan de skickas till en webbläsare.
  \"Från\" är ett mönster (en regexp) som skall översättas till texten
  i \"Till\". Dokumentation om regexpar finns i Emacs-manualen. \"Till\" 
  kan innehålla följande specialtecken:

      \\& ersätts med den text som matchar regexpen
      \\N ersätts med den text som matchar \(...\)-uttryck N i regexpen
      \\\\ ersätts med en \\.")
    (kom-text-links-doc . "\
  Definition av textlänkar. Varje rad definierar en länk för inlägg i ett
  visst möte. Alla förekomster av länkmönstret blir URL-länkar genom att
  texten som länkmönstret matchar substitueras enligt länksubstitutionen.
  Den text som faktist visas som länk i LysKOM-bufferten bestäms av 
  inställningen \"Visa grupp\". Använd 0 för att visa hela den matchande
  texten eller en siffta från 1 till 9 för att visa den N-te gruppen i
  länkmönstret (använd 0 om du inte förstår vad som avses med detta). 
  Om inställningen \"Versaler och gemener lika\" är på så räknas versaker
  och gemener av samma bokstav som lika.

  Länksubstitutionen kan innehålla följande specialtecken:

      \\& ersätts med den text som matchar regexpen
      \\N ersätts med den text som matchar \(...\)-uttryck N i regexpen
      \\\\ ersätts med en \\.")

    (kom-active-face-doc . "\
  Teckensnitt för klickbara texter förutom URLer, möten, vänner och
  bekanta, idioter och knäppskallar, och inläggsnummer. Värdet skall 
  vara namnet på en så kallad `face'.")
    (kom-active-highlight-face-doc . "\ 
  Teckensnitt avsett för klickbara texter som skall markeras extra
  tydligt. Värdet skall vara namnet på en så kallad `face'.")    
    (kom-active-strikethrough-face-doc . "\ 
  Teckensnitt avsett för klickbara texter som skall markeras extra
  tydligt. Värdet skall vara namnet på en så kallad `face'.")
    (kom-url-face-doc . "\
  Teckensnitt för klickbara URLer.")
    (kom-me-face-doc . "\
  Teckensnitt för ens eget namn.")
    (kom-highlight-face-doc . "\
  Teckensnitt för markering av klickbara ytor när muspekaren förs över
  dem.")
    (kom-subject-face-doc . "\
  Teckensnitt för ärenderader.")
    (kom-text-no-face-doc . "\
  Teckensnitt för inläggsnummer.")
    (kom-friends-face-doc . "\
  Teckensnitt för vänner och bekanta. De personer som räknas upp i
  listan \"vänner och bekanta\" kommer att markeras med detta
  teckensnitt.")
    (kom-morons-face-doc . "\
  Teckensnitt för idioter och knäppskallar. De personer som räknas upp
  i listan \"Idioter och knäppskallar\" kommer att markeras med detta
  teckensnitt.")
    (kom-presence-face-doc . "\
  Teckensnitt för närvaromeddelanden.")
    (kom-first-line-face-doc . "\
  Teckensnitt för första raden i inläggshuvudet. Detta teckensnitt
  överlagras på standardteckensnitten i första raden, så de egenskaper
  som inte sätts av detta teckensnitt kommer inte att påverkas.")
    (kom-warning-face-doc . "\
  Teckensnitt för viktiga varningar.")
    (kom-mark-face-doc . "\
  Teckensnitt för diverse markeringar. Bland annat används detta
  teckensnitt för markeringen när elispklienten bläddrar mindre än en
  hel skärmsida. Detta teckensnitt överlagras på standardteckensnitten
  där det används, så de egenskaper som inte sätts av detta
  teckensnitt kommer inte att påverkas.")
    (kom-dim-face-doc . "\
  Teckensnitt för diverse diskreta texter. Detta teckensnitt används
  bland annat för att visa passiva medlemskap.")
    (kom-text-body-face-doc . "\
  Teckensnitt för inläggstexter. Detta teckensnitt överlagras på
  standardteckensnitten i inläggstexten, så de egenskaper som inte
  sätts av detta teckensnitt kommer inte att påverkas.")
    (kom-dashed-lines-face-doc . "\
  Teckensnitt för streckrader runt inlägg. Detta teckensnitt
  överlagras på standardteckensnitten i streckraden, så de egenskaper
  som inte sätts av detta teckensnitt kommer inte att påverkas.")
    (kom-async-text-body-face-doc . "\
  Teckensnitt för meddelanden. Detta teckensnitt överlagras på
  standardteckensnitten i meddelandet, så de egenskaper som inte
  sätts av detta teckensnitt kommer inte att påverkas.")
    (kom-async-dashed-lines-face-doc . "\
  Teckensnitt för streckrader runt meddelanden. Detta teckensnitt
  överlagras på standardteckensnitten i streckraderna, så de
  egenskaper som inte sätts av detta teckensnitt kommer inte att
  påverkas.") 
    (kom-lynx-terminal-doc . "\
  Lynx kan köras inuti Emacs eller i en xterm (eller motsvarande). Här
  väljer du vilket.")
    (kom-lynx-terminal-command-doc . "\
  Kommandot som används för att starta Lynx i Emacs terminal-mode. 
  Ska normalt vara så enkelt som \"lynx\".")
    (kom-lynx-xterm-command-doc . "\
  Kommandot som används för att starta Lynx i en xterm. Kommandot ska
  starta både xterm och lynx.")
    (kom-show-sync-messages-doc . "\
  Om denna inställning är påslagen så kommer klienten att visa ett
  meddelande i ekoarean nät servern signallerar att den håller på
  spara databasen.")
    (kom-highlight-conferences-doc . "\
  Denna inställning styr hur mötesnamn färgläggs. För varje val
  kan ett utseende anges. Utseendena är namn på faces i Emacs.")
    (kom-netscape-variant-doc . "\
  Denna inställning styr var elispklienten försöker öppna URLer. Hur
  bra de olika valen fungerar beror på vilken variant av Firefox, 
  Mozilla, Netscape eller Opera man använder, och till och med vilket 
  operativsystem man använder.")
    (kom-auto-confirm-new-conferences-doc . "\
  Om den här inställningen är påslagen så kommer kommandona \"Lista
  nya möten\" och \"Lista nya personer\" automatiskt markera nya
  möten och personer som kända.")
    (kom-text-properties-doc . "\
  Om den här inställningen är på så kommer klienten att använda olika
  typsnitt och färger till olika saker. Till exempel kommer textnummer
  och annat man kan klicka på att visas med en avvikande färg.")
    (kom-fontify-text-doc . "\
  Om den här inställningen är på så kommer fet och kursiv stil att
  användas i inläggstext. Till exempel så kommer ord och fraser
  omgivna av understrykningstecken att visas med kursiv text.")

    ;;
    ;; Tags for variables
    ;;

    (kom-ansaphone-replies-tag . "Detaljerade automatsvar")
    (kom-bury-buffers-tag . "Begrav buffertar när man byter LysKOM:")

    (kom-personal-messages-in-window-tag . "Personliga meddelanden:   ")
    (kom-customize-in-window-tag       . "Inställningar för LysKOM: ")
    (kom-write-texts-in-window-tag     . "Skriv inlägg:             ")
    (kom-prioritize-in-window-tag      . "Prioritera möten:         ")
    (kom-edit-filters-in-window-tag    . "Ändra filter:             ")
    (kom-view-commented-in-window-tag  . "Återse kommentarer:       ")
    (kom-list-membership-in-window-tag . "Lista medlemskap:         ")

    (kom-user-prompt-format-tag . "Promptformat:")
    (kom-user-prompt-format-executing-tag . "Promptformat vid körning:")
    (kom-anonymous-prompt-format-tag . "Promptformat (anonym):")
    (kom-anonymous-prompt-format-executing-tag . "Promptformat vid körning (anonym):")
    (kom-enabled-prompt-format-tag . "Promptformat (admin):")
    (kom-enabled-prompt-format-executing-tag . "Promptformat vid körning (admin):")

    (kom-higher-priority-breaks-tag . 
"Läs prioriterade texter:                  ")
    (kom-created-texts-are-read-tag . 
"Läsmarkera skapade texter:                ")
    (kom-default-mark-tag           . 
"Defaultmarkering:                         ")
    (kom-print-number-of-unread-on-entrance-tag . 
"Visa olästa när man går till ett möte:    ")
    (kom-follow-comments-outside-membership-tag .
"Följ kommentarskedjor utanför medlemskap: ")
    (kom-show-footnotes-immediately-tag .
"Visa fotnoter omedelbart:                 ")
    (kom-membership-default-priority-tag . 
"Prioritet för nya medlemskap:             ")
    (kom-membership-default-message-flag-tag . 
"Mottag gruppmeddelanden för nya medlemskap: ")
    (kom-dashed-lines-tag . 
"Streckade linjer kring inläggstexten:     ")
    (kom-autowrap-tag . 
"Automatisk radbrytning vid läsning:       ")
    (kom-autowrap-timeout-tag . 
"Tidsbegränsning av automatisk radbrytning: ")
    (kom-show-author-at-end-tag .
"Visa författarens namn efter inlägget:    ")

    (kom-truncate-threshold-tag .
"Korta av långa inlägg:                    ")
    (kom-truncate-show-lines-tag .
"Rader att visa vid avkortade inlägg:      ")
    
    (kom-reading-puts-comments-in-pointers-last-tag . "Kommentarslänkar visas:")
    (kom-read-depth-first-tag . "Läsordning:")
    (kom-read-related-first-tag . "Läs relaterade texter först:")
    (kom-deferred-printing-tag . "Fördröjda utskrifter:")
    (kom-continuous-scrolling-tag . "Omedelbar scrollning:")

    (kom-presence-messages-in-echo-area-tag . 
"Närvaromeddelanden i ekoarean:        ")
    (kom-presence-messages-in-buffer-tag .
"Närvaromeddelanden i LysKOM-bufferten:")
    (kom-page-before-command-tag . "Rensa skärmen:")

    (kom-remember-password-tag . "Kom ihåg LysKOM-sessionslösenordet:")

    (kom-idle-hide-tag . 
"Antal minuter en session får vara inaktiv och ändå visas: ")
    (kom-show-where-and-what-tag . 
"Visa varifrån personer är inloggade och vad de gör:         ")
    (kom-show-since-and-when-tag . 
"Visa när personer loggade in och när de var aktiva:         ")



    (kom-login-hook-tag . "Kommandon som körs vid login:")
    (kom-do-when-done-tag . "Kommandon som körs efter allt är utläst:")
    (kom-permissive-completion-tag . "Petig utfyllnad av namn:")
    (kom-show-personal-messages-in-buffer-tag . 
"Var visas meddelanden:           ")
    (kom-pop-personal-messages-tag . 
"Ploppa upp meddelandebufferten:  ")
    (kom-default-message-recipient-tag . 
"Defaultmottagare för meddelanden:")

    (kom-audio-player-tag . "Ljudspelarprogram:")
    (kom-ding-on-new-letter-tag        . "När det kommer brev:                ")
    (kom-ding-on-priority-break-tag    . "När det kommer prioriterade inlägg: ")
    (kom-ding-on-wait-done-tag         . "När man har väntat klart:           ")
    (kom-ding-on-common-messages-tag   . "Vid alarmmeddelanden:               ")
    (kom-ding-on-group-messages-tag    . "Vid gruppmeddelanden:               ")
    (kom-ding-on-personal-messages-tag . "Vid personliga meddelanden:         ")
    (kom-ding-on-no-subject-tag        . "När ärenderad saknas:               ")

    (kom-filter-outgoing-messages-tag . "Visa automatiska meddelanden:")
    (kom-friends-tag . "Vänner och bekanta:")
    (kom-morons-tag . "Idioter och knäppskallar:")
    (kom-person-list-1-tag . "Mötes/personlista (1):")
    (kom-person-list-2-tag . "Mötes/personlista (2):")
    (kom-person-list-3-tag . "Mötes/personlista (3):")
    (kom-person-list-4-tag . "Mötes/personlista (4):")
    (kom-url-viewer-preferences-tag . "Öppna URLer med följande program:")
    (kom-windows-browser-command-tag . "Kommando för att starta en WWW-läsare i Windows:")
    (kom-mosaic-command-tag . "Kommando för att starta NCSA Mosaic:")
    (kom-netscape-command-tag . "Kommando för att starta Netscape-liknande WWW-läsare:")
    (kom-galeon-command-tag . "Kommando för att starta Galeon:")

    (kom-symbolic-marks-alist-tag . "Symboliska markeringstyper:")

    (kom-cite-string-tag . "Citatmarkering:")
    (kom-confirm-multiple-recipients-tag . 
"Bekräfta multipla mottagare:                   ")
    (kom-check-commented-author-membership-tag . 
"Kontrollera kommenterad författares medlemskap:")
    (kom-check-for-new-comments-tag . 
"Kontrollera olästa kommentarer:                ")

    (kom-ansaphone-record-messages-tag . 
"Spara meddelanden då automatiskt svar är påslaget: ")
    (kom-ansaphone-show-messages-tag . 
"Visa meddelanden då automatiskt svar är påslaget:  ")
    (kom-ansaphone-default-reply-tag . "Svarsmeddelande:")


    (kom-inhibit-typeahead-tag . "Buffra tangenttryckningar:")
    (kom-max-buffer-size-tag . "Maximal buffertstorlek:")

    (kom-remote-control-tag .     "Fjärrstyrning:                     ")
    (kom-self-control-tag .       "Tillåt fjärrstyrning av mig själv: ")
    (kom-remote-controllers-tag . "Tillåtna fjärrstyrare:")

    (kom-customize-format-tag . "Visa hjälptexterna för inställningar:")
    (kom-default-language-tag . "Språklista:")
    (kom-ispell-dictionary-tag . "Ordlista:")
    (kom-show-namedays-tag .    "Visa dagens namn:")
    (kom-show-week-number-tag . "Visa veckonummer:")

    (kom-relogin-behaviour-tag . "Återanslut automatiskt:")
    (kom-relogin-inhibit-commands-tag . "Kommandon för vilka återanslutning aldrig sker automatiskt:")
    (kom-membership-default-placement-tag . "Placering av nya medlemskap:")
    (kom-show-imported-importer-tag . "Visa importör av importerade inlägg:")
    (kom-show-imported-envelope-sender-tag . "Visa avsändare av importerade inlägg:")
    (kom-show-imported-external-recipients-tag . "Visa externa mottagare till importerade inlägg:")
    (kom-show-imported-message-id-tag . "Visa meddelande-id för importerade inlägg:")
    (kom-agree-text-tag . "Text för Hålla med:")
    (kom-silent-ansaphone-tag . "Pip när automatsvar är påslaget:")
    (kom-default-session-priority-tag . "Läsnivå för nya sessioner:")
    (kom-unsubscribe-makes-passive-tag . "Utträda ur möte gör om medlemskap till passiva:")
    (kom-review-priority-tag . "Prioritet för återsekommandon:")
    (kom-show-creating-software-tag . "Visa skapande klient:")
    (kom-text-footer-format-tag . "Format för inläggsfot:")
    (kom-long-lines-tag . "Långa streckade linjer:")
    (kom-postpone-default-tag . "Skönsvärde för uppskjuta läsning:")
    (kom-allow-incompleteness-tag . "Tillåt ofullständig information om olästa:")
    (kom-smileys-tag . "Visa gladmän grafiskt:")
    (kom-ignore-message-senders-tag . "Visa inte meddelanden från:")
    (kom-ignore-message-recipients-tag . "Visa inte meddelanden till:")
    (kom-text-footer-dash-length-tag . "Inläggsfotens längd:")
    (kom-text-header-dash-length-tag . "Längden på linjen ovanför inläggstexten:")
    (kom-show-personal-message-date-tag . "Visa datum för alla meddelanden:")
    (kom-w3-simplify-body-tag . "Visa HTML utan standardfärger:")
    (kom-mercial-tag . "Text när allt är utläst:")
    (kom-session-nickname-tag . "Hur bestäms sessionsnamn:")
    (kom-server-priority-tag . "Sessionsprioritet:")   
    (kom-server-priority-breaks-tag . "Byt till LysKOM med olästa:")
    (kom-complete-numbers-before-names-tag . "Läs mötesnummer före mötesnamn:")
    (kom-keep-alive-interval-tag . "Intervall för håll förbindelsen igång:")
    (kom-lost-session-notification-tag . "Notifiering av onormalt avslutade sessioner:")
    (kom-text-no-prompts-tag . "Hur kommandon frågar efter textnummer:")
    (kom-saved-file-name-tag . "Fil att arkivera inlägg i:")
    (kom-follow-attachments-tag . "Läs importerade bilagor som vanligt:")
    (kom-show-unread-in-frame-title-tag . "Visa olästamarkering i titelraden:")
    (kom-unread-mode-line-type-tag . "Typ av olästaindikering i mode-raden:")
    (kom-created-texts-are-saved-tag . "Spara författade inlägg:")
    (kom-confirm-add-recipients-tag . "Fråga om kommentarer skall sändas till nya mottagare:")
    (kom-trim-buffer-minimum-tag . "Minsta storlek av LysKOM-bufferten som tas bort:")
    (kom-dont-check-commented-authors-tag . "Författare som inte kontrolleras:")
    (kom-print-relative-dates-tag . "Visa relativa datumangivelser:")
    (kom-show-mx-date-as-local-tag . "Visa tidstämplar i lokal tidzon:")
    (kom-print-seconds-in-time-strings-tag . "Inkludera sekunder i tidsangivelser:")
    (kom-review-uses-cache-tag . "Återsekommandon använder sparade inlägg:")
    (kom-review-marks-texts-as-read-tag . "Återsekommandon läsmarkerar visade texter:")
    (kom-auto-review-faqs-tag . "Visa nya FAQer automatiskt:")
    (kom-auto-list-faqs-tag . "Lista nya FAQer automatiskt:")
    (kom-extended-status-information-tag . "Utökad statusinformation:")
    (kom-highlight-first-line-tag . "Annorlunda bakgrundsfärg på första raden:")
    (kom-highlight-dashed-lines-tag . "Annorlunda bakgrundsfärg före och efter inläggstexten:")
    (kom-highlight-text-body-tag . "Annorlunda bakgrundsfärg på inläggstexten:")
    (kom-async-highlight-dashed-lines-tag . "Annorlunda bakgrundsfärg före och efter meddelanden:")
    (kom-async-highlight-text-body-tag . "Annorlunda bakgrundsfärg på meddelanden:")
    (kom-edit-hide-add-button-tag . "Visa Addera-knappen när man skriver inlägg:")
    (kom-format-html-authors-tag . "Formattera HTML-meddelanden per författare:")
    (kom-format-show-images-tag . "Visa bildinlägg som bilder:")
    (kom-keyboard-menu-immediate-selection-tag . "Snabbval i textmenyer kräver bekräftelse:")
    (kom-max-overlays-tag . "Högsta antal färgmarkeringar:")
    (kom-mark-read-texts-as-read-in-new-recipient-tag . "Markera lästa inlägg lästa även i nya mottagare:")
    (kom-url-transformation-rules-tag . "Transformation av URLer:")
    (kom-text-links-tag . "Textlänkar:")
    (kom-active-face-tag . "Övriga klickbara texter:")
    (kom-active-highlight-face-tag . "Tydligare markerade klickbara texter:")
    (kom-active-strikethrough-face-tag . "Överstrukna klickbara texter:")
    (kom-url-face-tag . "Klickbara URLer:")
    (kom-me-face-tag . "Mitt eget namn:")
    (kom-highlight-face-tag . "Markering av klickbara ytor:")
    (kom-subject-face-tag . "Ärenderader:")
    (kom-text-no-face-tag . "Inläggsnummer:")
    (kom-friends-face-tag . "Vänner och bekanta:")
    (kom-morons-face-tag . "Idioter och knäppskallar:")
    (kom-presence-face-tag . "Närvaromeddelanden:")
    (kom-first-line-face-tag . "Första raden i inläggshuvudet:")
    (kom-warning-face-tag . "Viktiga varningar:")
    (kom-mark-face-tag . "Diverse markeringar:")
    (kom-dim-face-tag . "Diverse diskreta texter:")
    (kom-text-body-face-tag . "Inläggstexter:")
    (kom-dashed-lines-face-tag . "Streckrader runt inlägg:")
    (kom-async-text-body-face-tag . "Meddelanden:")
    (kom-async-dashed-lines-face-tag . "Streckrader runt meddelanden:")
    (kom-lynx-terminal-command-tag . "Kommando för Lynx i Emacs:")
    (kom-lynx-terminal-tag . "Var ska Lynx köra:")
    (kom-lynx-xterm-command-tag . "Kommando för Lynx i xterm:")
    (kom-show-sync-messages-tag . "Visa databasen-sparar-meddelanden:")
    (kom-highlight-conferences-tag . "Färgläggning av namn:")
    (kom-netscape-variant-tag . "Var ska Netscape-liknande WWW-läsare öppna URLer:")
    (kom-auto-confirm-new-conferences-tag . "Markera alltid nya möten och personer som kända:")
    (kom-text-properties-tag . "Använd typsnitt och färger:")
    (kom-fontify-text-tag . "Visa fetstil och kursiv text i inlägg:")
    )
)

  
;;;; ============================================================
;;;; The default Ansaphone message goes here. The more complex 
;;;; message specification probably should too, but it's not here
;;;; yet. People who know how to use it are smart enough to do it
;;;; right.

(lyskom-language-var local kom-ansaphone-default-reply sv
  "Jag läser inte LysKOM just nu. Skicka gärna ett brev i stället.")

;;;; ============================================================
;;;; Other language-dependent variables
;;;;

(lyskom-language-var local kom-ispell-dictionary sv
  "svenska")


;;;; ================================================================
;;;; Tell phrases should really be configured with the default
;;;; language used at the server and not for person reading if they
;;;; happen to differ. This is of coarse because they are sent to the
;;;; server for everybody else to see.

;;;; Aronsson was here 4 DEC 1990, thus creating version 0.18
					; Created *-tell-*


;;;; kom-tell-phrases 

;;; To coders of the elisp-client:
;;; if you change kom-tell-phrases here, make sure the list of valid
;;; keywords is updated it in vars.el.
;;; lyskom-tell-phrases-validation-keyword-list, lyskom-tell-phrases-validate

(eval-when-compile (defvar kom-tell-phrases))

(lyskom-language-strings local kom-tell-phrases sv
 '((kom-tell-silence		. "")   ; Why ?
   (kom-tell-send		. "Försöker lägga in en text.")
   (kom-tell-login		. "Loggar in.")
   (kom-tell-read		. "Läser.")
   (kom-tell-1st-pres		. "Skriver den första presentationen.")
   (kom-tell-write-comment	. "Skriver en kommentar.")
   (kom-tell-write-footnote	. "Skriver en fotnot.")
   (kom-tell-write-letter	. "Skriver ett brev.")
   (kom-tell-write-reply	. "Skriver ett privat svar.")
   (kom-tell-write-text	. "Skriver inlägg.")
   (kom-tell-conf-pres		. "Skriver presentation för ett nytt möte.")
   (kom-tell-recover		. "Återstartar kom. Suck.")
   (kom-tell-wait		. "Väntar.")
   (kom-tell-regret		. "Ångrar sig och slänger inlägget.")
   (kom-tell-review		. "Återser.")
   (kom-tell-change-name       . "Ändrar sitt namn till något annat.")
   (kom-tell-change-supervisor . "Ändrar organisatör för något.")
   (kom-tell-next-lyskom       . "Hoppar till ett annat LysKOM.")))

(if (and (boundp 'kom-tell-phrases)
         kom-tell-phrases)
    (lyskom-language-strings local kom-tell-phrases sv
      (mapcar (function 
               (lambda (x)
                 (cond ((and (consp x)
                             (symbolp (car x))
                             (stringp (cdr x))) x)
                       ((and (consp x)
                             (symbolp (car x))
                             (consp (cdr x))
                             (stringp (car (cdr x))))
                        (cons (car x) (car (cdr x))))
                       (t nil))))
              kom-tell-phrases)))


;; Placed here because this must NOT be evaluated before 
;; kom-tell-phrases is defined:

(lyskom-language-var local kom-mercial sv
                     (lyskom-get-string 'kom-tell-wait 'kom-tell-phrases))

;;;


(lyskom-language-strings local lyskom-error-texts sv
  '((error-0 . "Inget fel har inträffat")
    (error-2 . "Ännu ej implementerat i servern")
    (error-3 . "Inte längre implementerat i servern")
    (error-4 . "Felaktigt lösenord")
    (error-5 . "Strängen för lång")
    (error-6 . "Du är inte inloggad")
    (error-7 . "Ingen får logga in i LysKOM just nu")
    (error-8 . "Du försökte använda möte nummer 0")
    (error-9 . "Odefinierat eller hemligt möte")
    (error-10 . "Odefinierad eller hemlig person")
    (error-11 . "Ingen skriv- eller läsrättighet")
    (error-12 . "Otillåten operation")
    (error-13 . "Du är inte medlem i mötet")
    (error-14 . "Inget sådant textnummer")
    (error-15 . "Du kan inte använda globalt textnummer 0")
    (error-16 . "Inget sådant lokalt textnummer")
    (error-17 . "Du kan inte använda lokalt textnummer 0")
    (error-18 . "Namnet för kort, för långt eller innehåller felaktiga tecken")
    (error-19 . "Index utanför gränserna")
    (error-20 . "Mötet existerar redan")
    (error-21 . "Personen existerar redan")
    (error-22 . "Hemligt, men ej lässkyddat")
    (error-23 . "Du får inte ändra person/mötesflaggan")
    (error-24 . "Fel i databasen. Attans otur.")
    (error-25 . "Otillåten mottagartyp eller kommentarslänk")
    (error-26 . "Otillåten infotyp. (Bug i klienten)")
    (error-27 . "Redan mottagare till denna text")
    (error-28 . "Redan kommentar till denna text")
    (error-29 . "Redan fotnot till denna text")
    (error-30 . "Inte mottagare till denna text")
    (error-31 . "Inte kommentar till denna text")
    (error-32 . "Inte fotnot till denna text")
    (error-33 . "För många mottagare")
    (error-34 . "För många kommentarer")
    (error-35 . "För många fotnoter")
    (error-36 . "För många markeringar")
    (error-37 . "Du är inte författare till texten")
    (error-38 . "Du kan inte koppla upp dig till servern")
    (error-39 . "Minnet slut")
    (error-40 . "Servern har ballat ur")
    (error-41 . "Klienten tror att servern säger att den inte förstår klienten")
    (error-42 . "Ingen sådan session finns")
    (error-43 . "Ogiltigt reguljärt uttryck")
    (error-44 . "Texten är inte markerad")
    (error-45 . "Tillfälligt fel. Försök senare")
    (error-46 . "Serven vägrade ta emot en array")
    (error-47 . "Mottagaren tar inte emot anonyma texter")
    (error-48 . "Otillåten tilläggsinformation")
    (error-49 . "Otillåten ändring av tilläggsinformation")
    (error-50 . "Okänt asynkront meddelande")
    (error-51 . "Internt fel i servern")
    (error-52 . "Funktionen avstängd i servern")
    (error-53 . "Mottagaren var kanske inte inloggad")
    (error-54 . "Otillåten typ på medlemskap")
    (error-55 . "Ogiltigt intervall")
    (error-56 . "Ogiltig kombination av intervall")
    (error-57 . "Odefinierad mätning")
    (error-58 . "Otillåten prioritet")
    (error-59 . "Otillåten viktning")
    (error-60 . "Viktning noll ej giltig")
    (error-61 . "Ogiltigt sanningsvärde")

    (error--1 . "Du är inte medlem i något mottagarmöte")
))

(lyskom-language-var global lyskom-unread-mode-line sv
  (lyskom-make-lyskom-unread-mode-line))

(lyskom-language-var global lyskom-unread-title-format sv
  (lyskom-make-lyskom-unread-title-format))

(lyskom-language-var local lyskom-help-data sv lyskom-sv-help-data)

(provide 'lyskom-language-sv)

;;; swedish-strings.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: english-strings.el
;;;;
;;;; This file contains all strings in the LysKOM elisp client.
;;;; Language:     English.
;;;; ================================================================
;;;;
;;;; Translation from swedish-strings.el: David Byers
;;;;

(require 'lyskom-vars "vars")
(require 'lyskom-language "language")


;;; ================================================================
;;; The language definition

(lyskom-define-language 'en
                        'iso-8859-1
                        "English"
                        "Engelska")


;;; ================================================================
;;; lyskom-edit-mode-map                             English version

(defvar lyskom-en-edit-mode-map nil)
(lyskom-language-keymap lyskom-edit-mode-map en lyskom-en-edit-mode-map)

;;; Set the keymap for lyskom-edit-mode

(defvar lyskom-en-edit-prefix)
(defvar lyskom-en-edit-review-prefix)
(defvar lyskom-en-edit-insert-prefix)
(defvar lyskom-en-edit-aux-prefix)
(defvar lyskom-en-edit-add-prefix)

(if lyskom-en-edit-mode-map
    nil
  (setq lyskom-en-edit-mode-map (make-sparse-keymap))
  (define-prefix-command 'lyskom-en-edit-prefix)
  (define-prefix-command 'lyskom-en-edit-review-prefix)
  (define-prefix-command 'lyskom-en-edit-insert-prefix)
  (define-prefix-command 'lyskom-en-edit-aux-prefix)
  (define-prefix-command 'lyskom-en-edit-add-prefix)

  (define-key lyskom-en-edit-mode-map (kbd (lyskom-keys (lyskom-xemacs-or-gnu 'button2 'button2up))) 'kom-button-click-or-yank)
  (define-key lyskom-en-edit-mode-map (kbd (lyskom-keys (lyskom-xemacs-or-gnu 'button2up 'button2))) 'kom-mouse-null)
  (define-key lyskom-en-edit-mode-map (kbd (lyskom-keys 'button3)) 'kom-popup-menu)
  (define-key lyskom-en-edit-mode-map (kbd (lyskom-keys 'button3up)) 'kom-mouse-null)
  (define-key lyskom-en-edit-mode-map (kbd "*")     'kom-button-press-or-self-insert-command)
  (define-key lyskom-en-edit-mode-map (kbd "=")     'kom-menu-button-press-or-self-insert-command)
  (define-key lyskom-en-edit-mode-map (kbd "TAB")   'kom-edit-next-button-or-self-insert)
  (define-key lyskom-en-edit-mode-map (kbd "M-TAB") 'kom-edit-prev-button)
  (define-key lyskom-en-edit-mode-map (kbd "<S-tab>") 'kom-edit-prev-button)

  (define-key lyskom-en-edit-mode-map (kbd "C-c") 'lyskom-en-edit-prefix)
  (define-key lyskom-en-edit-prefix (kbd "C-x") 'lyskom-en-edit-aux-prefix)
  (define-key lyskom-en-edit-prefix (kbd "C-r") 'lyskom-en-edit-review-prefix)
  (define-key lyskom-en-edit-prefix (kbd "C-i") 'lyskom-en-edit-insert-prefix)
  (define-key lyskom-en-edit-prefix (kbd "TAB") 'lyskom-en-edit-insert-prefix)
  (define-key lyskom-en-edit-prefix (kbd "C-a") 'lyskom-en-edit-add-prefix)

  (define-key lyskom-en-edit-prefix (kbd "?")	'lyskom-help)
  (define-key lyskom-en-edit-prefix (kbd "*") 'kom-button-press)
  (define-key lyskom-en-edit-prefix (kbd "=") 'kom-menu-button-press)
  (define-key lyskom-en-edit-prefix (kbd "C-y") 'kom-edit-insert-commented)
  (define-key lyskom-en-edit-prefix (kbd "C-b")     'kom-edit-insert-buglist)
  (define-key lyskom-en-edit-prefix (kbd "C-c")	'kom-edit-send)
  (define-key lyskom-en-edit-prefix (kbd "C-s") 'kom-edit-ispell-message)
  (define-key lyskom-en-edit-prefix (kbd "C-k")	'kom-edit-quit)

  (define-key lyskom-en-edit-review-prefix (kbd "?") 'lyskom-help)
  (define-key lyskom-en-edit-review-prefix (kbd "C-c") 'kom-edit-show-commented)

  (define-key lyskom-en-edit-insert-prefix (kbd "?") 'lyskom-help)
  (define-key lyskom-en-edit-insert-prefix (kbd "C-c") 'kom-edit-insert-commented)
  (define-key lyskom-en-edit-insert-prefix (kbd "C-y") 'kom-edit-insert-commented)
  (define-key lyskom-en-edit-insert-prefix (kbd "1") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "2") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "3") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "4") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "5") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "6") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "7") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "8") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "9") 'kom-edit-insert-digit-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "SPC") 'kom-edit-insert-text)
  (define-key lyskom-en-edit-insert-prefix (kbd "C-l") 'kom-edit-insert-link)
  (define-key lyskom-en-edit-insert-prefix (kbd "C-i") 'kom-edit-insert-last-info-nodename)

  (define-key lyskom-en-edit-add-prefix (kbd "C-r") 'kom-edit-add-recipient)
  (define-key lyskom-en-edit-add-prefix (kbd "C-c") 'kom-edit-add-copy)
  (define-key lyskom-en-edit-add-prefix (kbd "<RET>") 'kom-edit-move-text)
  (define-key lyskom-en-edit-add-prefix (kbd "C-x") 'kom-edit-add-cross-reference)
  (define-key lyskom-en-edit-add-prefix (kbd "C-p") 'kom-edit-add-personal-comments)
  (define-key lyskom-en-edit-add-prefix (kbd "C-n") 'kom-edit-add-no-comments)
  (define-key lyskom-en-edit-add-prefix (kbd "C-r") 'kom-edit-add-read-confirm-request)
  (define-key lyskom-en-edit-add-prefix (kbd "?") 'lyskom-help))



(lyskom-language-missing-string lyskom-message cgdag en)
(lyskom-language-missing-string lyskom-message sixjune en)
(lyskom-language-missing-string lyskom-message holdnose en)
(lyskom-language-missing-string lyskom-message 13dayxmas en)
(lyskom-language-missing-string lyskom-message 20dayxmas en)
(lyskom-language-missing-string lyskom-message kyndeldag en)
(lyskom-language-missing-string lyskom-message skottdag en)
(lyskom-language-missing-string lyskom-message intwomday en)
(lyskom-language-missing-string lyskom-message mariebdag en)
(lyskom-language-missing-string lyskom-message johannesddag en)
(lyskom-language-missing-string lyskom-message fnday en)
(lyskom-language-missing-string lyskom-message allhelgonadag en)
(lyskom-language-missing-string lyskom-message varnlosdag en)
(lyskom-language-missing-string lyskom-message luciaday en)

(lyskom-language-ending-mismatch lyskom-message carbon-copy-prefix en sv)
(lyskom-language-ending-mismatch lyskom-message blank-carbon-copy-prefix en sv)
(lyskom-language-ending-mismatch lyskom-message newyeareve en sv)

;;; Formely known as lyskom-strings
(lyskom-language-strings local lyskom-message en 
  '(
    ; From vars.el: 
    ; From komtypes.el: nil
    ; From clienttypes.el: nil
    ; From startup.el:
    (server-q . "LysKOM server? ")
    (try-connect . "LysKOM elisp client version %#1s.\nAttempting to connect to %#2s.\n")
    (too-old-server . "The server is too old for this version of the client.")
    (connection-done . "Connection established. Server version is %#1s.\n\n")
    (what-is-your-name . "What is your name? ")
    (password . "Your password? ")
    (wrong-password . "Incorrect password.\n")
    (wrong-password-help . "
If you have forgotten your password you may be able to get a new one
by e-mailing the site administrators at one of the addresses listed
below:\n")
    (wrong-password-email . "* %#1t\n")
    (are-logged-in . "You have entered LysKOM. Please wait...\n")
    (you-have-motd . "\nYou have a notice on your mailbox:\n\n")
    (server-has-motd . "There is a notice on the server:\n")
    (lyskom-motd-was-garbed . "\nThe login message does not exist!
The message that was supposed to be shown after login has disappeared.
Please contact the LysKOM administrator.\n")

    (first-greeting . "%#1s
This appears to be the first time you use LysKOM. Welcome!
Please make sure you have spelled your name correctly. You should use your
full name and organisation, eg. \"Joe Hacker, MIT\". If your name is spelled
incorrectly, or you wish to change it, answer 'no' to the question below.

At present the LysKOM server stores most of the information so that
anybody can read it. Only passwords are encrypted.

If you are uncertain about how to use LysKOM, you can retrieve a manual by
anonymous ftp to ftp.lysator.liu.se. Ask your system administrator for help
on how to do this. You can also type \"?\" for the command \"Help\".
")

    (is-name-correct . "Is the name %#1s correct? ")
    (personal-password . "Enter a personal password: ")
    (repeat-password . "Repeat for confirmation: ")
    (repeat-failure . "The passwords were not the same.\n")

    (could-not-create-you .  "LysKOM couldn't create that user.\n")
    (presentation-subject . "%#1s")
    (presentation-form . "\
Name:      
Address:   
           
Telephone: 
E-mail:    
WWW:       

Other:     ")
    (presentation-help . "You are writing your presentation.\n")
    (not-present-anywhere . "Not in any conference.")
    (unknown-person . "Unknown user")
    (in-secret-conference . "Secret conference (%#1d).")
    (start-new-session-same-server
     . "You are already connected to that server. Do you want a new session? ")
    (new-session-in-buffer . "\n\n---- New session at %s ----\n\n")
    (warning-about-uncompiled-client . "
NOTE: The LysKOM client is not compiled. It is recommended to run a compiled
client for performance reasons. Read the accompanying README to learn how to
do that.
")

    ; From internal.el:
    (shaky-tcp . "At the moment I can't reach the server. The TCP/IP connection is shaky%#1s")
    (retrying-tcp . "Retrying.")

    ; From parse.el:
    (protocol-error . "protocol error: %s")

    ; From services.el:
    (interrupted . "Interrupted\n")

    ; From cache.el:
    ; No entries.

    ; From commands1.el:
    (appreciation . 
"You are a very special person, beautiful and wise, respected by
everybody around you. You are doing a splendid job. Many people love
you, body and soul. You make life easier for others. You are a very
warm and sensitive person.

Be proud of being You! You have a very good reason.\n\n")
    (abuse . 
"You are a nuisance, ugly and stupid, disrespected by everybody around
you. You are doing a worthless job. Many people hate you, body and
soul. You make life harder for others. You are a very cold and
unfeeling person.

Be ashamed of being You! You have a very good reason.\n\n")


    (what-conf-to-delete . "Conference/user to delete: ")
    (what-conf-to-change . "Conference to modify: ")
    (confirm-delete-pers-or-conf . "Really delete %#1s %#2s? ")
    (the-pers . "the user")
    (the-conf . "the conference")
    (deletion-not-confirmed . "Deletion aborted\n")
    (somebody-else-deleted-that-conf . "Somebody else just deleted the conference.\n")
    (conf-is-deleted . "OK, %#1s is now deleted.\n")
    (you-could-not-delete . "%#1M can't be deleted by you.\n")
    (you-have-deleted-yourself . "You have deleted yourself.\n")

    (what-text-to-delete . "Remove which text? ")
    (delete-marked-text . "The text is %#1s. Delete anyway? ")
    (delete-marked-by-you . "marked by you")
    (delete-marked-by-you-and-others . "marked by you and %#1?d%[someone else%]%[%#1d others%]")
    (delete-marked-by-several . "marked by %#1d user%#1?d%[%]%[s%]")
    (delete-commented-text-help . "\
The text you are about to remove has comments. If you remove it the 
comments will lose their context, and this annoys and even angers
many readers. You should consider moving it to another conference
instead of removing it.")
    (delete-commented-text . "The text has comments. Remove anyway? ")
    (really-delete-commented-text . "Removing a commented text may annoy many readers. Remove anyway?")
    (deleting-text . "Removing text %#1:n...")

    (presentation-for-whom . "View presentation for which conference/user? ")
    (text-to-see-author-of . "Review presentation of author of which text? ")
    (unread-presentation-for-whom . "Unread presentation for which conference/user? ")
    (text-to-unread-author-of . "Mark presentation of author of which text as unread? ")
    (somebody-deleted-that-conf . "Somebody just deleted that conference.\n")
    (review-presentation-of . "Review presentation of %#1M.\n")
    (has-no-presentation . "%#1:M has no presentation.\n")

    (have-to-read . "You must read a text first.\n")

    (no-comment-to . "There is no commented text.\n")
    (no-text-at-point . "There is no text at point.\n")
    (what-ancestor . "Which of the commented texts do you want? ")

    (who-letter-to . "Send a letter to whom? ")
    (who-send-text-to . "Send text to which conference? ")
    (has-motd . "%#1P has a notice on his/her mailbox:\n\n")
    (motd-persist-q . "Do you still want to write the text? ")

    (who-to-add . "Whom do you want to add? ")
    (where-to-add . "To which conference? ")
    (where-to-add-self . "Join which conference? ")
    (priority-q . "Priority of your membership? (0 (low) - 255 (high)) ")
    (other-priority-q . "Priority of the membership? (0 (low) - 255 (high)) ")
    (done . "done.\n")
    (cancelled . "cancelled.\n")
    (nope . "didn't work.\n")
    (add-already-member . "%#1P is already a member of %#2M.\n")
    (you-already-member . "You are already a member of %#1M.\n")

    (cant-find-supervisor . "Can't find supervisor of %#1M.\n")
    (is-read-protected-contact-supervisor . "%#1M is closed.
Send a letter to  %#2P to apply for membership.\n")

    (conf-does-not-exist . "\nThe conference doesn't exist.\n")

    (who-to-exclude . "Who do you want to remove? ")
    (where-from-exclude . "From which conference? ")

    (leave-what-conf . "Leave which conference? ")

    (error-fetching-person . "Error retrieving user.\n")
    (error-fetching-conf . "Error retrieving conference.\n")

    (name-of-conf . "Conference name? ")
    (anyone-member . "May anyone join? ")
    (secret-conf . "Secret conference? ")
    (comments-allowed . "Are comments allowed? ")
    (anonymous-allowed . "Are anonymous texts allowed? ")
    (secret-members-allowed . "Are secret members permitted? ")
    (what-comment-no . "Comment text number: ")
    (what-footnote-no . "Footnote text number: ")
    (what-private-no . "Private reply to text number: ")

    (quit-in-spite-of-unsent . "You have an unsent text. Do you really want to quit? ")
    (really-quit . "Do you really want to quit LysKOM? ")
    (session-ended . "
--------------------------------------------
  LysKOM session finished
  You are now disconnected from the server
--------------------------------------------
")
    (session-ended-long . "
-------------------------------------------------------------------------------
  LysKOM session finished
  You are now disconnected from the server
-------------------------------------------------------------------------------
")
    (session-auto-ended . "
============================================================
Disconnecting from LysKOM since all connections are in use
and you have finished reading. Please come back later.
============================================================\n\n")
    (session-auto-ended-long . "
===============================================================================
Disconnecting from LysKOM since all connections are in use
and you have finished reading. Please come back later.
===============================================================================
\n")
    (what-to-change-pres-you . "Change presentation of whom/what: ")
    (what-to-change-faq-you . "Change FAQ for which conference: ")
    (who-to-put-motd-for . "Post notice on whom/what: ")

    (what-to-set-pres-you . "Set presentation for whom/what: ")
    (what-text-to-set-as-pres-no . "Which text do you want as the new presentation: ")
    (what-to-set-motd-you . "Set notice on whom/what: ")
    (what-text-to-set-as-motd-no . "Which text do you want as the new notice: ")
    (conf-already-has-pres . "The conference/person already has a presentation. Continue anyway? ")
    (conf-already-has-motd . "The conference/person already has a notice. Continue anyway? ")
    (setting-conf-pres . "Setting presentation of %#1M to text %#2n...")
    (setting-conf-motd . "Setting notice for %#1M to text %#2n...")

    (who-to-remove-pres-for . "Remove presentation from whom: ")
    (removing-pres-for-conf . "Removing presentation (text %#2n) from %#1M...")

    (cant-get-conf-stat . "Cannot get the status of that conference.\n")
    (go-to-conf-p . "Go to conference: ")
    (want-become-member . "Do you want to join? ")
    (no-ok . "Okiedokie, whatever you say.\n")

    (who-to-remove-motd-for . "Remove notice from whom/what: ")

    (conf-all-read . "no unread texts")
    (no-in-conf . "You are not present in any conference.\n")

    (search-for-pers . "Enter search key (RETURN for all users): ")
    (search-for-conf . "Enter search key (RETURN for all conferences): ")
    (search-re . "Enter search regexp: ")
    (search-re-for-what . "What do you want to search for? ")
    (search-re-persons . "ppersons")
    (search-re-confs . "cconferences")
    (search-re-all . "bboth")

    (no-matching-confs . "No conferences match \"%#1s\".\n")
    (no-matching-perss . "No persons match \"%#1s\".\n")
    (no-matching-anys . "No persons or conferences match \"%#1s\".\n")
    (no-confs-exist . "There are no conferences in the database.\n")
    (no-pers-confs-exist . "There are no persons or conferences in the database.\n")
    (list-confs-created-by . "List owned conferences for: ")
    (listing-confs-created-by . "Listing owned conferences for %#1P\
    C=Created, O=Supervisor, S=Letterbox is super conf; S=Secret, P=Protected\n")
    (list-pers-confs-created-by . "List owned conferences and persons for: ")
    (getting-all-confs . "Retrieving a list of all conferences from the server...")
    (getting-all-pers-confs . "Retrieving a list of all persons and conferences from the server...")
    (getting-all-confs-done . "Retrieving a list of all conferences from the server...done")
    (getting-all-pers-confs-done . "Retrieving a list of all persons and conferences from the server...done")
    (finding-created-confs . "Finding owned conferences (%#1d of %#2d done)")
    (finding-created-pers-confs . "Finding owned persons and conferences (%#1d of %#2d done)")
    (no-created-confs . "%#1P is not creator, supervisor or super conference for any conference.\n")

    (name-to-be-changed . "Name to change: ")
    (no-such-conf-or-pers . "The conference or user doesn't exist.\n")
    (new-name . "New name: ")
    (new-paren . "New parenthesis: ")
    (too-many-parens-in-name . "Multiple parentheses in name. Use \"Change name\" instead.\n")
    (no-paren-in-name . "The name doesn't contain a parenthesis.\n")
    (who-to-change-supervisor-for . "Change supervisor of whom/what? ")
    (new-supervisor . "New supervisor: ")
    (text-to-mark . "Mark which text? ")
    (text-to-unmark . "Unmark which text? ")
    (what-mark . "Set which mark type (name or 0-255)? ")
    (erroneous-mark . "Erroneous mark type.\n")
    (want-to-create-symbolic-mark . "The mark type \"%#1s\" does not exist. Do you want to create it? ")
    (creating-symbolic-mark-type . "Creating mark type \"%#1s\" (%#2d).\n")
    (no-mark-types-left . "Sorry, there are no free mark types. Remove an old one first.")
    (unmarking-textno . "Unmarking text %#1n...")
    (marking-textno . "Marking text %#1n with mark %#2?b%[\"%#2s\" (%#3d)%]%[%#3d%]...")
    (list-which-mark . "List texts with which mark type (name or 0-255, RET for all)? ")

    (new-passwd-again . "Repeat the new password for confirmation: ")
    (what-mark-to-unread . "Mark which mark type as unread (name or 0-255, RET for all)? ")
    (what-mark-to-view . "Review which mark type (name or 0-255, RET for all)? ")
    (whos-passwd . "Change password for whom? ")
    (old-passwd . "Your current password: ")
    (new-passwd . "The new password: ")
    (changing-passwd . "Changing password...")
    (retype-dont-match . "You didn't reenter the same passwrod. Try again.\n")
    (palindrome . " (a palindrome!)")
    (lyskom-name . "User")
    (is-in-conf . "In conference")
    (from-machine . "At")
    (is-doing . "Activity")
    (connection-time . "Connected")
    (active-last . "Active last")
    (active . "Active")
    (lyskom-client . "Client")
    (move-roots-q . "Text %#1n is a footnote/attachment. Move %#2n%#3?b%[ et al.%]%[%] instead? ")
    (add-roots-q . "Text %#1n is a footnote/attachment. Add to %#2n%#3?b%[ et al.%]%[%] instead? ")
    (sub-roots-q . "Text %#1n is a footnote/attachment. Remove from %#2n%#3?b%[ et al.%]%[%] instead? ")
    (text-to-add-recipient . "Add recipient to which text:")
    (text-to-add-copy . "Add recipient of carbon copy to which text:")
    (text-to-add-bcc . "Add recipient of blind carbon copy to which text:")
    (text-to-delete-recipient . "Remove recipient from which text:")
    (text-to-move . "Which text do you want to move:")
    (text-tree-to-move . "Which text is the root of the tree you want to move:")

    (text-to-add-comment-to . "Add comment to which text:")
    (text-to-delete-comment-from . "Remove comment from which text:")
    (text-to-add-footnote-to . "Add footnote to which text:")
    (text-to-delete-footnote-from . "Remove footnote from which text:")
    (comment-to-move-q . "Which comment do you want to move:")
    (comment-move-from-q . "From which text do you want to move %#1n:")
    (comment-move-to-q . "To which text do you want to move %#1n:")
    (text-is-not-a-comment . "Text %#1n is not a comment to any text\n")
    (cant-move-footnotes . "Can't move foonotes automatically.\n")
    (moving-comment . "Moving %#1n from %#2n to %#3n...\n")

    (text-to-add-cross-reference-to . "Add cross reference to which text:")
    (text-has-no-recipients-r . "Text %#1n has no recipients\n")
    (cant-move-from-to-same . "Source and target conferences must be different.\n")

    (where-on-list-q . "Placement in your list? (0-%#1d) ")
    (accepting-invitation . "Accepting invitation to %#2M%#3?b%[ %#3s%]%[%]...")
    (member-in-conf . "Joining %#2M%#3?b%[ %#3s%]%[%]...")
    (member-in-conf-with-low-priority . "%#1M has lower priority than your session priority, and will not be shown.\n")
    (member-in-conf-with-unread-date . "with unread from %#2s %#3d, %#1d")
    (member-in-conf-with-unread . "unread %#1d unread")
    (add-member-in . "Adding %#1P as a member of %#2M...")
    (change-priority-for-q . "Change priority of conference: ")
    (cant-change-priority-of-passive . "Can't change priority of passive memberships.\n")
    (change-priority-for . "Changing priority of %#2M...")
    (unsubscribe-to . "Leaving %#1M...")
    (unsubscribe-to-2 . "Leaving %#1M%#2s...")
    (confirm-each-join . "Confirm joining individual conferences? ")
    (confirm-each-msg . "Confirm reception of group messages in individual möten? ")
    (receive-each-msg . "Do you want to receive group messages in all conferences? ")
    (no-confirm-each-sure . "Are you sure you want to join %#1d conferences automatically? ")
    (confirm-join . "Join %#1M? ")
    (getting-all-confs . "Getting a list of all conferences...")
    (getting-all-confs-progress . "Getting a list of all conferences (%#1d/%#2d)...")
    (getting-all-confs-done . "Getting a list of all conferences...done")
    (unsub-all-skipping-letterbox . "nix\nThis is your letterbox.\n")
    (unsub-all-skipping-supervised . "nix\nYou are the supervisor of the conference.\n")
    (unsub-secret-conf-q . "%#1M is secret. Leave anyway? ")
    (unsub-all-secret . "aLeave all secret conferences")
    (unsub-closed-conf-q . "%#1M är close. Leave anyway? ")
    (unsub-all-closed . "aLeave all closed conferences")
    (unsub-open-conf-q . "Leave %#1M? ")
    (unsub-all-open . "aLeave all open conferences")
    (abc-yes . "yYes")
    (abc-no . "nNo")
    (abc-yes-all . "aJa to all")
    (abc-no-all . "xNej to all")

    (move-tree-move . "mMove")
    (move-tree-sub  . "rRemove")
    (move-tree-add  . "aAdd")
    (move-tree-ign  . "nNothing")
    (move-tree-jump . "jJump")
    (move-tree-quit . "qCancel")

    (move-tree-add-rcpt-type . "Add as: ")
    (move-tree-rcpt . "rRecipient")
    (move-tree-cc . "cCarbon copy")
    (move-tree-bcc . "dBlind carbon copy")

    (move-tree-leave-cc-q . "Leave original recipient as carbon copy recipient? ")

    (exclude-from . "Removing %#1P from %#2M...")

    (unsubscribe-failed . "\nDidn't work. Perhaps %#1P isn't a member of %#2M?\n")
    (activate-mship . "Become active member of %#1M...")
    (activate-mship-done . "Activated passive membership of %#1M.\n")
    (passivate-done . "You are now a passive member of %#1M.
Leave the conference again to unsubscribe completely.\n")

    (You . "You")
    (could-not-create-conf . "Couldn't create the conference \"%#1s\".\n")
    (created-conf-no-name . "Conference number %[%#3@%#1:m %#2:M%] has been created.\n")
    (cant-read-textno . "You are not allowed to read text %#1:n.\n")

    (not-supervisor-for . "You are not the supervisor of %#1M.\n")
    (not-supervisor-for-server . "You do not have administrative rights on this LysKOM-server.\n")
    (go-to-conf . "Go to conference %#1M.\n")
    (cant-go-to-his-mailbox . "You are not allowed to go to %#1M's mailbox.\n")
    (not-member-of-conf . "You are not a member of %#1M.\n")
    (about-to-change-name-from . "%#1M\n")
    (change-name-done . "Done. New name: %[%#2@%#1:M%].\n")
    (change-name-nope . "Couldn't change name to %#1s. Error code %#3d. %#2s.\n")
    (change-supervisor-from-to . "Change supervisor of %#1M to %#2P...")
    (change-supervisor-nope . 
     "\nDidn't work. Perhaps you are not allowed to change the supervisor of %#1M?\n")
    
    (no-marked-texts . "You have not marked any texts.\n")
    (no-marked-texts-mark . 
     "You have not marked any texts with mark type \"%#1s\".\n")

    (weekdays . ["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday"
		 "Friday" "Saturday" "Sunday"])
    (weekdays-short . ["Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"])
    (time-is . "The time is %#1s%#2s (according to the server).")
    (time-is-week . "The time is %#1s%#2s, week %#3d (according to the server).")

    (xmaseve . "Christmas eve!\nYou didn't open any gifts early, did you?")
    (xmasday . "Christmas day.\nDid you get any nice gifts this year?")
    (newyearday . "\nMay %#1d be a prosperous and good year for you!")
    (newyeareve . "Happy New Year!")
    (newyearevelate . "Less than an hour left of %#1d...")
    (lysbday . "
On this day, in 1973, the Lysator Academic Computer Society was
formed, and it was a great day in the history of computing in Sweden.
Read all about it at http://www.lysator.liu.se/history/")
    (towelday . "\nTowel Day! Bring your towel.")

    (total-users . "    A total of %#1d users (%#2s).\n")
    (total-users-sans-date . "    A total of %#1d users\n")
    (total-visible-users . "    A total of %#1d visible user%#1?d%[%]%[s%] (%#2s).\n")
    (total-active-users . "    A total of %#1d active user%#1?d%[%]%[s%] (%#2s).\n")
    (total-visible-active-users
     . "    A total of %#1d visible%#1?d%[%]%[%] active user%#1?d%[%]%[s%] (%#2s).\n")
    (client-statistics . "    Distribution of clients:\n")
    (client-statistics-line . "    %4#2d %#1s%#3?d%[%]%[ [%#3d variants]%]")
    (who-to-add-q . "Add whom/what as a recipient of text %#1n? ")
    (who-to-add-copy-q . "Add which conference/user as carbon copy recipient of text %#1n? ")
    (who-to-add-bcc-q . "Add which conference/user as blind carbon copy recipient of text %#1n? ")
    (add-footnotes-too-q . "Try to add recipient to footnotes? ")
    (add-attachments-too-q . "Try to add recipient to attachments? ")

    (really-add-as-recpt-q . "Send future comments to the text to %#1M too? ") 
    (who-to-sub-q . "Remove whom/what as a recipient of %#1n? ")
    (sub-footnotes-too-q . "Try to remove recipient from footnotes? ")
    (sub-attachments-too-q . "Try to remove recipient from attachments? ")

    (who-to-move-from-q . "Move text %#1n from where? ")
    (who-to-move-to-q . "Move text %#1n to where? ")
    (edit-who-to-move-to-q . "Move text to where? ")
    (who-to-move-to-or-sub-q . "Move to where (empty to just remove recipient)? ")
    (move-footnotes-too-q . "Try to move footnotes? ")
    (move-attachments-too-q . "Try to move attachments? ")
    (move-text-not-recipient . "incorrect recipient.\n")

    (adding-name-as-recipient . "Adding %#1M as recipient of text %#2n...")
    (adding-name-as-copy . "%#1M will receive a carbon copy of text %#2n...")
    (adding-name-as-bcc . "%#1M will receive a blind carbon copy of text %#2n...")
    (remove-name-as-recipient . "Removing %#1M as recipient of text %#2n...")


    (unknown-recpt-text . "unknown mottagare")
    (cc-recpt-text . "carbon copy recipient")
    (bcc-recpt-text . "blind carbon copy recipient")
    (recpt-text . "recipient")
    (add-conf-as-rcpt-q . "Do you want to make %#1M a %#2s of text %#3n? ")
    (del-conf-as-rcpt-q . "Do you want to remove %#1M as a %#2s to text %#3n? ")
    (change-conf-as-rcpt-q . "Do you want to change %#2s %#1M to a %#3s of text %#4n? ")
    (move-conf-as-rcpt-q . "Do you want to move %#1n from %#2M to %#3M? ")

    (adding-cross-reference . "Adding cross reference...")

    (error-recipient-limit . "Text %#1n has too many recipients.\n")
    (error-already-recipient . "%#2M is already a recipient of text %#1n.\n")
    (error-permission-denied-add-recpt . "Only the author of %#1n or supervisor for %#2M can change recipient types.\n")
    (error-permission-denied-sub-recpt . "Only the author of %#1n or supervisor for %#2M can remove the recipient.\n")
    (error-access-denied-add-recpt . "You are not allowed to add texts to %#2M.\n")
    (error-not-recipient . "%#2M is not a recipient of text %#1n.\n")

    (moving-tree-what-action-q . "What do you want to do? ")
    (moving-cant-read . "Can't move text %#1n (unable to read text).\n")
    (moving-name . "Moving text %#3n from %#1M to %#2M...")
    (moving-already-moved . "Text %#1n does not have %#2M as a recipient.\nSkipping this text and its comments.\n")
    (text-to-add-q . "Add which text as a comment to %#1n? ")
    (text-to-remove-q . "Remove which text as a comment from %#1n? ")
    (text-to-add-footn-q . "Add which text as a footnote to %#1n? ")
    (text-to-remove-footn-q . "Remove which text as a footnote from %#1n? ")
    (add-comment-to . "Adding text %#1n as a comment to text %#2n...")
    (sub-comment-to . "Removing text %#1n as a comment to text %#2n...")
    (add-footnote-to . "Adding text %#1n as a footnote to text %#2n...")
    (sub-footnote-to . "Removing text %#1n as a footnote to text %#2n...")
    (comment-keep-recpt-p ."Should %#1s remain a recipient? ")
    (comment-all-relevant-p . "There are multiple recipients. Are they all relevant? ")
    (please-edit-recipients . "Modify the recipient list and send the text again.")
    (add-recipient-p . "Add recipient%#2?b%[ %#2M to reach%]%[%] %#1P? ")
    (checking-rcpt . "Checking recipients...")
    (checking-rcpt-done . "Checking recipients...done")
    (checking-comments . "Checking commented texts...")
    (checking-comments-done . "Checking commented texts...done")
    (please-check-commented-texts . "Review the commented text and its comments.")
    (have-unread-comment . "Send despite unread comments to text %#1n? ")
    (duplicate-recipients . "Unable to post. A recipient is duplicated (%#1M)")
    (no-recipients . "Unable to post text. No recipients were specified.")

    (matching-regexp . "Conferences/users matching `%#1s'\n")
    (matching-regexp-perss . "Users matching `%#1s'\n")
    (matching-regexp-confs . "Conferences matching `%#1s'\n")

    (who-is-active-all . "Showing all sessions.\n")
    (who-is-active-last-minutes . "Showing all sessions active the last %#1d minutes.\n")
    (showing-invisibles . "Showing invisible sessions.\n")
    (null-who-info . "No one (active) is logged on.\n")

    (no-other-lyskom-r . "There are no other active LysKOM sessions.\n")
    (no-lyskom-session . "There are no active LysKOM sessions.")
    (no-other-unread-lyskom-r . "There is no another LysKOM session with unread texts.\n")
    (no-unread-lyskom-r . "There is no active LysKOM session with unread texts.\n")
    (no-unread-lyskom . "There is no active LysKOM session with unread texts.")

    (who-is-on-in-what-conference . "Who in which conference: ")
    (who-is-present-in-what-conference . "Who present in which conference: ")
    (who-is-friend . "Only friends are shown.\n")
    (who-is-active-and-member . "Only members in %#1M are shown.\n")
    (who-is-active-and-present . "Only members present in %#1M are shown.\n")

    (personal-comment-to-anonymous . "It is not possible to write a personal comment to an anonymous text.\n")

    ;; Used for kom-is-person-member-of-conference:
    (pers-to-check-mship-for . "Whose membership do you want to check? ") ;-)
    (conf-to-check-mship-of . "...for which conference? ")
    (conf-is-empty . "The conference %#1M is empty.\n")
    (pers-is-member-of-conf . "Yes, %#1P is a member of the conference %#2M.\n")
    (pers-is-passive-member-of-conf . "Well, %#1P is only a passive member of the conference %#2M.\n")
    (pers-is-not-member-of-conf . "No, %#1P is not a member of the conference %#2M.\n")
    (pers-is-member-of-conf-2 . "Number of unread: %#2D (last access %#1s)\n")
    (pers-will-receive-async . "Reception of group messages %#1?b%[enabled%]%[disabled%]\n")
    (pers-mship-priority . "Membership priority: %#1d\n")
    (Unknown-number . "Unknown number")
    (text-to-check-will-read-for . "...for membership in recipients of text: ")
    (pers-to-check-will-read-for . "Person to check: ")
    (pers-is-member-of-rcpt . "Yes, %#1P is a member of at least one recipient to text %#2n.\n")
    (pers-is-passive-member-of-rcpt . "Well, %#1P is only a passive member of at least one recipient of %#2n.\n")
    (pers-is-not-member-of-rcpt . "No, %#1P is not a member of any recipient of %#2n.\n")

    ; From commands2.el:

    (your-memberships . "Your LysKOM conference memberships:\n")
    (memberships-header . "Last access	   Prio Unread	Conference\n")
    (memberships-line . "%16#1s  %#2d\t%#3d\t%#4M\n")
    
    (conf-for-status . "Get status of which conference? ")
    (no-such-conf . "The conference doesn't exist.\n")
    (status-record . "Status of conference %#1M (%#1m)%#2s\n\n")
    (change-type-prompt . "Change conference type for %#1M (%#1m) (%#2s)...")
    (Mailbox . "Mailbox")
    (Protected . "Protected")
    (no-comments . "No comments")
    (closed . "Closed")
    (allow-anon . "anonymomus ok")
    (allow-secret . "secret members")
                   
    (created-by . "Created by person %24#1p %#3s(%#2P)\n")
    (created-at . "Created:%34#1s\n")
    (members .    "Number of members: %19#1d\n")
    (conf-allows-secret-members . "Secret members:                          %#1s\n")
    (conf-allows-anon-texts .     "Anonymous texts:                         %#1s\n")
    (anon-texts-permitted . "Anonymous texts are permitted")
    (anon-texts-not-permitted . "Anonymous texts are not permitted")
    (secret-members-permitted . "Secret members are permitted")
    (secret-members-not-permitted . "Secret members are not permitted")
    (garb-nice .      "Expiration time:          %16#1d day%#1?d%[%]%[s%]\n")
    (keep-commented . "Min. expiration time for commented texts: %#1d day%#1?d%[%]%[s%]\n")
    (lowest-local-no . "Lowest local number: %21#1d\n")
    (highest-local-no . "Highest local number: %20#1d\n")
    (last-text-time . "Time of last text:    %20#1s (according to your cache)\n")
    (no-of-motd . "Notice in text:    %13#1n\n")
    (pers-has-privileges .  "Privileges:  %#1s\n")
    (pers-has-privileges-2 .  "%28#1s")
    (superconf-is-no-name . "Superconference: %25#1m %#3s(%#2M)\n")
    (permitted-submitters-no-name . "Allowed authors: %25#1m %#3s(%#2M)\n")
    (supervisor-is-no-name . "Supervisor: %30#1p %#3s(%#2P)\n")
    (presentation-no . "Presentation:    %25#1n\n")
    (conf-has-motd . "\n%#1M has a notice on his/her mailbox:\n")
    (conf-mship-priority . "Prioritet:       %25#1n%#2?b%[ %#2s%]%[%]\n")
    (status-conf-generic . "%-40#1s %#2s\n")
    (status-aux-item . "Unknown property:              %11#1s%#3s (created by %#2M)\n")
    (conf-mx-allow-envelope-sender-regexp . "Allowed senders for mail import:         %#1s %#2s\n")
    (conf-mx-list-name . "Imported mailing list:                   %#1s %#2s\n")
    (conf-mx-refuse-import . "Refuse import of:                        %#1s %#2s\n")
    (recommended-conf-aux . "Recommended conference:                  %#1M <%#1m> %#2s\n")
    (bad-recommended-conf-aux . "Recommended conference:                  \"%<20...#1s\" %[%#2@[invalid]%] %#3s\n")
    (status-read-faq-aux-item . "Read FAQ:                  %15#2n for %#1?z%[%#1M <%#1m>%]%[the server%] %#3s\n")
    (bad-status-read-faq-aux-item . "Read FAQ:                                \"%<20...#1s\" %[%#2@[invalid]%] %#3s\n")
    (status-rejected-recommendation-aux-item . "Rejected recommendation for:             %#1M %#2s\n")
    (bad-status-rejected-recommendation-aux-item . "Rejected recommendation for:             \"%<20...#1s\" %[%#2@[invalid]%] %#3s\n")
    (status-send-comments-to . "Redirect comments to:                    %#1M <%#1m> %#2s\n")
    (bad-status-send-comments-to . "Redirect comments to:                    \"%<20...#1s\" %[%#2@[invalid]%] %#3s\n")

    (Everybody . "Everyone")
    (show-members-list-also-q . "List members? ")
    (show-membership-info-q . "Show membership info number of unreads? ")
    (conf-has-these-members . "\n%#1M has the following members:\n")
    (conf-has-no-members . "\n%#1M has no members.\n")
    (member-list-header . "\nLast entered        Unread  Name\n")
    (secret-membership . "--- Secret line ---\n")
    (conf-membership-line . "%#4@%#1s%#2M %#3s\n")
    (conf-membership-line-2 . "                            Added %#1s by %#2P\n")
    (pers-for-status . "Get status of which user? ")
    (text-to-see-author-status-of . "Get status of the author of which text? ")
    (no-such-pers . "The user doesn't exist.\n")
    (pers-status-record . "Status of user %#1P (%#1p)\n")
    (created-time .  "Created:%34#1s\n\n")
    (created-confs . "Conferences created:%22#1d\n")
    (created-persons . "Users created:%28#1d\n")
    (created-texts . "Texts created:%28#1d\n")
    (created-lines . "Lines created:%28#1d\n")
    (created-chars . "Characters created:%23#1d\n")
    (no-of-sessions . "Session count:%28#1d\n")
    (present-time-d-h-m-s . "Total presence:%16#1d d %02#2d:%02#3d:%02#4d\n")
    (last-log-in . "Last login or logout:%21#1s\n")
    (user-name . "User: %36#1s\n")

    (read-texts . "Texts read:%31#1d\n")
    (marked-texts . "Texts marked:%29#1d\n")
    (time-for-last-letter . "Time of last letter:%22#1s (according to your cache)\n")
    (superconf . "Superconference: %25#1m %#3s(%#2M)\n")
    (supervisor . "Supervisor: %30#1p %#3s(%#2P)\n")
    (member-of-confs . "Member of (conferences):%18#1d\n")
    (presentation . "Presentation:    %25#1n\n")
    (show-membership-list-also-q . "List memberships? ")
    (not-allowed-see-confs . "%#1P is not a member of any conferences.\n")
    (is-member-of . "\n%#1P is a member of the following conferences:\n")
    (membership-list-header . "\nLast access         Unread   Conference\n")
    (pers-membership-line . "%#5@%#1s%#2s%#3M %#4s\n")
    (pers-membership-line-2 . "                             Added %#1s by %#2P\n")
    (is-supervisor-mark . "O ")
    (moronify-saving . "Saving change...")
    (moronify-saving-done . "Saving change...done")
    (moronify-saving-error . "Couldn't save change!")
    (befriend-saving . "Saving change...")
    (befriend-saving-done . "Saving change...done")
    (befriend-saving-error . "Couldn't save change!")
    (moronify-whom . "Add whom to morons list? ")
    (befriend-whom . "Add whom to friends list? ")
    (who-to-send-message-to . "Send message to whom? (%#1s) ")
    (send-empty-message-p . "The message is empty. Send it anyway? ")
    (his-total-unread . "\n%#1M has %#2d unread texts (plus %#3d in passive memberships).\n")
    (message-prompt . "Message%#1?z%[%]%[ (till alla inloggade)%]: ")
    (message-sent-to-user . "\
%[%#3$================================================================
%]%[%#4$Your message for %#2M (%#5s):

%#1t
%]%[%#3$----------------------------------------------------------------
%]")
    (message-sent-to-all . "\
%[%#3$================================================================
%]%[%#4$Your public message (%#5s):

%#1t
%]%[%#3$----------------------------------------------------------------
%]")
    (message-sent-to-user-long . "\
%[%#3$\
===============================================================================
%]%[%#4$Your message for %#2M (%#5s):

%#1t
%]%[%#3$\
-------------------------------------------------------------------------------
%]")
    (message-sent-to-all-long . "\
%[%#3$\
===============================================================================
%]%[%#4$Your public message (%#5s):

%#1t
%]%[%#3$\
-------------------------------------------------------------------------------
%]")
    (message-use-alarm-instead . "Use %#1s to send alarm messages.\n")
    (message-all-info . "\
%#1@Send alarm message to all users.

%[%#3FConsider using \"%#2C\" to send your message to a specific
conference or user.%]
")
    (message-recipient-info . "Send message to %#1M\n")
    (message-nope .
     "Unable to send message. %#3s\
The message you were sending to %#1M was:
%#2t\n")
    (only-last . "Last texts in %#1s: ")
    (initial-unread . "Initial number of unread (empty for all texts): ")
    (only-error . "Something went wrong. Sorry.\n")
    
    (lp--only-last . "Number of text to read: ")

    (session-list-unreads-in-confs . 
			      "You have %#2?z%[%#2d unread letter%#2?d%[%]%[s%], in total %]%[%]%#3d unread text%#3?d%[%]%[s%] in %#4d conference%#4?d%[%]%[s%] in %#1s.\n")
    (session-list-no-unread-in . "You have no unread texts in %#1s.\n")

    (you-have-unreads . "You have %#1d unread text%#1?d%[%]%[s%] in %#2M\n")
    (you-have-unreads-special . "You have %#1d uncommented text%#1?d%[%]%[s%] in %#2M\n")
    (you-have-no-unreads . "You have read everything in %#1M\n")
    (you-have-read-everything . "No news (is bad news).\n")
    (no-unreads-shown . "Found no conferences meeting that criterion.\n")
    (total-unreads .
     "You have %#1d unread text%#1?d%[%]%[s%] texts in %#2d conference%#2?d%[%]%[s%].\n")
    (shown-unreads . "Showed %#1d unread text%#1?d%[%]%[s%] in %#2d conference%#2?d%[%]%[s%].\n")
    (list-unread-with-n-unread . "Listing conferences with at least %#1d unread.\n")
    (list-unread-with-at-most-n-unread . "Listing conferences with at most %#1d unread.\n")
    (waiting-for-anything .
     "You are waiting for a text in any conference.\n")
    (waiting-higher-than . 
     "You are waiting for a text in any conference with a priority higher than %#1d.\n")
    
    (have-to-be-in-conf-with-unread . "You must go to a non-empty conference first.\n")
    (Texts . "Text")
    (Written . "Written")
    (Lines . "Lines")
    (Author . "Author")
    (Subject . "Subject")
    (Comments . "Co")
    (Num-marks . "Mk")
    (mark-type . "Type")
    (mark-no . "Mrk")

    (could-not-read . "You couldn't read the text (%#1n).\n")
    (multiple-choice . "There are several alternatives.")

    (does-not-exist . "Unknown command.") ; Only people fixing bugs or receiving bug reports should change these:
    (summary-line . "%=-8#1n%#2s%4#3d  %[%#4@%#5:P%]  %[%#6@%#7r%]\n")
    (diff-what-text-old . "Old text to compare: ")
    (diff-what-text-new . "New text to compare: ")

    ; Only people fixing bugs or receiving bug reports should change these:
    (buggreport-compilestart . "Creating bug report...")
    (buggreport-compileend . "Creating bug report...done")
    (buggreport-description . "This is what I was doing:
\(Fill in your comments below\)\n================\n\n
================
In the information below are the 100 most recently pressed keys from
your emacs. If you recently logged on, you password may be contained in
this list. If that is the case, change the characters corresponding to
your password to asterisks.

When you have finished writing this, send your bug report to the LysKOM
developers. You can do this either by email to bug-lyskom@lysator.liu.se or
by mailing a hardcopy of your bug report to:
Lysator, c/0 ISY, Linkoping Univerity, S-581 83 Linkoping, SWEDEN.
Mark the envelope with \"LysKOM bug report\"\n\n")
    (buggreport-internals . "LysKOM's internal information:\n\n")
    (buggreport-version . "lyskom-version:")
    (buggreport-emacs-version . "emacs-version:")
    (buggreport-system-id . "system-id:")
    (buggreport-ctl-arrow-doc . "ctrl-doc:")
    (buggreport-unparsed . "\nlyskom-unparsed-buffer:")
    (buggreport-command-keys . "Recently pressed keys:")
    (buggreport-backtrace . "\n*Backtrace*:\n%#1s\n")
    (buggreport-communications . "\nlyskom-debug-communications-to-buffer-buffer:")
    (buggreport-all-kom-variables . "\n\nOther variables:\n***** *********")
    (buggreport-instead-of-byte-comp . "byte-code(\"byte-string\"")
    (buggreport-subject . "Bugreport elisp-client version %#1s")


    (not-logged-in . "You are not logged on. ")
    (name-is-not-in-conf . "%#1s is not in any conference.\n")
    (name-is-in-conf . "%#1s is in\n%#2s\n")
    (connected-during . "Connect time: %#1d seconds.\n")

    (conf-to-set-permitted-submitters-q . "For which conference do you want to set the allowed authors? ")
    (conf-to-set-super-conf-q . "Set superconference of which conference? ")
    (new-super-conf-q . "Which conference do you want as superconference? ")
    (new-permitted-submitters-q . "Allow members of which conference as authors in %#1s? (all) ")
    (super-conf-for-is . "Changing superconference of %#1M to %#2M...")
    (permitted-submitters-removed-for-conf . "Allowing all authors to conference %#1M...")
    (submitters-conf-for-is . "Changing authors admitted to conference %#1M to the members of %#2M...") 
   
    (conf-to-set-garb-nice-q . "Set expiration time for which conference? ")
    (new-keep-commented-q . "How many days shall new comments protect texts from removal? ")
    (new-garb-nice-q . "After how many days shall texts be removed? ")
    (garb-nice-for-is . "Changing expiration for %#1M to %#2d day%#2?d%[%]%[s%]...")
    (keep-commented-for-is . "Changing minimum expiration for commented texts in %#1M to %#2d day%#2?d%[%]%[s%]...")

    (really-shutdown . "Are you sure you want to shut down the server? ")
    (closing-server . "Shutting down the server...")
    (really-sync . "Are you sure you want to save the database? ")
    (syncing-server . "Saving the database...")
    (administrator . "administrator")
    (no-longer-administrator . "a regular user again")
    (you-are-now . "Ok, you are now running as %#1s.\n")
    (setting-motd . "Changing login message to text %#1n.\n")
    (set-motd-success . "You have set a new login message.\n")
    (set-motd-failed . "Didn't work. Perhaps you were not an administrator.\n")
    (removing-motd . "Removing the login message.\n")
    (removed-motd . "You have removed the login message.\n")
    (who-to-throw-out . "Whose session do you want to kill? ")
    (throwing-out . "Killing session %#1d... ")
    (postpone-prompt . "How much do you want to read now? ")
    (set-session-priority . "Set reading level: ")

    ; From review.el:
    (no-unread-done . "You need to unread something before you can unread more.\n")
    (no-review-done . "You need to review something before you can review more.\n")
    (not-reviewing . "You are currently not reviewing anything.\n")
    (unread-how-many . "Unread how many?")
    (unread-how-many-roots . "Unread how many root texts?")
    (unread-how-many-more . "Unread how many more?")
    (review-how-many . "Review how many?")
    (review-how-many-roots . "Review how many root texts?")
    (review-how-many-more . "Review how many more?")
    (latest-n . "last %#1d")
    (first-n . "first %#1d")

    (review-by-whom . "Review %#1s %#2?b%[%#2s %]%[%]by whom: ")
    (review-to-conf . "Review %#1s %#2?b%[%#2s %]%[%]to conference: ")
    (unread-by-whom . "Unread %#1s %#2?b%[%#2s %]%[%]by whom: ")
    (unread-to-conf . "Unread %#1s %#2?b%[%#2s %]%[%]to conference: ")
    (all-confs . "all conferences")

    (unread-info-by-to . "Unread %#1s %#4?b%[%#4s %]%[%]by %#2P to %#3M.\n")
    (unread-more-info-by-to . "Unread %#1s %#4?b%[%#4s %]%[%]by %#2P to %#3M.\n")
    (unread-rest . "the rest")
    (unread-more . "%#1d more")

    (review-info-by-to . "Review %#1s %#4?b%[%#4s %]%[%]by %#2P to %#3M forwards.\n")
    (review-more-info-by-to . "Review %#1s %#4?b%[%#4s %]%[%]by %#2P to %#3M forwards.\n")
    (review-filter-roots . "roots")
    (review-rest . "the rest")
    (review-more . "%#1d more")
    (you-review . "You are now reviewing %#1s.\n")
    (read-text-first . "You must read a text first.\n")
    (cannot-read-last-text . "You cannot review the last read text.\n")
    (review-n-texts . "Review %#1d texts.\n")
    (review-marked . "Review %#1d marked texts.\n")
    (review-text-no . "Review text %#1n\n")
    (review-many-comments . "Review %#2?d%[one%]%[%#2d%] comment%#2?d%[%]%[s%] to text %#1n.\n")
    (view-many-comments . "Read %#2?d%[one%]%[%#2d%] comment%#2?d%[%]%[s%] to %#1n.\n")
    (view-texts-in-conf . "Grand total of %#1d texts to read in %#2M.\n")

    (not-reading-anywhere . "You are not reading in any conference.\n")
    (read-normally-read . "How many texts to you want to read again? ")

    (review-conf-gone . "The conference does not exist.\n")
    (review-pers-gone . "The user does not exist.\n")
    (review-cant-read-conf . "You can't review texts to a closed conference you are not a member of.\n")
    (review-cant-read-letterbox . "You can't review texts to somebody else's mailbox.\n")
    (review-cant-read-empty . "The conference is empty.\n")
    (cant-review-everything . "You cannot review every text in LysKOM.\n")
    (cant-unread-everything . "You cannot unread every text in LysKOM.\n")

    (more-than-one-root . "Text %#1n has more than one root.\n")
    (more-than-one-root-review . "\
Text %#1n has more than one root but only one of the trees will be shown.\n")

    ; From edit-text.el:
    (press-C-c-C-c . "Enter C-c C-c to post the text.")
    (recipient . "Recipient")
    (carbon-copy . "Carbon copy")
    (blank-carbon-copy . "Blind Carbon copy")
    (recipient-prefix . "\\([Rr]\\)")
    (carbon-copy-prefix . "[Cc]\\([Aa]\\|[Cc]\\)")
    (blank-carbon-copy-prefix . "[Bb]\\([Ll]\\|[Cc][Cc]\\)")
    (add-recipient . "Add a recipient")
    (add-recipient-or-xref . "Add...")

    (secret-aux-flag . "secret")
    (anonymous-aux-flag . "anonymous")
    (inherit-aux-flag  . "inherited")
    (aux-item-prefix . "[*]")
    (aux-item-prefix-regexp . "\\[\\*\\]\\s-*")
    (comment-item-prefix . "#\\s-*")
    (text-no-comment . "%#1d %#2s /%#3d line%#3?d%[%]%[s%]/ %#4P %#5?b%[ [anonymous]%]%[%]\n")
    (cant-fcc-text-file-error . "Unable to save text %#1n to \"%#2s\" (%#3s: %#4s).\n")
    (cant-fcc-text . "Unable to save text %#1n to \"%#2s\" (%#3s).\n")

    (header-subject . "Subject: ")
    (header-separator . "\\<lyskom-edit-mode-map>\
--- Write below. \
Post: \\[kom-edit-send], \
Kill: \\[kom-edit-quit], \
Help: \\[describe-mode] ---")
    (text-mass . "%#4s%#1s%[%#6@\n%]%[%#5@%#2s%]%[%#7@\n%]%#3s")
    (comment-to-by . "%#1s to text %#2n%#3s.\n")
    (already-sent . "You have already posted this text. Post it anyway? ")
    (subject . "Subject: ")
    (subject-prefix . "[Ss]")
    (enter-subject-idi . "Enter a subject.")
    (which-text-include . "Include which text? ")
    (added-recipient . "Recipient: ")
    (added-carbon-copy . "Carbon copy to conference: ")
    (added-blank-carbon-copy . "Blind carbon copy to conference: ")
    (text-to-comment-q . "Which text do you want to comment? ")
    (conf-has-motd-no . "The conference has a notice. (%#1d)\n\n%#2s")
    (still-want-to-add . "Do you still want to add the conference as a recipient? ")
    (could-not-create-text . "\nCouldn't create the text. Error: %#2s.\n")
    (no-get-text . "You were not allowed to retrieve the text.")
    (unknown-header . "Unknown header")
    (transform-error . "Send unformatted (%#1s)? ")
    (cant-find-info-node . "Can't find the info buffer")

    (link-type . "What to you want to link to? ")
    (which-text-to-link . "Add link to text: ")
    (which-text-to-link-err . "Text not found. Add link to text: ")
    (which-pers-to-link . "Add link to user: ")
    (which-conf-to-link . "Add link to conference: ")

    ; From view-text.el:
    (view-text-first-line . "%#7$%#2@%#1n %#3s /%#4d line%#4?d%[%]%[s%]/ %#5P%#6?b%[%#6s%]%[%]\n")

    (marked-by-you . "Marked by you (type: %#1s).\n")
    (marked-by-you-and-others . "Marked by you (type: %#2s) and %#1?d%[someone else%]%[%#1d others%].\n")
    (marked-by-several . "Marked by %#1d user%#1?d%[%]%[s%].\n")

    (timeformat-day-yyyy-mm-dd-hh-mm-ss . "%#7s %4#1d-%02#2d-%02#3d %02#4d:%02#5d:%02#6d")
    (timeformat-yyyy-mm-dd-hh-mm-ss . "%4#1d-%02#2d-%02#3d %02#4d:%02#5d:%02#6d")
    (timeformat-yyyy-mm-dd-hh-mm . "%4#1d-%02#2d-%02#3d %02#4d:%02#5d")
    (timeformat-yyyy-mm-dd . "%4#1d-%02#2d-%02#3d")
    (timeformat-hh-mm-ss . "%02#4d:%02#5d:%02#6d")
    (timeformat-hh-mm . "%02#4d:%02#5d")

    (format-time-date-and-time . "%#1s %#2s")
    (format-time-just-date . "%#1s")
    (format-time-just-time . "%#2s")
    (today . "today")
    (yesterday . "yesterday")

    (no-such-text-no . "The text doesn't exist. (%#1:n)\n")
    (text-created-at . "Created: %#1s\n")
    (text-imported-at . "Imported: %#1s\n")
    (text-imported-at-by . "Imported: %#1s by %#2P\n")

    (head-Subject . "Subject: ")
    (Recipient . "Recipient")
    (Extra-recipient . "CC")
    (Hidden-recipient . "BCC")
    (mx-Recipient . "External recipient")
    (mx-Extra-recipient . "External CC")
    (mx-Extern-reply-to . "External replies to")
    (Strange-recipient . "Also to")
    (send-at . "   Posted:     %#1s\n")
    (sent-by . "   Posted by %#1P\n")
    (received-at . "    Received: %#1s\n")

    (comment-to-text . "Comment to text %#1n")
    (footnote-to-text . "Footnote to text %#1n")
    (comment-in-text . "Comment in text %#1n")
    (footnote-in-text . "Footnote in text %#1n")

    (attachment-to-text . "Attachment %#3sto text %#1n")
    (attachment-in-text . "Attachment %#3sin text %#1n")
    
    (envelope-sender . "Sent by: %#1s\n")
    (attachment-filename . "Attachment file name: \"%#1s\"\n")
    (message-id . "Message-ID: %#1s\n")

    (comment-to-text-by . "Comment to text %#1n by %#2P")
    (footnote-to-text-by . "Footnote to text %#1n by %#2P")
    (comment-in-text-by . "Footnote in text %#1n by %#2P")
    (footnote-in-text-by . "Footnote in text %#1n by %#2P")

    (written-by . " by %#1P\n")

    ; From async.el:

    (name-has-changed-to-name . "%#1:P has changed name to %#2:P")
    (name-has-changed-to-name-r . "%#4@%[%#3@%#1:P%] has changed name to %[%#3@%#2:P%]\n")
    (you-changed-name-to . "You have now changed your name to %[%#2@%#1:P%].\n")
    (database-sync . "Synching database.")

    (lyskom-is-full . "\
===========================================================
Message from the LysKOM system: Somebody tried to connect,
but failed since all connections available to LysKOM are in
use. Please leave and return later if you are just waiting
for a text.
===========================================================\n")
    (lyskom-is-full-long . "\
===============================================================================
Message from the LysKOM system: Somebody tried to connect,
but failed since all connections available to LysKOM are in
use. Please leave and return later if you are just waiting
for a text.
===============================================================================
")
    (has-entered . "%#1:P has entered %#2s.")
    (has-entered-r . "%#2@%#1P has entered %#3s%#6?b%[ from %#6s%]%[%]%#4?b%[ (%#4s %#5s)%]%[%].\n")
    (has-left . "%#1:P has left %#2s.")
    (has-left-r . "%#2@%#1P has left %#3s (%#4s %#5s).\n")
    (Unknown . "Unknown")
    (unknown . "unknown")
    (Unknown2 . "Unknown")

    (no-longer-member . "You are no longer a member of %#1M.\n")
    (no-longer-member-n . "You are no longer a member of %#1m (conference is gone).\n")
    (have-become-member . "You have become a member of %#1M.\n")
    (have-become-invited-member . "You have been invited to %#1M.
Go to the conference to accept or decline the invitation. You can also use
the command 'Join conference' to accept the invitation.\n")
    (have-become-passive-member . "You have become a passive member of %#1M.\n")


    (message-broadcast . "\
%[%#4$\
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#5$\
Alarm from %#1P (%#3s):

%#2t
%]%[%#4$\
----------------------------------------------------------------
%]")
    (message-broadcast-long . "\
%[%#4$\
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#5$Alarm from %#1P (%#3s):

%#2t
%]%[%#4$\
-------------------------------------------------------------------------------
%]")
    (message-from . "\
%[%#4$\
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#5$Personal message from %#1P (%#3s):

%#2t
%]%[%#4$\
----------------------------------------------------------------
%]")
    (message-from-long . "\
%[%#4$\
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#5$Personal message from %#1P (%#3s):

%#2t
%]%[%#4$\
-------------------------------------------------------------------------------
%]")
    (message-from-to ."\
%[%#5$\
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#6$Group message to %#3M\nfrom %#2P (%#4s):

%#1t
%]%[%#5$\
----------------------------------------------------------------
%]")
    (message-from-to-long ."\
%[%#5$\
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%]%[%#6$Group message to %#3M\nfrom %#2P (%#4s):

%#1t
%]%[%#5$\
-------------------------------------------------------------------------------
%]")


    (text-is-created . "Text %#1n has been created!")

    ; Used in mode-line-process
    (mode-line-waiting . ": waiting")
    (mode-line-working . ": working")
    (mode-line-saving . ": saving")
    (mode-line-down . ": down")

    ; From completing-read.el:

    (person-or-conf-no-regexp . "\\`[ \t]*[mpMP]\\w*[ \t]+\\([0-9]+\\)\\'")
    (session-no-regexp . "\\`[ \t]*[sS]\\w*[ \t]+\\([0-9]+\\)\\'")
    (conf-prompt . "Which conference/person? ")

    ; From flags.el:
    (saving-settings . "Saving options...")
    (saving-settings-done . "Saving options...done")
    (save-options-failed-internal . "\
Failed to save options because of an internal error.

Global options:       %#1?b%[Encoding failed%]%[OK%]
Elisp client options: %#2?b%[Encoding failed%]%[OK%]
Other client options: %#3?b%[Encoding failed%]%[OK%]

Rapportera gärna detta fel till elispklientens utvecklare.
")
    (hang-on . "Wait a moment...\n")
    (no-changes . "Nothing was saved since no options had been changed.\n")
    (could-not-save-options . "Couldn't save options.\n")
    (could-not-create-area . "Couldn't create the text.\n")
    (could-not-set-user-area . "Couldn't alter the user area. The server says error: %#1d\n")
    (you-dont-exist . "You don't exist. Go away.\n")
    (error-in-options . "There was an error in one of your variables (%#1s)
It was set to \"%#2s\" in the user area. It is set to nil instead.
Send a bug report.\n")
    (error-in-options-short . "There was an error in your saved settings. Some settings have not been read.")
    (reading-settings-from-server . "Saved settings have changed on the server. Re-reading saved settings.\n")

    ; From elib-string.el:
    ; No entries.

    ; From lyskom-rest.el:

    (mode-line-unread . " Unread ")
    (mode-line-letters . "letters ")
    (frame-title-unread . "Unread")
    (frame-title-letters . " letters")
    (sessions-with-unreads . "Sessions with unread texts")
    (unread-letters . "unread letters")

    (bad-text-no-prefix . "Unable to translate prefix `%s' to a text number")
    (prefix-arg-try-again . "Specify another text or press control-g to abort.\n")
    (error-code . "%#2?+%[Error code %#2d/%#3S: %]%[%]%#1s.\n")
    (error-in-kom-do-when-done . "The variable kom-do-when-done has an erroneous value.
You should set it to a better value.\n")
    (extended-command . "LysKOM: ")
    (wait-for-server . "LysKOM is waiting for a response from the server. Wait for the prompt.\n")
    (review-text-q . "Review text: ")

    (completely-read-conf . "You have seen all the texts in this conference.\n")
    (not-in-any-conf . "You are not in a conference now.\n")

    (all-conf-unread-r . "You have nothing unread.\n")
    (all-conf-unread-s . "You have nothing unread. ")
    (enter-conf-unread . "%#1d unread text%#1?d%[%]%[s%]")
    (enter-conf-unread-faq . "%#1d unread FAQ%#1?d%[%]%[s%]")

    (save-one-on-file-q . "Save copy of text %#1n in file: ")
    (save-many-on-file-q . "Save copies of %#1d texts in file: ")
    (saving-one-on-file . "Saving copy of text %#1n in %#2s.\n")
    (saving-many-on-file . "Saving %#1d texts in %#2s.\n")
    (save-text-to-file-q . "Save body of text %#1n in which file: ")
    (save-text-confirm . "File %#1s exists. Overwrite? ")
    (saving-text . "Saving text %#1n as %#2s...")
    (what-save-no . "Which text do you want to save? ")
    (wait-for-prompt . "Wait for the prompt.")

    (conference-no . "<conference %#1d>")
    (person-no . "<person %#1d>")
    (prompt-several-messages . "(%d messages)")
    (prompt-single-message . "(%d message)")

    (text-buffer-missing . "The text buffer no longer exists.\n")
    (re-edit-text-prompt . "Edit the text that couldn't be created")
    (go-to-pri-conf-prompt . "Go to next prioritized conference")
    (go-to-pri-mailbox-prompt . "Gå to your mailbox (prioritized)")
    (read-pri-text-conf . "Read next prioritized text")
    (read-pri-letter-prompt . "Read next letter (prioritized)")
    (review-next-text-prompt . "Review next text")
    (review-next-comment-prompt . "Review next comment")
    (review-next-marked-prompt . "Review next marked text")
    (review-next-faq-prompt . "Review next FAQ")
    (read-next-letter-prompt . "Read next letter")
    (read-next-footnote-prompt . "Read next footnote")
    (read-next-comment-prompt . "Read next comment")
    (read-next-text-prompt . "Read next text")
    (read-next-attachment-prompt . "Read next attachment")
    (go-to-conf-of-marked-prompt . "Resume reviewing marked")
    (go-to-conf-of-review-tree-prompt . "Resume reviewing comments")
    (go-to-conf-of-review-prompt . "Resume reviewing texts")
    (go-to-conf-of-review-faq-prompt . "Resume reviewing FAQs")
    (go-to-next-conf-prompt . "Go to next conference")
    (go-to-your-mailbox-prompt . "Go to your mailbox")
    (next-pri-session-prompt . "Go to prioritized LysKOM \"%#1s\"")
    (next-unread-session-prompt . "Go to LysKOM \"%#1s\"")
    (no-such-kom-session . "No such session with unread texts.\n")
    (the-command . "Command: %#1C")
    (error-in-login-hook . "There was an error in your kom-login-hook: %#1s\n")

    (give-a-number . "Enter a number: ")

    (yes-regexp . "\\`[yY][eE][sS]\\'")
    (no-regexp . "\\`[nN][oO]\\'")
    (yes-string . "Yes")
    (no-string . "No")
    (yes-or-no-nag . "Answer yes or no.")
    (yes-or-no . "(yes or no) ")

    (y-or-n-instring . "yYnN ")
    (j-or-n-nag . "Answer y or n. ")
    (j-or-n . "(y or n) ")
    (y-instring . "yY ")

    (a-or-b-or-c-nag . "Pleas press a valid key. ")

    (person-does-not-exist . "User %#1d (does not exist).")
    (conference-does-not-exist . "Conference %#1d (does not exist).")
    (conf-no-does-not-exist-r . "Conference %#1d does not exist.")
    (person-is-anonymous . "Anonymous user")

    (process-signal . "Signal from the process.")
    (dead-session . "No active LysKOM session.")
    (resurrect-session . "The LysKOM session is not active. Do you want to reconnect? ")
    (not-lyskom-buffer . "This is not an active LysKOM session.")
    (closed-connection . "
**************************************************
%#2s
LysKOM session killed abnormally
Error message: %#1s**************************************************")
    (closed-connection-other-buf . "\
**************************************************
The LysKOM session %#1s was killed abnormally.
**************************************************
")
    (error-not-found . "Error code %#1d. No explanation available.")

    ; Useful in more places than one:
    (illegal-command . "Illegal command.\n")
    (no-such-text . "The text doesn't exist.\n")
    (no-such-text-m . "The text doesn't exist.")
    (nobody . "nobody")
    (everybody . "everyone")
    (everything . "everything")
    (anybody . "anyone")
    (forward . "forward")
    (backward . "backward")
    (wait . "Wait a moment...\n")
    (comment . "Comment")
    (comment-prefix . "\\([Cc][Oo]\\)")
    (footnote . "Footnote")
    (footnote-prefix . "\\([Ff]\\)")
    (by . " by %#1P")
    (text-created .  "Text %#1n has been created.\n")
    (text-created-anonymous .  "\
Text %#1n has been created (anonymously). To make it more difficult
for others to figure out that you wrote the text, you should wait a while
before reading it.\n")

    (resolve-session . "Which session: ")

    (starting-program . "Starting %#1s...")
    (super-jump . "Filtering subject \"%#1r\" in conference \"%#2M\"\n")
    (no-recipient . "There are no recipients for this text.\n")
    (filtered . "[Filtered]")
    (filter-error-specification . "Error in the filter specification")
    (filter-error-bad-not . "Error in the filter specification after 'not'")
    (filter-error-unknown-key . "The filter key '%S' is unknown.")
    (filter-error-key-arg . "Bad filter data (%S %S)")
    (filter-tree . "Skipping text %#1n \"%#2r\" by %#3P and all its comments.\n")
    (filter-text . "Skipping text %#1n \"%#2r\" by %#3P.\n")
    (filter-permanent . "Permanent? ")
    (filter-action . "Filter how? ")
    (filter-in-conf . "In which conference? (all) ")
    (filter-subject . "Filter which subject? ")
    (filter-recipient . "Filter which recipient? ")
    (filter-which-text . "Filter texts containing: ")
    (filter-author . "Filter which author? ")
    (permanent . "(permanent)")
    (temporary . "(temporary)")
    (filter-edit-buffer-name . "*LysKOM Filter Edit*")
    (filter-edit-empty-list . "Empty list")
    (filter-edit-start-of-list . "Beginning of list")
    (filter-edit-end-of-list . "End of list")
    (filter-edit-filter-how . "Filter how? ")
    (filter-edit-filter-what . "What do you want to filter? ")
    (filter-edit-bad-argument . "Bad input: %s")
    (filter-edit-outside-entry . "Can't do that outside a filter")
    (filter-edit-outside-list . "Can't do that outside the list")
    (filter-edit-end-of-pattern . "End of filter")
    (filter-edit-save-p . "Save changes? ")
    (filter-edit-remove-empty . "Empty filters cause all texts to be filtered. Do you want to remove these? ")
    (filter-edit-restart-p . "You have made changes. Really revert? ")
    (filter-edit-help . "p Up, n Down, i New line, M-i New filter, d Delete line, M-d Delete filter")
    (filter-edit-header . "Edit filters on \"%s\"\n")
    (filter-edit-footer . "
 Up line:     p  New line:       i    Delete line:       d
 Up filter:   P  New filter:     M-i  Delete filter:     M-d
 Down line:   n  Save changes:   s    Change action:     a
 Down filter: N  Revert changes: g    Change permanence: t
 Quit:        q                       More help:         C-h m
")
    (filter-edit-saving . "Saving changes...")
    (filter-edit-saving-done . "Saving changes...done")
    (filter-edit-saving-error . "Couldn't save changes!")
    (filter-edit-insert-pred . "%#1s (=,!=): ")
    (filter-edit-insert-arg . "%#1s %#2s (what): ")
    (no-filters . "No filters are defined.\n")
    (view-filters-header . "\nActive filters:\n\n")
    (view-filters-footer . "")

    (ansaphone-new-message . "New Ansaphone message: ")
    (ansaphone-message . "Ansaphone message:
----------------------------------------------------------------------
%#1t
----------------------------------------------------------------------
")
    (ansaphone-message-long . "Ansaphone message:
-------------------------------------------------------------------------------
%#1t
-------------------------------------------------------------------------------
")
    (ansaphone-state . "The Ansaphone is %#1s.")
    (ansaphone-state-r . "The Ansaphone is now %#1s.\n")
    (ansaphone-messages-gone . "Recorded messages have been erased.")
    (ansaphone-no-messages . "No recorded messages.\n")
    (ansaphone-message-list-start . "Recorded messages:\n\n")
    (ansaphone-message-list-end . "\n\n")
    (ansaphone-message-header . "Automatic reply (set %#1s):\n")

    (remote-erase-messages . "Remote control (%#1P %#2s): Erased recorded messages\n")
    (remote-set-message . "Remote control (%#1P %#2s): Ansaphone message:
----------------------------------------------------------------------
%#3t
----------------------------------------------------------------------
")
    (remote-set-message-long . "Remote control (%#1P %#2s): Ansaphone message:
-------------------------------------------------------------------------------
%#3t
-------------------------------------------------------------------------------
")    (remote-set-ansaphone . "Remote control (%#1P %#2s): The ansaphone is %#3s\n")
    (remote-list-messages . "Remote control (%#1P %#2s): Listed recorded messages\n")
    (remote-quit . "Remote control (%#1P %#2s): Quit\n")

    (illegal-remote . 
"Illegal remote control attempt:
Time: %#1s
From: %#2P <%#2p>
To  : %#3P <%#3p>
Text: 
%#4t")
    (illegal-remote-reply . "Remote control rejected: %#1s") 
    (remote-not-in-list . "Unauthorised person")
    (remote-bad-command . "Unknown or malformed command")
    (remote-unknown-error . "Unknown error")

    (remote-control-who . "Remotely control which session? ")
    (remote-control-autoreply . "Ansaphone on or off? ")

    (state-on . "on")
    (state-off . "off")
    

    (text-popup-title . "Text %#1s")
    (conf-popup-title . "Conference %#1s")
    (pers-popup-title . "User %#1s")
    (url-popup-title  . "URL %#1s")
    (aux-popup-title  . "Property")
    (timestamp-popup-title . "Timestamp %#1s")
    (recpt-type-popup-title . "Recipient type: %#1s")
    (add-recpt-button-text . "[Add...]")
    (add-recpt-button-text-regex . "\\[Add\\.\\.\\.\\]")
    (generic-popup-title . "%#1s")

    (who-i-am-not-present . "%#1P (not in any conference) \n")
    (who-i-am-present . "%#1P is present in %#2M\n")
    (who-i-am-client . "The program is lyskom.el, version %#1s%#2?b%[ (MULE)%]%[%].\n")
    (who-i-am-server . "This is %#1s, version %#2s.\n")
    (who-i-am-emacs . "Running under %#1s%#2?b%[ (MULE)%]%[%].\n")

    (no-such-session-r . "That session does not exist. Perhaps the user is not logged on.\n")
    (person-not-logged-in-r . "%#1P %#2?b%[has not been logged on since %#2s%]%[is not logged on%].\n")

    (session-status . "Session %#1d is %#2P <%#2p>
%#5s %#7s %#4M
Using %#6D from %#3s\n")
    (session-status-9 . "Session %#1d is %#2P <%#2p>
%#5s %#7s %#4M
Using %#6D from %#3s
On since %#8s%#9s")
    (session-status-inactive . "\nHas been inactive for %#1s\n")
    (one-day . "one day")
    (one-hour . "one hour")
    (one-minute . "one minute")
    (years . "years")
    (year . "år")
    (month . "month")
    (months . "months")
    (days . "days")
    (day . "day")
    (hours . "hours")
    (minutes . "minutes")
    (and . "and")
    (session-is-active . " and is currently active.\n")
    (session-is-invisible . "This session is invisible.\n")
    (status-for-session . "Session status for whom? ")
    (unknown-doing-what . "Exists")
    (doing-where-conn . "in")
    (doing-nowhere-conn . "but is")
    (waiting-for-membership . "Waiting for the membership list to be fetched...%d/%d")

    ;; From slow.el
    (no-such-command . "There is no such command.\n")
    (command-completions . "You may mean one of the following:\n %#1s\n")

    (which-language . "Change language to: ")
    (send-formatted . "Send as formatted text? ")
    (changing-language-to . "Changing to %#1s.\n")
    (language-set-to . "Language set to %#1s%#2?b%[ %#3@[%#2s]%]%[%].\n")
    (language-not-loaded . "%#1s is unavailable.\n")

    (reformat-html . "HTML")
    (reformat-enriched . "enriched")
    (reformat-filled . "filled")
    (reformat-image . "image")
    (image-no-show . "< The image (%#1s) cannot be shown. Try:\n  %#2t\n  %#3t >")
    (reformat-video . "video")
    (video-no-show . "< Video (%#1s) not supported. >")
    (reformat-audio . "audio")
    (audio-no-show . "< Audio (%#1s) not supported. >")
    (reformat-multipart . "multipart")
    (multipart-no-show . "< Multipart (%#1s) texts not supported. >")
    (reformat-message . "message")
    (message-no-show . "< Message type (%#1s) not supported. >")
    (reformat-model . "model")
    (model-no-show . "< Models (%#1s) not supported. >")
    (reformat-application . "application data")
    (application-no-show . "< Application data (%#1s) not supported. >")
    (reformat-unknown . "unknown type")
    (unknown-no-show . "< Unknown type (%#1s) not supported. >")

    (reformat-truncated . "truncated")

    (reformat-signature . "designed")

    (reformat-deswascii . "was swascii")

    (reformat-utf-8 . "was UTF-8")

    (reformat-rot13 . "rot13")

    (pdf-no-show . "< PDF attachments are not supported. Emacs doc-view is required to display in new window. >")
    (view-pdf . "%#1@< PDF attachments are not supported. > [View PDF in new window]")
    (reformat-pdf . "pdf")
    (doc-view-buffer-name . "Text %#1n: %#2s")

    (need-library . "The \"%#1s\" package is required for this command.\n")
    (calc-expression . "Expression: ")

    (do-send-anonymous . "Send the text anonymously? ")
    (remove-self-sending-anonymous . "Remove yourself as a recipient of the text? ")
    (anonymous . "anonymous")
    (Anonymous . "Anonymous")

    (secret-conf-letter . "S")
    (protected-conf-letter . "P")
    (created-conf-letter . "C")
    (superconf-conf-letter . "S")
    (supervisor-conf-letter . "O")

    ;; Some Help

    (where-is-doesnt-exist . "The command %#1s does not exist")
    (where-is-on-no-key . "%#1s is not on any key")
    (where-is-on-key . "%#1s is on %#2s")

    ;; From aux-items.el

    (content-type-aux . "Content type: %#1s")
    (content-type-regexp . "Content type: \\(\\S-+\\)")

    (agreeing . "Agreeing with text %#1n...")
    (fast-replying . "Remark to text %#1n...")
    (author-fast-replies . "Remarks by the author:")
    (author-fast-reply-aux . "  \"%#1t\"")
    (other-fast-replies . "Remarks:")
    (other-fast-reply-aux . "  \"%#1t\" /%#2P/")
    (fast-reply-too-long . "Can't create remarks that consist of more than one line.\n")

    (conf-to-list-faqs . "List FAQ for which conference: ")
    (faq-for-conf-aux . "FAQ for %#1M <%#1m>")
    (bad-faq-for-conf-aux . "FAQ: \"%<20...#1s\" %[%#2@[invalid]%] %#3s")
    (faq-for-server-aux . "FAQ for the LysKOM-server") 
    (faq-in-text-aux . "FAQ in text:                 %10#1n %#2D")
    (bad-faq-in-text-aux . "FAQ in text:                             \"%<20...#1s %[%#2@[invalid]%] %#3s")
    (conf-to-add-faq . "Which conference do you want to add an FAQ for? ")
    (text-to-add-as-faq . "Which text is the new FAQ? ")
    (text-to-change-as-faq . "Which FAQ do you want to change? ")
    (adding-faq . "Adding text %#1n as an FAQ for %#2?b%[%#2M%]%[the server%]...")
    (conf-to-del-faq . "Which conference do you want to remove an FAQ from? ")
    (text-to-del-as-faq . "Which text do you want to remove as FAQ? ")
    (deleting-faq . "Removing text %#1n as FAQ for %#2?b%[%#2M%]%[the server%]...")
    (conf-has-no-faq . "%#1?b%[%#1M%]%[The server%] has no FAQ\n")
    (unread-which-faq . "Mark FAQ for which conference as unread? ")
    (view-which-faq . "View FAQ for which conference? ")
    (review-faq-for-r . "View FAQ for %#1?b%[%#1M%]%[the server%].\n")
    (set-faq-for-conf-done . "FAQ for %#1?b%[%#1M%]%[the server%] set to text %#3n.\n")
    (set-faq-for-conf-failed . "Unable to set the FAQ for %#1?b%[%#1M%]%[the server%] to text %#3n.\n")
    (changed-faq-for-conf-done . "FAQ in text %#2n for %#1?b%[%#1M%]%[the server%] changed to text %#3n.\n")
    (changed-faq-for-conf-failed . "Unable to change FAQ for %#1?b%[%#1M%]%[the server%] to text %#3n.
%#2?b%[Text %#2n is still the FAQ.
%]%[%]%#4s")
    (faq-in-text . "FAQ in text %#1n %#3s%#4s")
    (faq-in-text-by . "FAQ in text %#1n %#5s %#3sby %#2P %#4s")
    (server-has-new-faq . "\nThere %#1?d%[is a new FAQ%]%[are %#1d new FAQs%] for the server.\n")
    (unread-faqs-header . "\nUnread FAQ%#1?d%[%]%[s%] for %#2?b%[%#2M%]%[the server%]::\n")
    (all-faqs-header . "All FAQs for %#1?b%[%#1M%]%[the server%]:\n")

    (too-many-languages . "Cannot code that many character sets. Send uncoded? ")
    (too-many-languages-error . "Cannot code that many character sets. Unable to send text.")
    (too-many-content-types . "Cannot figure out what content type you want. Simplify the text.")

    (cross-reference-text-status-aux . "See text:              %10#1n /%#2P/")
    (cross-reference-conf-status-aux . "See conference:                         <%#1m> %#1M /%#2P/")
    (cross-reference-pers-status-aux . "See person:                             <%#1p> %#1P /%#2P/")
    (strange-cross-reference-status .  "See also:                                %#1s (whatever that means)")
    (cross-reference-text-aux . "See text %#1n /%#2P/")
    (cross-reference-conf-aux . "See conference <%#1m> %#1M /%#2P/")
    (cross-reference-pers-aux . "See person <%#1p> %#1P /%#2P/")
    (cross-reference-text-regexp . "See text \\([0-9]+\\)")
    (cross-reference-conf-regexp . "See conference <\\([0-9]+\\)>")
    (cross-reference-pers-regexp . "See person <\\([0-9]+\\)>")
    (strange-cross-reference . "See %#1s, whatever that might mean")

    (no-comments-aux . "The author has requested others not to comment this text")
    (no-comments-edit-aux . "Request that others do not comment")
    (no-comments-regexp . "Request that others do not comment")
    
    (personal-comment-aux . "The author has requested private replies only")
    (personal-comment-edit-aux . "Request private replies only")
    (personal-comment-regexp . "Request private replies only")
    
    (request-confirmation-aux . "%#1P has requested confirmation of reading")
    (request-confirmation-edit-aux . "Request confirmation of reading")
    (request-confirmation-regexp . "Request confirmation of reading")
    (confirm-read-q . "Confirm reading text %#1n? ")
    
    (read-confirm-aux . "Confirmed reading: %#1P %#2s")

    (redirect-email-aux . "Send e-mail to %#1s")
    (redirect-lyskom-aux . "Send texts to conference %#1M")

    (label-what-kind . "Set personal label on what (text, conference, user)? ")
    (label-what-text . "Set personal label on text: ")
    (label-what-conf . "Set personal label on which conference? ")
    (label-what-pers . "Set personal label on which person? ")
    (label-what-label . "Personal label: ")
    (label-secret    . "Should others to be able to see the label? ")

    (creating-software-aux . "Created with %#1s")
    (world-readable-text-aux . "The text can be read without logging on")
    (world-readable-text-edit-aux . "Make the text readable without logging on")

    (cant-get-aux-item . "Can't find property\n")
    (aux-item-no-info . "No information available\n")
    (aux-item-info . "\
Number:        %#1d %#6s
Type:          %#2d (%#3s)
Created by:    %#4P <%#4p>
Created:       %#5s
Flags:         %#7s
Inherit limit: %#8s
Contents:     \"%#9s\"
")
    (secret . "Secret")
    (deleted . "Deleted")
    (hide-creator . "Secret creator")
    (inherit . "Inherited")
    (unlimited-inherit . "Unlimited inheritance")
    (no-inheritance . "Inheritance is off")
    (no-more-inheritance . "Inherit limit reached; will not be further inherited")
    (inherit-steps . "%#1d steps")

    (aux-item-for . "Properties for ")
    (aux-item-for-conference-no . "conference <%#1m> %#1M")
    (aux-item-for-text-no . "text %#1n")
    (aux-item-for-server . "the server")

    (what-fast-reply-no . "Remark to which text? ")
    (fast-reply-prompt . "Remark: ")
    (agree-prompt . "Text: ")
    (default-agree-string . "I agree")
    (what-agree-no . "Agree with which text? ")

    (what-kind-to-add-aux-to . "Attach information to what? ")
    (which-conf-to-add-aux-to . "Which conference to you want to attach information to? ")
    (which-text-to-add-aux-to . "Which text to you want to attach information to? ")
    (which-aux-item-tag . "Information tag: ")
    (which-aux-item-inherit . "Set the  inherit-flag? ")
    (which-aux-item-secret . "Set the secret-flag? ")
    (which-aux-item-anonymous . "Set the anonymous-flag? ")
    (which-aux-item-rsv1 . "Set the dont-garb-flag? ")
    (which-aux-item-rsv2 . "Set the reserved2-flag? ")
    (which-aux-item-rsv3 . "Set the reserved3-flag? ")
    (which-aux-item-rsv4 . "Set the reserved4-flag? ")
    (which-aux-item-inherit-limit . "Inherit limit: ")
    (which-aux-item-data . "Data: ")

    ;; Cross-reference stuff

    (Person . "User")
    (person . "user")
    (persons . "users")
    (Conference . "Conference")
    (conference . "conference")
    (conferences . "conferences")
    (Text . "Text")
    (text . "text")
    (texts . "texts")
    (Server . "Server")
    (server . "server")
    (servers . "servers")
    (abc-person . "pPerson")
    (abc-conference . "cConference")
    (abc-text . "tText")

    (xref-type . "What to you want to refer to? ")
    (which-text-to-xref . "Add reference to text: ")
    (which-text-to-xref-err . "Text not found. Add reference to text: ")
    (which-pers-to-xref . "Add reference to user: ")
    (which-conf-to-xref . "Add reference to conference: ")

    ;; Aux-item stuff from edit-text

    (no-comments-q . "The author doesn't want comments. Comment anyway? ")
    (private-answer-q . "The author wants private replies. Write private reply? ")

    (your-invited . "You have been invited to %#1M by %#2P.
By accepting the invitation you will continue to be a member of %#1M.
If you decline the invitation you will not be a member of the conference.
")
    (accept-invitation . "Do you want to accept the invitation now? ")
    (enter-passive . "You are a passive member of %#1M.
You must become an active member of the conference to enter it.\n")
    (convert-passive . "Do you want to become an active member of %#1M? ")
    (bug-secret-mship . "You are a secret member of %#1M\n")

    (invitation-mt-type . "invited")
    (passive-mt-type . "passive")
    (secret-mt-type . "secret")
    (message-flag-off-mt-type . "no group messages")
    (message-flag-on-mt-type . "group messages")

    (Invitation-mt-type . "Invited")
    (Passive-mt-type . "Passive")
    (Secret-mt-type . "Secret")
    (Message-flag-mt-type . "Group messages")

    (not-author . "You are not the author of text %#1n.")
    (not-author-try-anyway-p . "You are not the author of the text. Try anyway? ")
    (what-no-comments-no . "Which text do you want to prevent comments to: ")
    (adding-no-comments . "Preventing comments to text %#1n...")
    (already-no-comments . "Text %#1n already prevents comments.\n")

    (what-private-answer-no . "Which text do you want private replies to: ")
    (adding-private-answer . "Requesting only private replies to text %#1n...")
    (already-private-answer . "Text %#1n already has a request for private replies only.\n")

    (what-request-confirm-no . "Which text do you want read confirmations for: ")
    (adding-request-confirm . "Requesting read confirmations for text %#1n...")
    (already-request-confirm . "Text %#1n already has a request for read confirmations.\n")

    (review-mail-headers-to-what . "Which text's mail headers do you want to see? ")
    (no-mail-headers . "Text %#1n has no mail headers\n")
    (mail-headers-for . "Mail headers for text %#1n:\n")
    (email-name-prefix . "")
    (email-name-suffix . "")

    (you-are-anonymous . "You are now somewhat anonymous.\n")
    (you-are-nonanonymous . "You are no longer anonymous.\n")
    (you-are-already-anonymous . "You are already somewhat anonymous.\n")
    (you-are-already-nonanonymous . "You are already non-anonymous.\n")

    (start-keep-alive . "Sending data at %#1d second intervals to keep the connection active.")
    (stop-keep-alive . "No longer keeping connection active by sending extra data.")

    (review-converted-q . "Review which text converted?")
    (review-noconversion-q . "Review which text unconverted?")
    (review-rot13-q . "Review which text in rot13?")
    (unread-commented-q . "Mark the commented for which text as unread?")
    (review-commented-q . "Review the commented for which text?")

    (review-tree-q . "Review all comments recursively for which text?")
    (find-root-q . "Review root for text?")
    (find-root-review-q . "Review tree of which text?")
    (review-comments-q . "Review all comments to which text?")
    (review-xrefs-q . "Review all cross references from which text?")
    (unread-tree-q . "Unread all comments recursively for which text?")
    (unread-root-q . "Unread root for text?")
    (unread-root-review-q . "Unread tree of which text?")
    (unread-comments-q . "Unread all comments to which text?")

    (confusion-who-to-reply-to . "I can't figure out which text you want to write a private reply to.\n")
    (confusion-what-to-answer-to . "I can't figure out which text you want to write a reply to.\n")
    (confusion-what-to-view . "I can't figure out which text you want to view.\n")

    (confusion-what-to-reply-to . "I can't figure out which text you want to write a remark to.\n")
    (confusion-what-to-agree-to . "I can't figure out which text you want to agree with.\n")
    (confusion-what-to-request-confirmation . "I can't figure out which text you want read confirmations for.\n")
    (confusion-what-to-comment . "I can't figure out which text you want to comment.\n")
    (confusion-what-to-footnote . "I can't figure out to which text you want to write a footnote to.\n")

    (confusion-what-to-delete . "I can't figure out which text you want to delete.\n")
    (confusion-what-to-add-sub-recipient . "I can't figure out which text you want to %#1s.\n")
    (add-rcpt-action-name . "add a recipient to")
    (add-copy-action-name . "add a carbon copy recipient to")
    (add-bcc-action-name . "add a blind carbon copy recipient to")
    (sub-action-name . "remove a recipient from")
    (move-action-name . "move")

    (confusion-what-to-add-comment-to . "I can't figure out which text you want to add a comment to.\n")
    (confusion-what-to-sub-comment-from . "I can't figure out which text you want to remove a comment from.\n")
    (confusion-what-to-add-footnote-to . "I can't figure out which text you want to add a footnote to.\n")
    (confusion-what-to-sub-footnote-from . "I can't figure out which text you want to remove a footnote from.\n")
    
    (confusion-what-to-mark . "I can't figure out which text you want to mark.\n")
    (confusion-what-to-unmark . "I can't figure out which text you want to unmark.\n")
    (confusion-what-to-save . "I can't figure out which text you want to save.\n")

    (confusion-what-to-review-mail-headers . "I can't figure out which text's mail headers you want to see.\n")
    (confusion-what-to-unread-root . "I can't figure out which text's root you want to mark as unread.\n")
    (confusion-what-to-unread-root-review . "I can't figure out which tree you want to mark as unread.\n")
    (confusion-what-to-find-root . "I can't figure out which text's root you want to see.\n")
    (could-not-find-root . "Unable to identify the root text. The comment structure may be cyclic.\n")
    (confusion-what-to-find-root-review . "I can't figure out which tree you want to review.\n")

    ;; Help stuff

    (help-with-what . "What do you want help with? ")
    (help-for . "Help for \"%#1s\"\n")

    ;; Button actions

    (lyskom-button-view-text-action . "View text")
    (lyskom-button-unread-text-action . "Unread text")
    (lyskom-button-copy-text-no-action . "Copy text number")
    (lyskom-button-review-converted-action . "Review converted")
    (lyskom-button-review-noconversion-action . "Review unconverted")
    (lyskom-button-review-rot13-action . "Review rot13")
    (lyskom-button-find-root-review-action . "Review tree")
    (lyskom-button-find-root-action . "Review root text")
    (lyskom-button-comment-text-action . "Write comment")
    (lyskom-button-private-comment-text-action . "Write personal reply")
    (lyskom-button-mark-text-action . "Mark text")
    (lyskom-button-unmark-text-action . "Unmark text")
    (lyskom-button-save-text-action . "Save archive copy")
    (lyskom-button-save-text-body-action . "Save text body")
    (lyskom-button-review-comments-action . "Review all comments")
    (lyskom-button-review-tree-action . "Review all comments recursively")
    (lyskom-button-write-footnote-action . "Write footnote")
    (lyskom-button-fast-reply-action . "Remark")
    (lyskom-button-view-conf-presentation-action . "View presentation")
    (lyskom-button-view-conf-status-action . "View conference status")
    (lyskom-button-goto-conf-action . "Go to conference")
    (lyskom-button-send-message-action . "Send group message")
    (lyskom-button-add-self-action . "Join conference")
    (lyskom-button-sub-self-action . "Leave conference")
    (lyskom-button-view-pers-presentation-action . "View presentation")
    (lyskom-button-view-pers-status-action . "View user status")
    (lyskom-button-view-session-status-action . "View session status")
    (lyskom-button-mail-action . "Send letter")
    (lyskom-button-send-message-action . "Send personal message")
    (lyskom-button-moronify-action . "Add to morons list")
    (lyskom-button-befriend-action . "Add to friend list")
    (lyskom-button-open-url-action . "Open")
    (lyskom-button-copy-url-action . "Copy")
    (lyskom-button-goto-info-node-action . "Open")
    (lyskom-button-open-email-action . "Send mail")
    (lyskom-button-copy-email-action . "Copy")
    (lyskom-button-info-aux-action . "Information")
    (lyskom-button-delete-aux-action . "Delete")
    (lyskom-button-copy-timestamp-action . "Copy")
    (lyskom-button-recpt-type-recipient . "Recipient")
    (lyskom-button-recpt-type-copy . "Carbon copy")
    (lyskom-button-recpt-type-bcc . "Blind Carbon copy")
    (lyskom-button-recpt-type-sub . "Remove")
    (lyskom-button-recpt-add-recipient . "Recipient")
    (lyskom-button-recpt-add-copy . "Carbon copy recipient")
    (lyskom-button-recpt-add-bcc . "Blind carbon copy recipient")
    (lyskom-button-aux-type-xref . "Reference to conference/person/text")
    (lyskom-button-aux-type-no-comments . "Request no comments")
    (lyskom-button-aux-type-personal-comments . "Request personal reply")

    (lyskom-edit-toggle-secret-aux-action . "Toggle \"secret\"")
    (lyskom-edit-toggle-anonymous-aux-action . "Toggle \"anonymous\"")
    (lyskom-edit-toggle-inherit-aux-action . "Toggle \"inherited\"")
    (lyskom-edit-delete-aux-action . "Delete")
    (lyskom-prioritize-flag-toggle-action . "Toggle")
    (lyskom-prioritize-flag-set-action . "Set")
    (lyskom-prioritize-flag-clear-action . "Clear")

    (server-status-header   . "Status for LysKOM-server %#1s%#2?b%[ (%#2s:%#3d)%]%[%]\n\n")
    (server-status-server   . "Canonical server name:                   %#1s%#2?b%[:%#2s%]%[%]\n")
    (server-status-version  . "Software version:                        %#1s %#2s\n")
    (server-status-protocol . "Protocol version:          %15#1d\n")
    (server-status-sessions . "\
Number of sessions:  %21#1d (total)
                     %21#2d active in the last %#7d minutes
                     %21#3d inactive sessions
                     %21#4d unknown activity
                     %21#5d invisible sessions
                     %21#6d not logged on/secret/zombies\n")
    (server-status-first-text . "Oldest existing text:       %14#1n\n")
    (server-status-last-text  . "Youngest existing text:     %14#1n%#2?b%[ (%#2n at start)%]%[%]\n")
    (server-status-texts .      "Number of texts:            %14#1d (%#2s now, %#3d at start)\n")
    (server-status-confs .      "Number of conferences:      %14#1d (%#2s now, %#3d at start)\n")
    (server-status-pers .       "Number of persons:          %14#1d (%#2s now, %#3d at start)\n")
    (server-status-has-motd . "\nThe server has a notice:\n")
    (server-status-time .      "Current server time:            %#1s\n")
    (server-status-boot-time . "Server start time:              %#1s\n")
    (server-status-save-time . "Initial database save time:     %#1s%#2?b%[ (%#2s)%]%[%]\n")

    (mship-type-invitation-q . "Membership invitation? ")
    (mship-type-passive-q . "Passive membership? ")
    (mship-type-secret-q . "Secret membership? ")
    (recommend-which-conf . "Which conference do you want to recommend? ")
    (recommend-set-priority-q . "Recommend a priority? ")
    (recommend-set-mship-type-q . "Recommend a membership type? ")
    (recommending-conf . "Recommending %#1M%#2?b%[ (priority %#2d)%]%[%]%#3?b%[ %#3s%]%[%]...")
    (has-recommended-conf . "%#1P has recommended conference %#2M. Do you want to become a member?\n")
    (accept-recommendation . "Join conference %#1M? ")
    (accepting-recommendation . "Joining recommended conference %#2M...")

    (redirect-for-whom . "Redirect comments for whom: ")
    (redirect-to-which-conf . "Redirect comments to: ")
    (redirecting-comments-to . "Redirecting comments for %#1P to %#2M%#3?b%[ (old %#1M)%]%[%]...")

    (kom-redirect-comments-e49 . "You do not have permission to perform this redirection")
    (kom-redirect-comments-e48 . "Comment redirection is not supported by the server")

    (external-program-missing . "Can't find the external program \"%#1s\" that is required for this command.")
    (ignored-user-area-var . "\

Ignoring the following variables in your settings:

    %#1s

These variables are not registered as LysKOM variables and cannot be
read from the server. You can remove the variables from the server by
giving the command \"Save options\".

%#2?b%[%#5F\
Unknown variables may be the result of saving your settings in version
 %#3s of the client; this is version %#4s.

%]%[%]")


    (unknown-aux-item . "Unknown property")
    (text-header-aux-item . "%#1s: <%#2d> \"%#3s\" %#4s")

    (aux-content-type-name . "Content type")
    (aux-fast-reply-name . "Remark")
    (aux-cross-reference-name . "Reference")
    (aux-no-comments-name . "Request for no comments")
    (aux-personal-comment-name . "Request for personal replies")
    (aux-request-confirmation-name . "Request for read confirmation")
    (aux-read-confirm-name . "Read confirmation")
    (aux-redirect-name . "Redirect")
    (aux-x-face-name . "Picture")
    (aux-alternate-name-name . "Alternate name")
    (aux-pgp-signature-name . "PGP signature")
    (aux-pgp-public-key-name . "PGP public key")
    (aux-e-mail-address-name . "E-mail address")
    (aux-faq-text-name . "FAQ in text")
    (aux-creating-software-name . "Creating software")
    (aux-mx-author-name . "E-mail author")
    (aux-mx-from-name . "E-mail from")
    (aux-mx-reply-to-name . "E-mail reply to")
    (aux-mx-to-name . "E-mail to")
    (aux-mx-cc-name . "E-mail CC")
    (aux-mx-date-name . "E-mail date")
    (aux-mx-message-id-name . "E-mail message ID")
    (aux-mx-in-reply-to-name . "E-mail in reply to")
    (aux-mx-misc-name . "E-mail headers")
    (aux-mx-allow-filter-name . "E-mail allow-filter")
    (aux-mx-reject-forward-name . "E-mail reject-forward")
    (aux-mx-allow-envelope-sender-regexp . "Allowed senders for mail import")
    (aux-notify-comments-name . "Notify me about comments")
    (aux-faq-for-conf-name . "FAQ for conference")
    (aux-recommended-conf-name . "Conference recommendation")
    (aux-allowed-content-type-name . "Allowed content type")
    (aux-canonical-name-name . "Canonical name")
    (aux-mx-list-name-name . "Mailing list name")
    (aux-send-comments-to-name . "Send comments to")
    (aux-world-readable-name . "World-readable")
    (aux-mx-refuse-import . "Refuse text import")
    (aux-elisp-client-read-faq-name . "Read FAQ")
    (aux-elisp-client-rejected-invitation-name . "Rejected invitation")

    (review-marking-as-read . "Review marks as read")
    (review-not-marking-as-read . "Review does not mark as read")

    (review-using-cache . "Review uses cache")
    (review-not-using-cache . "Review doesn't use cache")

    (using-ssh-connection . "Using ssh connection to %#1s...")
    (opening-ssh-connection . "Opening ssh connection to %#1s...")
    (ssh-cant-connect . "Unable to open ssh connection: %s")
    (ssh-closing . "Closing ssh connection to %#1s")
    (ssh-unknown-host . "unknown host")

    (keyboard-cancel . "Cancel")
    (keyboard-menu-help . "(choose: C-n, C-p; confirm: RET)")
    (customize-help . "See the beginning of the buffer for more information")

    (no-mule-warning . "\

%#1@You have disabled support for multibyte characters (MULE) in emacs.
This may be due to a call to standard-display-european in your .emacs
or in one of the shared emacs startup files.

This software is designed to run with support for multibyte characters
enabled. Without this support texts may be displayed incorrectly, and
you may encounter problems writing texts. Consider enabling sypport
for multibyte characters in emacs.

")
    (coding-system-mismatch-warning . "\

%#3@Your emacs is configured to encode characters using \"%#1s\",
but the LysKOM server you are using recommends \"%#2s\".

You may encounter problems when writing comments, and comments to your
texts may contain a mix of characters that cannot be encoded or
displayed correctly. Please consider changing your language
environment to one that uses \"%#2s\" to encode text (see the Emacs
manual and the command set-language-environment for more information).

")


    (has-nameday-1 . "Today's name is %#1s%#2?b%[ (%#2s)%]%[%].")
    (has-nameday-2 . "Today's names are %#1s and %#2s%#3?b%[ (%#3s)%]%[%].")
    (no-nameday . "No nameday today%#2?b%[ (%#2s)%]%[%]")

    (invalid-date-entry . "Invalid date")
    (number-out-of-range . "Invalid number")
    (or-date . "or date")

    (set-unread-n . "Only read the most recent %#1?d%[text%]%[%#1d texts%].\n")
    (set-unread-date . "Only read texts since %#2s %#3d, %#1d.\n")

    (jump-from-text . "Skip comments to which text? ")
    (jumping-from-text . "Skipping comments to text %#1n.\n")
    (jumping-from-text-review . "Skipping reviewed comments.\n")

    (session-to-copy-options-from . "Session to copy options from: ")
    (reading-settings-from . "Reeading options from %s...")
    (reading-settings-from-done . "Reading options from %s...done")

    (super-jump-q . "Super jump which text? ")
    (conf-list-legend . "* Not member; - Passive member\n")

    (unread-text-q . "Make which text unread? ")
    (marking-text-unread . "Marking text %#1n as unread...")
    (cant-mark-text-unread . "Couldn't mark %#1n as unread (%#2s)\n")
    (confusion-what-to-mark-unread . "I can't figure out what text you want to mark as unread.\n")
    (no-comment-to-to-unread . "There is no commented text to mark as unread.\n")

    (mx-refuse-import-html . "HTML")
    (mx-refuse-import-spam . "Spam")
    (mx-refuse-import-all . "All texts")
    (abc-html . "hHTML")
    (abc-spam . "sSpam")
    (abc-everything . "aAll texts")
    (abc-regexp . "rAllow sender")
    (limit-import-regexp-q . "Allow only which sender (regexp)? ")
    (limit-import-to-conf . "Limit import to which conference? ")
    (limit-import-of-what . "Limit import of what? ")
    (limit-import-not-super . "You do not seem to be the supervisor of the conference. Try anyway? ")
    (limiting-import . "Limiting import of  %#1s to %#2M...")
    (limiting-import-regexp . "Limiting import to %#2M to sender %#1s...")

    (set-message-flag-for-conf . "Sett message flag for which conference? ")
    (set-message-flag-to-what . "Receive grupp messages to %#1M? ")
    (setting-message-flag . "%#1?b%[Enabling%]%[Disabling%] reception of group messages to %#2M...")

    (text-has-no-comments . "Text %#1n has no comments\n")
    (text-has-no-footnotes . "Text %#1n has no footnotes\n")
    (set-message-flag-q . "Accept group messages to %#1M? ")

    (why-you-got-no-presentation . "\

%#1@\
========================================================================
You do not have a presentation%#2?z%[ but you've written %#2d texts%]%[%]

Use the command \"Change presentation\" to write or change your 
presentation.


%[%#4F\
Your presentations serves to give other users of %#3s some information
about who you are, and this contributes to the sense of community in
%#3s. This message will be repeated at random intervals until you have
written a presentation.%]
========================================================================

")

    (delete-user-area-warning . "\
%#1@\
========================================================================
If you delete your user area, all your saved settings will be lost. You
can restore the user area by saving your current settings, but any
saved settings from other clients will still be lost.

Are you completely certain that you know what you are doing and that
you really want to delete your user area?
========================================================================
")
    (delete-user-area-confirm . "Delete your user area and all saved settings? ")
    (removing-user-area . "Removing reference to user area from person status...")
    (deleting-user-area . "Deleting the user area...")

    (status-server-stats . "Server statistics:\n\n")
    (current-average . "current")

    (unit-second . "second")
    (unit-minute . "minute")
    (unit-hour . "hour")
    (unit-day . "day")
    (unit-mongth . "month")
    (unit-year . "year")

    (db-status-backup . "information loss may have occurred")
    (no-support-in-server . "No support in server for this command.\n")
    (mark-confs-as-known . "Mark these %#2d %#1s as known? ")
    (no-new-conferences . "No new %#2s%#1?b%[ since %#1s%]%[%].\n")
    (new-conferences-since . "New %#2s%#1?b%[ since %#1s%]%[%]:\n")
    (list-confs-from-date . "List %#1s created since what date (empty for everything)? ")

    (privs-wheel . "wheel")
    (privs-admin . "administrator")
    (privs-statistic . "statistics")
    (privs-create-conf . "create conferences")
    (privs-create-pers . "create persons")
    (privs-change-name . "change names")
    (privs-flg7 . "unknown (7)")
    (privs-flg8 . "unknown (8)")
    (privs-flg9 . "unknown (9)")
    (privs-flg10 . "unknown (10)")
    (privs-flg11 . "unknown (11)")
    (privs-flg12 . "unknown (12)")
    (privs-flg13 . "unknown (13)")
    (privs-flg14 . "unknown (14)")
    (privs-flg15 . "unknown (15)")
    (privs-flg16 . "unknown (16)")
    (lyskom-no-privileges . "no permissions")

    (what-pers-privs-to-change . "Whose privileges do you want to change? ")
    (change-pers-privs-prompt . "\
Current privileges for %#1P (%#1p):
    %#2s
Change privileges for %#1P (%#1p)...")

    (set-wheel-priv-q . "Activate wheel privileges? ")
    (set-admin-priv-q . "Activate administrator privileges? ")
    (set-statistic-priv-q . "Activate statistics privileges? ")
    (set-create-conf-priv-q . "Activate right to create conferences? ")
    (set-create-pers-priv-q . "Activate right to create persons? ")
    (set-change-name-priv-q . "Activate right to change names? ")
    (set-flg7-priv-q . "Activate unknown privilege 7? ")
    (set-flg8-priv-q . "Activate unknown privilege 8? ")
    (set-flg9-priv-q . "Activate unknown privilege 9? ")
    (set-flg10-priv-q . "Activate unknown privilege 10? ")
    (set-flg11-priv-q . "Activate unknown privilege 11? ")
    (set-flg12-priv-q . "Activate unknown privilege 12? ")
    (set-flg13-priv-q . "Activate unknown privilege 13? ")
    (set-flg14-priv-q . "Activate unknown privilege 14? ")
    (set-flg15-priv-q . "Activate unknown privilege 15? ")
    (set-flg16-priv-q . "Activate unknown privilege 16? ")

    (canceling-command . "Canceling command...")

    ;; From mship-edit.el

    (no-selection . "No selection")
    (selection . "%#1d selected")
    (lp-no-hidden . "")
    (lp-hidden . "(%#1d hidden)")
    (priority-prompt . "New priority for %#1M: ")
    (priority-prompt-marked . "New priority for selected conferences: ")
    (lp-no-creation-info . "No information on when the membership was created")
    (lp-invited . "Invited")
    (lp-added . "Added")
    (lp-nope . "Operation failed: %#1s")
    (lp-no-entry . "No membership on this line")
    (lp-no-active-filter . "(no filters active)")
    (lp-active-filters . "Active filters: %#1s")
    (lp-mark-mship-with-prio . "Select memberships with priority: ")
    (lp-unmark-mship-with-prio . "Deselect memberships with priority: ")
    (lp-no-selection . "No memberships selected")
    (lp-at-min-prio . "Already at minimum priority")
    (lp-at-max-prio . "Already at maximum priority")
    (lp-beginning-of-list . "Beginning of list")
    (lp-end-of-list . "End of list")
    (lp-goto-priority . "Go to priority: ")
    (lp-mode-name . "Membership")
    (lp-header-main . "Membership for %#1M on %#2s")
    (lp-list-header . " Prio   %#1s  Last entered Unread IHPM\n")
    (lp-help-footer . "
 Select membership:  SPC      Select region:  C-w      Flytta markerade:   C-y
 Set priority:       p        Increase prio:  +        Decrease prio:      -
 Move up:            M-p      Move down:      M-n      Toggle flags:   I,H,P,M
 Postpone reading:   P        Only last:      o   
 Quit:               C-c C-c                           More help:        C-h m
")
    (lp-hide-read-after . "Hide memberships read after: ")
    (lp-hide-read-since . "Hide memberships not read since: ")
    (lp-skipping-missing-meeting . "Conference %#1M no longer exists, skipping.")
    ))


(lyskom-language-var local lyskom-month-names en
  '(("january" . 1)   ("jan" . 1)
    ("february" . 2)  ("feb" . 2)
    ("march" . 3)     ("mar" . 3)
    ("april" . 4)     ("apr" . 4)
    ("may" . 5)       ("may" . 5)
    ("june" . 6)      ("jun" . 6)
    ("july" . 7)      ("jul" . 7)
    ("august" . 8)    ("aug" . 8)
    ("september" . 9) ("sep" . 9)
    ("october" . 10)  ("oct" . 10)
    ("november" . 11) ("nov" . 11)
    ("december" . 12) ("dec" . 12)))


;;; ================================================================
;;;              The commands and their associated functions


;;; Formely known as lyskom-commands
(lyskom-language-strings local lyskom-command en
  '(
    (kom-help                 . "Help")
    (kom-slow-mode            . "Long commands")
    (kom-quick-mode           . "Short commands")
    (kom-moronify             . "Add to morons list")
    (kom-befriend             . "Add to friend list")
    (kom-send-message         . "Send message")
    (kom-send-alarm           . "Send alarm")
    (kom-create-conf          . "Create conference")
    (kom-delete-conf          . "Delete conference/user")
    (kom-delete-text          . "Remove text")
    (kom-display-time         . "Time")
    (kom-go-to-conf           . "Go (to) conference")
    (kom-go-to-next-conf      . "(Go to) next conference")
    (kom-jump                 . "Skip (all) comments")
    (kom-list-created-conferences     . "List owned conferences") 
    (kom-list-conferences     . "List conferences") 
    (kom-list-persons         . "List users")
    (kom-list-news            . "List news")
    (kom-list-sessions        . "List sessions")
    (kom-list-re              . "List (using) regexps")
    (kom-membership           . "List memberships")
    ;; (kom-list-marks	      . "List marks")
    (kom-postpone             . "Postpone reading")
    (kom-set-session-priority . "Set reading level")
    (kom-prioritize           . "Prioritize conferences")
    (kom-status-person        . "Status (of) person")
    (kom-status-conf          . "Status (of) conference")
    (kom-add-self             . "Join conference")
    (kom-change-priority      . "Change priority")
    (kom-list-summary         . "List text (subjects)")
    (kom-sub-self             . "Leave (a) conference")
    (kom-quit                 . "Quit")
    (kom-recover              . "Recover") 
    (kom-start-anew           . "New User")
    (kom-view                 . "Review text")
    (kom-find-root-review     . "Review tree")
    (kom-review-comments      . "Review all comments")
    (kom-review-more-comments . "Review more comments")
    (kom-review-tree          . "Review all comments recursively")
    (kom-review-clear         . "Review and skip")
    (kom-review-last-normally-read
     			      . "Review again")
    (kom-review-cross-references
     			      . "Review all cross references")
    (kom-review-converted     . "Review converted")
    (kom-review-noconversion  . "Review unconverted")
    (kom-review-rot13         . "Review rot13")
    (kom-review-next          . "Review next")
    (kom-find-root            . "Review root (text)")
    (kom-review-by-to         . "Review last")
    (kom-review-roots-by-to         . "Review last roots")
    (kom-review-first         . "Review first")
    (kom-review-first-roots         . "Review first roots")
    (kom-review-all           . "Review all")
    (kom-review-all-roots           . "Review all roots")
    (kom-review-more          . "Review more")
    (kom-view-commented-text  . "Review (the) commented (text)")
    (kom-view-previous-commented-text
     			      . "Review (the) previously commented (text)")
    (kom-review-stack         . "Review stack")
    (kom-review-presentation  . "Review presentation")
    (kom-review-backward      . "(Review) Backwards")
    (kom-view-next-text       . "(Read) next text")
    (kom-who-is-on            . "Who (is on)")
    (kom-who-is-on-in-conference
                              . "Who (is on in) conference")
    (kom-who-is-present-in-conference
                              . "Who (is) present (in) conference")
    (kom-who-is-on-and-friend . "Which friends (are on)")
    (kom-who-am-i             . "Where (am) i")
    (kom-list-clients	      . "List clients")
    (kom-busy-wait            . "Wait (for news)")
    (kom-write-comment        . "(Write) comment")
    (kom-comment-previous     . "(Write) comment (to) previous text")
    (kom-write-footnote       . "(Write) footnote")
    (kom-private-answer       . "(Write) personal reply (by letter)")
    (kom-private-answer-previous
   . "(Write) personal (reply to) previous text (by letter)")
    (kom-set-unread     	      . "Only (the) last")
    (kom-write-text           . "Write (a) text")
    (kom-send-letter          . "Write (a) letter")
    (kom-change-name          . "Change name")
    (kom-change-parenthesis   . "Change parenthesis")
    (kom-change-password      . "Change password")
    (kom-change-supervisor    . "Change supervisor")
    (kom-change-presentation  . "Change presentation")
    (kom-get-appreciation     . "(Please) pat my head")
    (kom-get-abuse            . "(Please) kick my butt")
    (kom-mark-text            . "Mark (text)")
    (kom-unmark-text          . "Unmark (text)")
    (kom-review-marked-texts  . "Review marked (texts)")
    (kom-review-all-marked-texts . "Review all marked (texts)")
    (kom-add-recipient        . "Add recipient")
    (kom-add-copy             . "Add (recipient of) carbon copy")
    (kom-add-bcc	      . "Add (recipient of) blind carbon copy")
    (kom-sub-recipient        . "Remove recipient")
    (kom-move-text            . "Move text")
    (kom-move-text-tree       . "Move tree")
    (kom-add-comment          . "Add comment")
    (kom-sub-comment          . "Remove comment")
    (kom-move-comment	      . "Move comment")
    (kom-add-cross-reference  . "Add cross reference")
    (kom-add-member           . "Add (a) member")
    (kom-sub-member           . "Remove (a) member")
    (kom-change-conf-motd     . "(Post) notice (on the) conference")
    (kom-set-garb-nice        . "Change expiration")
    (kom-set-super-conf       . "Change superconference")
    (kom-set-permitted-submitters . "Change allowed authors")
    (kom-unset-conf-motd      . "Remove notice")
    (kom-save-text            . "Archive (copy of) text (in file)")
    (kom-save-text-body       . "Save text body (in file)")
    (kom-save-options         . "Save options")
    (kom-shutdown-server      . "Shut down (server)")
    (kom-sync-database        . "Save (the) database")
    (kom-enable-adm-caps      . "Become administrator")
    (kom-disable-adm-caps     . "Become (normal) user")
    (kom-set-motd             . "Change login message")
    (kom-remove-motd          . "Remove login message")
    (kom-force-logout         . "Kill session")
    (kom-filter-author        . "Filter author")
    (kom-filter-subject       . "Filter subject")
    (kom-filter-recipient     . "Filter recipient")
    (kom-super-jump           . "Super jump")
    (kom-filter-edit          . "Edit filters")
    (kom-filter-text          . "Filter contents")
    (kom-list-filters         . "List filters")
    (kom-show-user-area       . "Show user area")
    (kom-delete-user-area     . "Remove user area")
    (kom-change-conf-type     . "Change conference type")

    (kom-change-auto-reply    . "Change ansaphone message")
    (kom-toggle-auto-reply    . "Ansaphone")
    (kom-list-messages        . "List messages")
    (kom-erase-messages       . "Erase messages")

    (kom-remote-autoreply     . "Remote control ansaphone")
    (kom-remote-set-message   . "Remote control change ansaphone message")
    (kom-remote-list-messages . "Remote control list messages")
    (kom-remote-erase-messages . "Remote control erase messages")
    (kom-remote-quit          . "Remote control quit")

    (kom-status-session       . "Status (of) session")
    (kom-customize            . "Customize LysKOM")
    (kom-next-kom             . "Next LysKOM")
    (kom-previous-kom         . "Previous LysKOM")
    (kom-next-unread-kom      . "Next unread LysKOM")
    (kom-change-language      . "Change language")
    (kom-calculate            . "Calculate")
    (kom-list-marks           . "List marked texts")
    (kom-where-is             . "Where (is the) command")
    (kom-fast-reply           . "Remark (to text)")
    (kom-agree                . "Agree")
    (kom-add-faq              . "Add FAQ")
    (kom-del-faq              . "Remove FAQ")
    (kom-review-faq           . "View FAQ")

    (kom-sub-footnote         . "Remove footnote")
    (kom-add-footnote         . "Add footnote")

    (kom-add-no-comments      . "Prevent comments")
    (kom-add-private-answer   . "Request private reply")
    (kom-add-request-confirm  . "Request confirmation of reading")

    (kom-review-mail-headers  . "Review mail headers")

    (kom-compare-texts        . "Compare two texts")
    (kom-diff-texts           . "View diff")

    (kom-view-url-in-text     . "View URL")

    (kom-become-anonymous     . "Become anonymous")
    (kom-become-nonanonymous  . "Become non-anonymous (come forth into the light)")

    (kom-keep-alive           . "Keep connection alive")
    (kom-stop-keep-alive      . "Stop keeping connection alive")

    (kom-is-person-member-of-conference . "Check (if person is a) member (of conference)")
    (kom-will-person-read-text . "Check (if person will) read (text)")

    (kom-change-conf-faq      . "Change FAQ")

    (kom-make-review-mark-as-read . "Review marks as read")
    (kom-make-review-not-mark-as-read . "Review does not mark as read")

    (kom-set-presentation     . "Add presentation")
    (kom-remove-presentation  . "Remove presentation")
    (kom-set-motd-text        . "Add notice")
    (kom-create-aux-item      . "Create propoerty")
    (kom-status-server        . "Status (of) server")
    (kom-add-server-faq       . "Add server FAQ")
    (kom-del-server-faq       . "Remove server FAQ")
    (kom-review-server-faq    . "Review server FAQ")
    (kom-change-server-faq    . "Change server FAQ")
    (kom-recommend-conference . "Recommend conference")
    (kom-redirect-comments    . "Redirect comments")
    (kom-copy-options         . "Copy options")
    (kom-mark-unread          . "Unread text")
    (kom-unread-by-to         . "Unread last")
    (kom-unread-roots-by-to         . "Unread last")
    (kom-unread-more          . "Unread more")
    (kom-unread-commented-text . "Unread (the) commented (text)")
    (kom-unread-previous-commented-text . "Unread (the) previously commented (text)")
    (kom-unread-comments      . "Unread all comments")
    (kom-unread-tree          . "Unread all comments recursively")
    (kom-unread-root          . "Unread original (text)")
    (kom-unread-root-review   . "Unread tree")
    (kom-unread-last-normally-read
     			      . "Unread again")
    (kom-unread-first         . "Unread first")
    (kom-unread-first-roots   . "Unread first roots")
    (kom-unread-all           . "Unread all")
    (kom-unread-all-roots     . "Unread all roots")

    (kom-unread-presentation  . "Unread presentation")
    (kom-unread-server-faq    . "Unread server FAQ")
    (kom-unread-faq           . "Unread FAQ")
    (kom-unread-marked-texts  . "Unread marked (texts)")
    (kom-unread-all-marked-texts . "Unread all markeed (texts)")

    (kom-join-all-conferences . "Join all conferences")
    (kom-leave-all-conferences . "Leave (almost) all conferences")
    (kom-limit-import         . "Limit imported texts")
    (kom-change-message-flag  . "Change reception (of) group messages")
    (kom-list-faqs            . "List FAQs")
    (kom-list-server-faqs     . "List server-FAQs")
    (kom-list-new-conferences . "List new conferences")
    (kom-list-new-persons     . "List new users")
    (kom-change-privileges    . "Change privileges")
    ))

(lyskom-language-var global lyskom-language-codes en
  '((aa . "Afar")
    (ab . "Abkhazian")
    (af . "Afrikaans")
    (am . "Amharic")
    (ar . "Arabic")
    (as . "Assamese")
    (ay . "Aymara")
    (az . "Azerbaijani")
    (ba . "Bashkir")
    (be . "Byelorussian")
    (bg . "Bulgarian")
    (bh . "Bihari")
    (bi . "Bislama")
    (bn . "Bengali")
    (bo . "Tibetan")
    (br . "Breton")
    (ca . "Catalan")
    (co . "Corsican")
    (cs . "Czech")
    (cy . "Welsh")
    (da . "Danish")
    (de . "German")
    (dz . "Bhutani")
    (el . "Greek")
    (en . "English")
    (eo . "Esperanto")
    (es . "Spanish")
    (et . "Estonian")
    (eu . "Basque")
    (fa . "Persian")
    (fi . "Finnish")
    (fj . "Fiji")
    (fo . "Faroese")
    (fr . "French")
    (fy . "Frisian")
    (ga . "Irish")
    (gd . "Scots Gaelic")
    (gl . "Galician")
    (gn . "Guarani")
    (gu . "Gujarati")
    (ha . "Hausa")
    (he . "Hebrew")
    (hi . "Hindi")
    (hr . "Croatian")
    (hu . "Hungarian")
    (hy . "Armenian")
    (ia . "Interlingua")
    (id . "Indonesian")
    (ie . "Interlingue")
    (ik . "Inupiak")
    (is . "Icelandic")
    (it . "Italian")
    (iu . "Inuktitut")
    (ja . "Japanese")
    (jw . "Javanese")
    (ka . "Georgian")
    (kk . "Kazakh")
    (kl . "Greenlandic")
    (km . "Cambodian")
    (kn . "Kannada")
    (ko . "Korean")
    (ks . "Kashmiri")
    (ku . "Kurdish")
    (ky . "Kirghiz")
    (la . "Latin")
    (ln . "Lingala")
    (lo . "Laotian")
    (lt . "Lithuanian")
    (lv . "Latvian Lettish")
    (mg . "Malagasy")
    (mi . "Maori")
    (mk . "Macedonian")
    (ml . "Malayalam")
    (mn . "Mongolian")
    (mo . "Moldavian")
    (mr . "Marathi")
    (ms . "Malay")
    (mt . "Maltese")
    (my . "Burmese")
    (na . "Nauru")
    (ne . "Nepali")
    (nl . "Dutch")
    (no . "Norwegian")
    (oc . "Occitan")
    (om . "Oromo")
    (or . "Oriya")
    (pa . "Pundjabi")
    (pl . "Polish")
    (ps . "Pashto")
    (pt . "Portuguese")
    (qu . "Quechua")
    (rm . "Rhaeto-Romance")
    (rn . "Kirundi")
    (ro . "Romanian")
    (ru . "Russian")
    (rw . "Kiyarwanda")
    (sa . "Sanskrit")
    (sd . "Sindhi")
    (sg . "Sangho")
    (sh . "Serbo-Croatian")
    (si . "Singhalese")
    (sk . "Slovak")
    (sl . "Slovenian")
    (sm . "Samoan")
    (sn . "Shona")
    (so . "Somali")
    (sq . "Albanian")
    (sr . "Serbian")
    (ss . "Siswati")
    (st . "Sesotho")
    (su . "Sudanese")
    (sv . "Swedish")
    (sw . "Swahili")
    (ta . "Tamil")
    (te . "Telugu")
    (tg . "Tajik")
    (th . "Thai")
    (ti . "Tigrinya")
    (tk . "Turkmen")
    (tl . "Tagalog")
    (tn . "Setswana")
    (to . "Tonga")
    (tr . "Turkish")
    (ts . "Tsonga")
    (tt . "Tatar")
    (tw . "Twi")
    (ug . "Uigur")
    (uk . "Ukrainian")
    (ur . "Urdu")
    (uz . "Uzbek")
    (vi . "Vietnamese")
    (vo . "Volapük")
    (wo . "Wolof")
    (xh . "Xhosa")
    (yi . "Yiddish")
    (yo . "Yorouba")
    (za . "Zhuang")
    (zh . "Chinese")
    (zu . "Zulu")
    (-- . "Unknown language (%#1s)")))

(lyskom-language-missing-string lyskom-menu kom-help en)
(lyskom-language-missing-string lyskom-menu kom-change-language en)

(lyskom-language-strings global lyskom-menu en
  '((lyskom    . "LysKOM")
    (read      . "Read")
    (dont-read . "Jump")
    (write     . "Write")
    (conference . "Conference")
    (other     . "Other")
    (person    . "User")
    (server    . "Server")
    (marks     . "Marks")
    (filters   . "Filters")
    (move      . "Go")
    (info      . "About")
    (send      . "Send message")
    (review    . "Review")
    (unread    . "Unread")
    (receivers . "Receivers")
    (commented . "Commented")
    (aux-items . "Properties")
    (conf-admin . "Conference admin")
    (server-admin . "Server admin")
    (membership . "Membership")
    (pers-admin . "User admin")
    (autoreply . "Auto-reply")
    (remote-control . "Remote control")
    (language . "Change language")
    (kom-change-global-language . "Display and commands")
    (kom-change-local-language . "Display language only")
    (kom-edit-send . "Send")
    (kom-edit-send-anonymous . "Send anonymously")
    (kom-edit-quit . "Throw away") 
    (kom-ispell-message . "Check spelling")
    (kom-edit-add-recipient . "Add recipient")
    (kom-edit-add-copy . "Add carbon copy")
    (kom-edit-show-commented . "Review commented")
    (kom-edit-insert-commented . "Cite commented")
    (kom-edit-insert-buglist . "Paste commented bug list")
    (kom-edit-add-bcc . "Add blind carbon copy")
    (kom-edit-add-cross-reference . "Add cross reference")
    (kom-edit-add-no-comments . "Request no comments")
    (kom-edit-add-personal-comments . "Request personal replies")
    (kom-edit-add-read-confirm-request . "Request read confirmation")
    (kom-edit-move-text . "Move to new recipient")

    (lp--toggle-membership-selection . "Toggle membership selection")
    (lp--select-region . "Select region")
    (lp--select-priority . "Select priority")
    (lp--deselect-priority . "Deselect priority")
    (lp--deselect-all . "Deselect all")
    (lp--membership-expansion . "Membership expansion")
    (lp--toggle-entry-expansion . "Toggle expansion")
    (lp--expand-entry . "Expand membership")
    (lp--contract-entry . "Contract membership")
    (lp--membership-priority . "Priority")
    (lp--set-priority . "Change priority")
    (lp--move-up . "Move up")
    (lp--move-down . "Move down")
    (lp--increase-priority . "Increase priority")
    (lp--decrease-priority . "Lower priority")
    (lp--yank . "Move selected")
    (lp--filter . "Filter")
    (lp--show-hide-memberships-read-before . "Toggle display of memberships read before...")
    (lp--show-hide-memberships-read-after . "Toggle display of memberships read after...")
    (lp--show-hide-read-memberships . "Toggle display of memberships without unreads")
    (lp--show-hide-passive-memberships . "Toggle display of passive memberships")
    (lp--show-all . "Show all memberships")
    (lp--membership-type . "Membership type")
    (lp--toggle-invitation . "Toggle invitation")
    (lp--toggle-passive . "Toggle passive")
    (lp--toggle-message-flag . "Toggle messages")
    (lp--toggle-secret . "Toggle secret")
    (lp--set-unread . "Only last") 
    (lp--quit . "Quit")
))

(lyskom-language-var local lyskom-onoff-table en
  '(("on" . on) ("off" . off)))

(lyskom-language-var local lyskom-filter-predicate-list en
      '(("=" . nil) ("!=" . t)))

(lyskom-language-var local lyskom-filter-what en
      '((author . "Author")
        (author-no . "Author (number)")
        (author-re . "Author (regexp)")
        (subject . "Subject")
        (subject-re . "Subject (regexp)")
        (recipient . "Recipient")
        (recipient-no . "Recipient (number)")
        (recipient-re . "Recipient (regexp)")
        (text . "Contents")
        (text-re . "Contents (regexp)")))

(lyskom-language-var local lyskom-filter-actions en
      '((skip-text . "Skip")
        (dontshow . "Don't show")
        (read . "Exclude from filter")
        (skip-tree . "Skip comments")
	(obliterate . "Obliterate")))

(defconst lyskom-keybindings-missing nil)

(defvar lyskom-en-mode-map nil)
(lyskom-language-keymap lyskom-mode-map en lyskom-en-mode-map)

(defvar lyskom-en-review-prefix)
(defvar lyskom-en-view-prefix)
(defvar lyskom-en-change-prefix)
(defvar lyskom-en-next-prefix)
(defvar lyskom-en-list-prefix)
(defvar lyskom-en-filter-get-prefix)
(defvar lyskom-en-S-prefix)
(defvar lyskom-en-who-prefix)
(defvar lyskom-en-A-prefix)
(defvar lyskom-en-B-prefix)
(defvar lyskom-en-unread-prefix)

(if lyskom-en-mode-map
    nil
  (setq lyskom-en-mode-map (make-keymap))
  (suppress-keymap lyskom-en-mode-map)
  (define-prefix-command 'lyskom-en-review-prefix)
  (define-prefix-command 'lyskom-en-change-prefix)
  (define-prefix-command 'lyskom-en-next-prefix)
  (define-prefix-command 'lyskom-en-list-prefix)
  (define-prefix-command 'lyskom-en-filter-get-prefix)
  (define-prefix-command 'lyskom-en-S-prefix)
  (define-prefix-command 'lyskom-en-who-prefix)
  (define-prefix-command 'lyskom-en-A-prefix)
  (define-prefix-command 'lyskom-en-B-prefix)
  (define-prefix-command 'lyskom-en-unread-prefix)
  (define-prefix-command 'lyskom-en-view-prefix)

  (define-key lyskom-en-mode-map (kbd "h") 'lyskom-en-change-prefix)
  (define-key lyskom-en-mode-map (kbd "r") 'lyskom-en-review-prefix)
  (define-key lyskom-en-mode-map (kbd "f") 'lyskom-en-filter-get-prefix)
  (define-key lyskom-en-mode-map (kbd "n") 'lyskom-en-next-prefix)
  (define-key lyskom-en-mode-map (kbd "l") 'lyskom-en-list-prefix)
  (define-key lyskom-en-mode-map (kbd "s") 'lyskom-en-S-prefix)
  (define-key lyskom-en-mode-map (kbd "w") 'lyskom-en-who-prefix)
  (define-key lyskom-en-mode-map (kbd "a") 'lyskom-en-A-prefix)
  (define-key lyskom-en-mode-map (kbd "b") 'lyskom-en-B-prefix)
  (define-key lyskom-en-mode-map (kbd "u") 'lyskom-en-unread-prefix)
  (define-key lyskom-en-mode-map (kbd "v") 'lyskom-en-view-prefix)

  (define-key lyskom-en-mode-map (kbd "M-m") 'kom-toggle-mark-as-read-prefix)
  (define-key lyskom-en-mode-map (kbd "M-c") 'kom-toggle-cache-prefix)

  (define-key lyskom-en-mode-map (kbd (lyskom-keys 'button2)) 'kom-button-click)
  (define-key lyskom-en-mode-map (kbd (lyskom-keys 'button3)) 'kom-popup-menu)
  (define-key lyskom-en-mode-map (kbd (lyskom-keys 'button2up)) 'kom-mouse-null)
  (define-key lyskom-en-mode-map (kbd (lyskom-keys 'button3up)) 'kom-mouse-null)
  (define-key lyskom-en-mode-map (kbd "*") 'kom-button-press)
  (define-key lyskom-en-mode-map (kbd "=") 'kom-menu-button-press)
  (define-key lyskom-en-mode-map (kbd "TAB") 'kom-next-link)
  (define-key lyskom-en-mode-map (kbd "M-TAB") 'kom-previous-link)
  (define-key lyskom-en-mode-map (kbd "<S-tab>") 'kom-previous-link)

  ;; These should be first in order to be last in the menu of alternatives.
  (define-key lyskom-en-review-prefix     (kbd "?") 'lyskom-help)
  (define-key lyskom-en-unread-prefix     (kbd "?") 'lyskom-help)
  (define-key lyskom-en-view-prefix       (kbd "?") 'lyskom-help)
  (define-key lyskom-en-change-prefix     (kbd "?") 'lyskom-help)
  (define-key lyskom-en-next-prefix       (kbd "?") 'lyskom-help)
  (define-key lyskom-en-list-prefix       (kbd "?") 'lyskom-help)
  (define-key lyskom-en-filter-get-prefix (kbd "?") 'lyskom-help)
  (define-key lyskom-en-S-prefix          (kbd "?") 'lyskom-help)
  (define-key lyskom-en-who-prefix        (kbd "?") 'lyskom-help)
  (define-key lyskom-en-A-prefix          (kbd "?") 'lyskom-help)
  
  (define-key lyskom-en-mode-map (kbd "o")     'kom-set-unread)
  (define-key lyskom-en-mode-map (kbd "x")     'kom-extended-command)
  (define-key lyskom-en-mode-map (kbd "<SPC>") 'kom-next-command)
  (define-key lyskom-en-mode-map (kbd "<LFD>")  'kom-page-next-command)
  (define-key lyskom-en-mode-map (kbd "<RET>")  'kom-line-next-command)

  (define-key lyskom-en-mode-map (kbd "?")  'kom-help)
  (define-key lyskom-en-mode-map (kbd "e")  'kom-send-letter)
  (define-key lyskom-en-mode-map (kbd "g")  'kom-go-to-conf)
  (define-key lyskom-en-mode-map (kbd "t")  'kom-write-text)
  (define-key lyskom-en-mode-map (kbd "c")  'kom-write-comment)
  (define-key lyskom-en-mode-map (kbd "C")  'kom-comment-previous)
  (define-key lyskom-en-mode-map (kbd "F")  'kom-write-footnote)
  (define-key lyskom-en-mode-map (kbd "p")  'kom-private-answer)
  (define-key lyskom-en-mode-map (kbd "P")  'kom-private-answer-previous)
  (define-key lyskom-en-mode-map (kbd "j")  'kom-jump)
  (define-key lyskom-en-mode-map (kbd "J")  'kom-super-jump)
  (define-key lyskom-en-mode-map (kbd "I")  'kom-who-am-i)
  (define-key lyskom-en-mode-map (kbd "W")  'kom-busy-wait)
  (define-key lyskom-en-mode-map (kbd "m")  'kom-add-self)
  (define-key lyskom-en-mode-map (kbd "M")  'kom-mark-text)
  (define-key lyskom-en-mode-map (kbd "U")  'kom-unmark-text)
  (define-key lyskom-en-mode-map (kbd "q")  'kom-quit)
  (define-key lyskom-en-mode-map (kbd "z")  'kom-bury)
  (define-key lyskom-en-mode-map (kbd "R")  'kom-recover)
  (define-key lyskom-en-mode-map (kbd "T")  'kom-display-time)

  (define-key lyskom-en-list-prefix (kbd "k") 'kom-list-sessions)
  (define-key lyskom-en-list-prefix (kbd "m") 'kom-list-marks)
  (define-key lyskom-en-list-prefix (kbd "c") 'kom-list-conferences)
  (define-key lyskom-en-list-prefix (kbd "n") 'kom-list-news)
  (define-key lyskom-en-list-prefix (kbd "u") 'kom-list-persons)
  (define-key lyskom-en-list-prefix (kbd "r") 'kom-list-re)
  (define-key lyskom-en-list-prefix (kbd "M") 'kom-membership)
  (define-key lyskom-en-list-prefix (kbd "s") 'kom-list-summary)
  (define-key lyskom-en-list-prefix (kbd "f") 'kom-list-filters)
  (define-key lyskom-en-list-prefix (kbd "q") 'kom-list-faqs)
  (define-key lyskom-en-list-prefix (kbd "Q") 'kom-list-server-faqs)

  (define-key lyskom-en-next-prefix (kbd "t") 'kom-view-next-new-text)
  (define-key lyskom-en-next-prefix (kbd "c") 'kom-go-to-next-conf)
  (define-key lyskom-en-next-prefix (kbd "l") 'kom-next-kom)
  (define-key lyskom-en-next-prefix (kbd "u") 'kom-next-unread-kom)

  (define-key lyskom-en-filter-get-prefix (kbd "p") 'kom-get-appreciation)
  (define-key lyskom-en-filter-get-prefix (kbd "k") 'kom-get-abuse)
  (define-key lyskom-en-filter-get-prefix (kbd "s") 'kom-filter-subject)
  (define-key lyskom-en-filter-get-prefix (kbd "a") 'kom-filter-author)
  (define-key lyskom-en-filter-get-prefix (kbd "c") 'kom-filter-text)
  (define-key lyskom-en-filter-get-prefix (kbd "r") 'kom-filter-recipient)

  (define-key lyskom-en-who-prefix (kbd "w") 'kom-who-is-on)
  (define-key lyskom-en-who-prefix (kbd "c") 'kom-who-is-on-in-conference)
  (define-key lyskom-en-who-prefix (kbd "p") 'kom-who-is-present-in-conference)
  (define-key lyskom-en-who-prefix (kbd "f") 'kom-who-is-on-and-friend)

  (define-key lyskom-en-change-prefix (kbd "p") 'kom-change-presentation)
  (define-key lyskom-en-change-prefix (kbd "q") 'kom-change-conf-faq)
  (define-key lyskom-en-change-prefix (kbd "f") 'kom-filter-edit)
  (define-key lyskom-en-change-prefix (kbd "m") 'kom-change-auto-reply)
  (define-key lyskom-en-change-prefix (kbd "t") 'kom-move-text)
  (define-key lyskom-en-change-prefix (kbd "c") 'kom-move-comment)
  (define-key lyskom-en-change-prefix (kbd "T") 'kom-move-text-tree)

  (define-key lyskom-en-unread-prefix (kbd "SPC") 'kom-mark-unread)
  (define-key lyskom-en-unread-prefix (kbd "0") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "1") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "2") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "3") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "4") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "5") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "6") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "7") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "8") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "9") 'kom-initial-digit-unread)
  (define-key lyskom-en-unread-prefix (kbd "c") 'kom-unread-commented-text)
  (define-key lyskom-en-unread-prefix (kbd "C") 'kom-unread-previous-commented-text)
  (define-key lyskom-en-unread-prefix (kbd "a ?") 'lyskom-help)
  (define-key lyskom-en-unread-prefix (kbd "a c") 'kom-unread-comments)
  (define-key lyskom-en-unread-prefix (kbd "a r") 'kom-unread-tree)
  (define-key lyskom-en-unread-prefix (kbd "g") 'kom-unread-last-normally-read)
  (define-key lyskom-en-unread-prefix (kbd "o") 'kom-unread-root)
  (define-key lyskom-en-unread-prefix (kbd "l") 'kom-unread-by-to)
  (define-key lyskom-en-unread-prefix (kbd "L") 'kom-unread-roots-by-to)
  (define-key lyskom-en-unread-prefix (kbd "M") 'kom-unread-more)
  (define-key lyskom-en-unread-prefix (kbd "r") 'kom-unread-root-review)
  (define-key lyskom-en-unread-prefix (kbd "f") 'kom-unread-first)
  (define-key lyskom-en-unread-prefix (kbd "F") 'kom-unread-first-roots)
  (define-key lyskom-en-unread-prefix (kbd "A") 'kom-unread-all-roots)
  (define-key lyskom-en-unread-prefix (kbd "f") 'kom-unread-first)
  (define-key lyskom-en-unread-prefix (kbd "p") 'kom-unread-presentation)
  (define-key lyskom-en-unread-prefix (kbd "q") 'kom-unread-faq)
  (define-key lyskom-en-unread-prefix (kbd "Q") 'kom-unread-server-faq)
  (define-key lyskom-en-unread-prefix (kbd "m") 'kom-unread-marked-texts)
  (define-key lyskom-en-unread-prefix (kbd "a m") 'kom-unread-all-marked-texts)
  (define-key lyskom-en-unread-prefix (kbd "a SPC") 'kom-unread-all)

  (define-key lyskom-en-view-prefix (kbd "u") 'kom-view-url-in-text)

  (define-key lyskom-en-review-prefix (kbd "SPC") 'kom-view)
  (define-key lyskom-en-review-prefix (kbd "0") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "1") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "2") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "3") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "4") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "5") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "6") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "7") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "8") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "9") 'kom-initial-digit-view)
  (define-key lyskom-en-review-prefix (kbd "b") 'kom-review-backward)
  (define-key lyskom-en-review-prefix (kbd "c") 'kom-view-commented-text)
  (define-key lyskom-en-review-prefix (kbd "C") 'kom-view-previous-commented-text)
  (define-key lyskom-en-review-prefix (kbd "a ?") 'lyskom-help)
  (define-key lyskom-en-review-prefix (kbd "a c") 'kom-review-comments)
  (define-key lyskom-en-review-prefix (kbd "a M") 'kom-review-more-comments)
  (define-key lyskom-en-review-prefix (kbd "a r") 'kom-review-tree)
  (define-key lyskom-en-review-prefix (kbd "a x") 'kom-review-cross-references)
  (define-key lyskom-en-review-prefix (kbd "j") 'kom-review-clear)
  (define-key lyskom-en-review-prefix (kbd "n") 'kom-review-next)
  (define-key lyskom-en-review-prefix (kbd "u") 'kom-review-noconversion)
  (define-key lyskom-en-review-prefix (kbd "v") 'kom-review-converted)
  (define-key lyskom-en-review-prefix (kbd "3") 'kom-review-rot13)
  (define-key lyskom-en-review-prefix (kbd "o") 'kom-find-root)
  (define-key lyskom-en-review-prefix (kbd "l") 'kom-review-by-to)
  (define-key lyskom-en-review-prefix (kbd "L") 'kom-review-roots-by-to)
  (define-key lyskom-en-review-prefix (kbd "f") 'kom-review-first)
  (define-key lyskom-en-review-prefix (kbd "F") 'kom-review-first-roots)
  (define-key lyskom-en-review-prefix (kbd "A") 'kom-review-all-roots)
  (define-key lyskom-en-review-prefix (kbd "M") 'kom-review-more)
  (define-key lyskom-en-review-prefix (kbd "g") 'kom-review-last-normally-read)
  (define-key lyskom-en-review-prefix (kbd "q") 'kom-review-faq)
  (define-key lyskom-en-review-prefix (kbd "Q") 'kom-review-server-faq)
  (define-key lyskom-en-review-prefix (kbd "s") 'kom-review-stack)
  (define-key lyskom-en-review-prefix (kbd "p") 'kom-review-presentation)
  (define-key lyskom-en-review-prefix (kbd "r") 'kom-find-root-review)
  (define-key lyskom-en-review-prefix (kbd "m") 'kom-review-marked-texts)
  (define-key lyskom-en-review-prefix (kbd "a m") 'kom-review-all-marked-texts)
  (define-key lyskom-en-review-prefix (kbd "a SPC") 'kom-review-all)
  (define-key lyskom-en-review-prefix (kbd "x a") 'kom-agree)
  (define-key lyskom-en-review-prefix (kbd "x q") 'kom-fast-reply)
  (define-key lyskom-en-review-prefix (kbd "h") 'kom-review-mail-headers)

  (define-key lyskom-en-S-prefix (kbd "c") 'kom-status-conf)
  (define-key lyskom-en-S-prefix (kbd "p") 'kom-status-person)
  (define-key lyskom-en-S-prefix (kbd "s") 'kom-status-session)
  (define-key lyskom-en-S-prefix (kbd "k") 'kom-status-server)
  (define-key lyskom-en-S-prefix (kbd "m") 'kom-send-message)
  (define-key lyskom-en-S-prefix (kbd "u m") 'kom-sub-recipient)
  (define-key lyskom-en-S-prefix (kbd "u k") 'kom-sub-comment)
  (define-key lyskom-en-S-prefix (kbd "u q") 'kom-del-faq)
  (define-key lyskom-en-S-prefix (kbd "u f") 'kom-sub-footnote)
  (define-key lyskom-en-S-prefix (kbd "u ?") 'lyskom-help)

  (define-key lyskom-en-A-prefix (kbd "?") 'lyskom-help)
  (define-key lyskom-en-A-prefix (kbd "m") 'kom-add-recipient)
  (define-key lyskom-en-A-prefix (kbd "e") 'kom-add-copy)
  (define-key lyskom-en-A-prefix (kbd "d") 'kom-add-bcc)
  (define-key lyskom-en-A-prefix (kbd "k") 'kom-add-comment)
  (define-key lyskom-en-A-prefix (kbd "r") 'kom-add-cross-reference)
  (define-key lyskom-en-A-prefix (kbd "q") 'kom-add-faq)
  (define-key lyskom-en-A-prefix (kbd "f") 'kom-add-footnote)
  (define-key lyskom-en-A-prefix (kbd "Q") 'kom-add-server-faq)

  (define-key lyskom-en-B-prefix (kbd "l") 'kom-previous-kom)


  ;; Running in) buffer

  (define-key lyskom-en-mode-map (kbd "M-p") 'kom-backward-text)
  (define-key lyskom-en-mode-map (kbd "M-n") 'kom-forward-text)
  (define-key lyskom-en-mode-map (kbd "C-M-p") 'kom-prev-prompt)
  (define-key lyskom-en-mode-map (kbd "C-M-n") 'kom-next-prompt)
  (define-key lyskom-en-mode-map (kbd "s a") 'kom-save-text)

  (define-key lyskom-en-mode-map (kbd "C-?") 'scroll-down)
  (define-key lyskom-en-mode-map (kbd "<DEL>") 'scroll-down)
  (define-key lyskom-en-mode-map (kbd "<delete>") 'scroll-down)
  (define-key lyskom-en-mode-map (kbd "<BS>") 'scroll-down)

  (define-key lyskom-en-mode-map (kbd "<") 'beginning-of-buffer)
  (define-key lyskom-en-mode-map (kbd ">") 'end-of-buffer)
  )


;;; ================================================================
;;; Prioritization

(defvar lyskom-en-prioritize-mode-map nil "Keymap used in lyskom-prioritize-mode")
(lyskom-language-keymap lyskom-prioritize-mode-map en lyskom-en-prioritize-mode-map)

(if lyskom-en-prioritize-mode-map
    nil
  (setq lyskom-en-prioritize-mode-map (make-keymap))
  (suppress-keymap lyskom-en-prioritize-mode-map)
  (define-key lyskom-en-prioritize-mode-map (kbd "SPC") 'lp--toggle-membership-selection)
  (define-key lyskom-en-prioritize-mode-map (kbd "C-k") 'lp--toggle-membership-selection)
  (define-key lyskom-en-prioritize-mode-map (kbd "p")   'lp--set-priority)
  (define-key lyskom-en-prioritize-mode-map (kbd "C-w") 'lp--select-region)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-w") 'lp--select-region)
  (define-key lyskom-en-prioritize-mode-map (kbd "C-y") 'lp--yank)
  (define-key lyskom-en-prioritize-mode-map (kbd "#")   'lp--select-priority)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-#")   'lp--deselect-priority)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-DEL") 'lp--deselect-all)
  (define-key lyskom-en-prioritize-mode-map (kbd "C-p") 'lp--previous-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd "<up>") 'lp--previous-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd "C-n") 'lp--next-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd "<down>") 'lp--next-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd "<home>") 'lp--first-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd "<end>") 'lp--last-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-<") 'lp--first-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd "M->") 'lp--last-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd "g") 'lp--goto-priority)
  (define-key lyskom-en-prioritize-mode-map (kbd "RET") 'lp--toggle-entry-expansion)
  (define-key lyskom-en-prioritize-mode-map (kbd "+") 'lp--increase-priority)
  (define-key lyskom-en-prioritize-mode-map (kbd "-") 'lp--decrease-priority)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-p") 'lp--move-up)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-n") 'lp--move-down)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-<up>") 'lp--move-up)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-<down>") 'lp--move-down)
  (define-key lyskom-en-prioritize-mode-map (kbd "I") 'lp--toggle-invitation)
  (define-key lyskom-en-prioritize-mode-map (kbd "H") 'lp--toggle-secret)
  (define-key lyskom-en-prioritize-mode-map (kbd "P") 'lp--toggle-passive)
  (define-key lyskom-en-prioritize-mode-map (kbd "M") 'lp--toggle-message-flag)
  (define-key lyskom-en-prioritize-mode-map (kbd "o") 'lp--set-unread)
  (define-key lyskom-en-prioritize-mode-map (kbd "P") 'lp--postpone)
  (define-key lyskom-en-prioritize-mode-map (kbd "C-c C-c") 'lp--quit)
  (define-key lyskom-en-prioritize-mode-map (kbd "q") 'lp--quit)
  (define-key lyskom-en-prioritize-mode-map (kbd "(") 'lp--expand-entry)
  (define-key lyskom-en-prioritize-mode-map (kbd ")") 'lp--contract-entry)

  (define-key lyskom-en-prioritize-mode-map (kbd (lyskom-keys 'button2up)) 'kom-button-click)
  (define-key lyskom-en-prioritize-mode-map (kbd (lyskom-keys 'button2)) 'kom-mouse-null)
  (define-key lyskom-en-prioritize-mode-map (kbd (lyskom-keys 'button3)) 'kom-popup-menu)
  (define-key lyskom-en-prioritize-mode-map (kbd (lyskom-keys 'button3up)) 'kom-mouse-null)
  (define-key lyskom-en-prioritize-mode-map (kbd "*") 'kom-button-press)
  (define-key lyskom-en-prioritize-mode-map (kbd "=") 'kom-menu-button-press)
  (define-key lyskom-en-prioritize-mode-map (kbd "TAB") 'kom-next-link)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-TAB") 'kom-previous-link)
  (define-key lyskom-en-prioritize-mode-map (kbd "C-i") 'kom-next-link)
  (define-key lyskom-en-prioritize-mode-map (kbd "M-C-i") 'kom-previous-link)
  )



;;;==============================================================
;;; Keymap for filter editing
;;;

(defvar lyskom-en-filter-edit-map nil)
(lyskom-language-keymap lyskom-filter-edit-map en lyskom-en-filter-edit-map)

(if lyskom-en-filter-edit-map ()
  (setq lyskom-en-filter-edit-map (make-keymap))
  (suppress-keymap lyskom-en-filter-edit-map)
  (define-prefix-command 'lyskom-en-filter-edit-prefix)
  (define-key lyskom-en-filter-edit-map (kbd "C-c")  'lyskom-en-filter-edit-prefix)
  (define-key lyskom-en-filter-edit-map (kbd "p") 'lyskom-filter-edit-prev-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "P") 'lyskom-filter-edit-prev-entry)
  (define-key lyskom-en-filter-edit-map (kbd "n") 'lyskom-filter-edit-next-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "N") 'lyskom-filter-edit-next-entry)
  (define-key lyskom-en-filter-edit-map (kbd "C-p") 'lyskom-filter-edit-prev-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "C-n") 'lyskom-filter-edit-next-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "C-b") 'lyskom-filter-edit-prev-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "C-f") 'lyskom-filter-edit-next-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "M-p") 'lyskom-filter-edit-prev-entry)
  (define-key lyskom-en-filter-edit-map (kbd "M-n") 'lyskom-filter-edit-next-entry)
  (define-key lyskom-en-filter-edit-map (kbd "d") 'lyskom-filter-edit-delete-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "M-d") 'lyskom-filter-edit-delete-entry)
  (define-key lyskom-en-filter-edit-map (kbd "D") 'lyskom-filter-edit-delete-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "C-d") 'lyskom-filter-edit-delete-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "i") 'lyskom-filter-edit-insert-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "I") 'lyskom-filter-edit-insert-pattern)
  (define-key lyskom-en-filter-edit-map (kbd "M-i") 'lyskom-filter-edit-insert-entry)
  (define-key lyskom-en-filter-edit-map (kbd "<") 'lyskom-filter-edit-beginning-of-list)
  (define-key lyskom-en-filter-edit-map (kbd ">") 'lyskom-filter-edit-end-of-list)
  (define-key lyskom-en-filter-edit-map (kbd "M-<") 'lyskom-filter-edit-beginning-of-list)
  (define-key lyskom-en-filter-edit-map (kbd "M->") 'lyskom-filter-edit-end-of-list)
  (define-key lyskom-en-filter-edit-map (kbd "q") 'lyskom-filter-edit-quit)
  (define-key lyskom-en-filter-edit-map (kbd "C-c C-c")   'lyskom-filter-edit-quit)
  (define-key lyskom-en-filter-edit-map (kbd "x") 'lyskom-filter-edit-expunge)
  (define-key lyskom-en-filter-edit-map (kbd "s") 'lyskom-filter-edit-save)
  (define-key lyskom-en-filter-edit-map (kbd "g") 'lyskom-filter-edit-revert)
  (define-key lyskom-en-filter-edit-map (kbd "t") 'lyskom-filter-edit-toggle-permanent)
  (define-key lyskom-en-filter-edit-map (kbd "a") 'lyskom-filter-edit-toggle-action)
  (define-key lyskom-en-filter-edit-map (kbd "?") 'lyskom-filter-edit-brief-help)
  (define-key lyskom-en-filter-edit-map (kbd "h") 'lyskom-filter-edit-brief-help)
  )



;;;; ============================================================
;;;; Strings and things for the customize mode
;;;;

(defvar lyskom-en-customize-map nil)
(lyskom-language-keymap lyskom-customize-map en lyskom-en-customize-map)


(if lyskom-en-customize-map
    nil
  (setq lyskom-en-customize-map (make-sparse-keymap))
  (define-key lyskom-en-customize-map (kbd "TAB") 'widget-forward)
  (define-key lyskom-en-customize-map (kbd "M-TAB") 'widget-backward)
  (define-key lyskom-en-customize-map (kbd "<S-tab>") 'widget-backward)
  (define-key lyskom-en-customize-map (kbd "C-i") 'widget-forward)
  (define-key lyskom-en-customize-map (kbd "M-C-i") 'widget-backward)
  (define-key lyskom-en-customize-map (kbd "C-m") 'widget-button-press)
  (define-key lyskom-en-customize-map (kbd "C-j") 'widget-button-press)
  (define-key lyskom-en-customize-map (kbd "<RET>") 'widget-button-press)
  (define-key lyskom-en-customize-map (kbd "<LFD>") 'widget-button-press)
  (define-key lyskom-en-customize-map (kbd (lyskom-keys 'button1up)) 'kom-mouse-null)
  (define-key lyskom-en-customize-map (kbd (lyskom-keys 'button2up)) 'kom-mouse-null)
  (define-key lyskom-en-customize-map (kbd (lyskom-keys 'button3up)) 'kom-mouse-null)
  (define-key lyskom-en-customize-map (kbd (lyskom-keys 'button1)) 'widget-button-click)
  (define-key lyskom-en-customize-map (kbd (lyskom-keys 'button2)) 'widget-button-click)
  (define-key lyskom-en-customize-map (kbd (lyskom-keys 'button3)) 'lyskom-widget-click)
  (define-key lyskom-en-customize-map (kbd "C-c C-c") 'lyskom-customize-save-and-quit)
  (define-key lyskom-en-customize-map (kbd "C-c C-k") 'lyskom-customize-quit)
  (define-key lyskom-en-customize-map (kbd "C-c C-s") 'lyskom-customize-save)
  (define-key lyskom-en-customize-map (kbd "C-c C-a") 'lyskom-customize-apply)
  (define-key lyskom-en-customize-map (kbd "?") 'lyskom-customize-help)
)

(lyskom-language-strings local lyskom-custom-strings en
  '(
    ;;
    ;; Widget strings
    ;;

    (which-person . "Specify person: ")
    (which-conf . "Specify a conference: ")
    (which-conf-or-person . "Specify a person or conference: ")
    (which-name . "Specify a name: ")
    (some-person . "Person %#1d")
    (invalid-value . "Invalid value (%#1S)")
    (unknown-command . "Unknown command (%#1s)")
    (ding . "Audio cue")

    ;;
    ;; Help messages
    ;;

    (variable-type-help . "Select to save the setting in the server.")
    (default-help-echo . "Change the value of  %#1s.")
    (change-this-name . "Change the name in this entry.")
    (show-doc . "Show documentation.")
    (hide-doc . "Hide documentation.")
    (select-command . "Select a command.")
    (select-what-to-execute . "\
Select whether to execute command or keyboard macro.")
    (select-url-viewer . "Select a web browser.")
    (select-number . "Select number of times.")
    (select-audio-file . "Select an audio file.")
    (select-priority . "Select a priority.")
    (select-buffer . "Select a buffer name.")
    (select-buffer-size . "Select maximum buffer size.")

    ;;
    ;; Strings that are used in types and so forth
    ;;

    (buffer-name . "%#1s-configure")

    (other-window .     "Some other window             ")
    (other-frame .      "Some other frame              ")
    (new-frame .        "In a new frame                ")
    (lyskom-window .    "The LysKOM buffer's window    ")
    (window-on-buffer . "A window displaying the buffer")

    (on . "On ")
    (off . "Off")
    (yes . "Yes")
    (no . "No ")
    (to-file . "To file:")
    (max-text-length . "For texts shorter than: ")
    (autowrap-timeout . "Timeout in seconds: ")
    (no-timeout . "No timeout")

    (session-lost-all-buffers . "Show notification in all active LysKOM buffers")
    (session-lost-beep . "Beep")

    (friends . "Friends and other special people")

    (turned-off .      "Turned off           ")
    (number-of-times . "A few times")
    (specific-spec . "Per recipient/sender")
    (sound-file . "Audio file")
    (other-persons . "All others")

    (selected-mark . "Mark          ")
    (conf-or-person . "Person or conference")
    (ask .           "Ask every time         ")
    (limited-number . "A limited number")
    (unlimited-number . "Unlimited")

    (before . "Before the text")
    (after . "After the text")
    (depth-first . "In comment order")
    (time-order . "In time order")
    (ancestor . "Read unread commented first")
    (relative . "Read unread relatives first")

    (ar-message-type . "Message type")
    (ar-personal     . "Personal")
    (ar-group        . "Group")
    (ar-alarm        . "Alarm")
    (ar-any-type     . "Any message type")
    (ar-sender       . "Sender")
    (ar-any-sender   . "Any sender")
    (ar-specified-sender . "Specified senders")
    (ar-pers-or-conf . "Person or conference")
    (ar-recipient    . "Recipient")
    (ar-any-recipient . "Any recipient")
    (ar-specified-recipient . "Specified recipients")
    (ar-person       . "Person")
    (ar-message-text . "Message text")
    (ar-any-message  . "Any message")
    (ar-matching-regexp . "Matching regexp")
    (ar-reply        . "Reply")
    (ar-no-reply     . "No reply")
    (ar-reply-text   . "Reply text")

    (express-break . "Immediately upon creation  ")
    (break .         "After current comment chain")
    (no-break .      "After current conference   ")

    (express-server-break . "Immediately")
    (express-server-letters-break . "Immediately if letters arrive")
    (server-break . "After current comment chain")
    (letters-server-break . "After current comment chain if letters arrive")
    (after-conf-server-break . "After current conference")
    (after-conf-letters-server-break . "After current conference if letters arrive")
    (when-done-server-break . "After everything has been read")
    (no-server-break . "Never")

    (command . "Command")
    (command-list . "Command list")
    (some-persons . "For some persons:")
    (name . "Name")

    (page-none . "Never                        ")
    (page-all .  "Before every command         ")
    (page-some . "Before the following commands")

    (ask-every-time . "Ask every time         ")
    (fixed-priority . "Fixed priority")

    (messages-in-lyskom-buffer . "In the LysKOM buffer")
    (discard-messages .          "Nowhere - discard them")
    (in-named-buffer .           "In a named buffer:")

    (automatic-session-name . "Automatic")
    (given-session-name . "Use specified:")

    (simple-unread-mode-line . "Simple")
    (explicit-unread-mode-line . "Show all sessions with unread")

    (everybody-rcpt . "Everybody                                ")
    (group-rcpt .     "The recipient of the last group message  ")
    (sender-rcpt .    "The sender of the most recent message    ")
    (last-recipient-recpt . "The last person I sent a message to")

    (viewer-program . "Web browser")
    (no-viewer            . "(none selected)")
    (default-viewer       . "Browse-URL (all)")
    (netscape-viewer . "Firefox/Mozilla/Netscape or Opera (all)")
    (windows-viewer       . "Windows default or Netscape-like (all)")
    (emacs-w3-viewer . "Emacs W3 mode (HTTP, Gopher, FTP)")
    (emacs-general-viewer . "Emacs (FTP, Telnet, Mail)")
    (emacs-dired-viewer . "Emacs Dired (FTP)")
    (emacs-mail-viewer . "Emacs Mail mode (Mail)")
    (emacs-telnet-viewer . "Emacs Telnet mode (telnet)")
    (mosaic-viewer . "NCSA Mosaic (all)")
    (lynx-viewer          . "Lynx (all)")
    (galeon-viewer        . "Galeon (all)")

    (dont-check . "Don't ask for confirmation")
    (check-before-open . "Confirm before writing the text")
    (check-before-send . "Confirm before sending the text")
    (no-size-limit . "No limit")
    (max-size-in-bytes . "Fixed limit (in bytes)")
    (execute . "Execute")
    (kbd-macro . "Keyboard macro")
    (command . "Command")
    (enter-kbd-macro . "Enter a keybard macro. Finish with %#1s")
    (long-format .  "Show help texts")
    (short-format . "Hide help texts")

    (truncate-threshold-lines . "Max lines")

    (first . "First")
    (last . "Last")
    (specific-placement . "Specific location")
    (priority . "Priority")
    (same-as-conf . "Same as current conference")
    (custom-format . "Custom format:")
    (default-format . "Standard format")
    (a-string . "A string:")
    (some-string . "A random string")
    (unspecified . "Unspecified")

    (symbolic-mark-association . "Association")
    (symbolic-mark-name . "Symbolic name: ")
    (mark-type-to-assoc . "Mark type to associate with: ")

    (prompt-for-text-no . "Prompt for text number")

    (all-others . "All others")
    (some-person . "Person")
    (format-html . "Format HTML:")

    (url-transform-regexp . "From (regexp):")
    (url-transform-newtext . " To:")

    (default-namedays . "Namedays for current language")
    (specific-namedays . "Specific name list")
    (several-name-lists . "Several name lists")

    (link-pattern-for . "Link patterns for")
    (all-conferences . "All recipients")
    (link-specific-rcpt . "Specific recipient")
    (link-pattern . "Link pattern")
    (link-replace . "Link substitution")
    (link-highlight-match . "Highlight group")
    (link-fold-case . "Case insensitive")

    (generated-face . "Automatic")
    (predefined-fonts . "Fonts intended for specific uses\n")
    (other-fonts . "Fonts with no specific uses\n")
    (other-face . "Other")

    (ext-simple-command . "Simple command")
    (ext-complex-command . "Command with arguments")
    (ext-complex-component . "Command/argument")

    (lynx-xterm . "Lynx in xterm")
    (lynx-emacs . "Lynx in emacs terminal-mode")

    (morons . "Morons and other \"special\" people")
    (friends . "Friends and other special people")
    (me . "Me, myself and I")
    (i-am-supervisor . "Conferences i supervise")
    (has-no-presentation . "Conferences without presentations")
    (highlight-conferences . "Conferences:")
    (highlight-face . "Face:")
    (highlight-conflist . "Conference list")

    (yes-dont-pop . "Yes, but don't uniconify frames")
    (language . "Language")

    (specific-extended-info . "Only selected information")
    (extended-info-show . "Show:")
    (extended-info-type . "Information type:")

    (extended-server-info . "All extended server information")
    (extended-conf-info . "All extended conference information")
    (extended-pers-info . "All extended person information")
    (extended-read-faq-info . "Read FAQs")
    (extended-statistics-info . "Full server statistics")
    (extended-all-other-info . "Other information")

    (show-presence . "Show presence messages")
    (conflist . "Conference/person list")
    (pers-list-1 . "Conference/person list 1")
    (pers-list-2 . "Conference/person list 2")
    (pers-list-3 . "Conference/person list 3")
    (pers-list-4 . "Conference/person list 4")
    (other-list . "Other list")
    (always-show-presence . "Show all presence messages")
    (never-show-presence . "Show no presence messages")
    (obsolete-setting . "Obsolete setting")
    (detailed-setting . "Detailed setting")

    ;;
    ;; Misc doc strings
    ;;

    (lyskom . "Configuration of LysKOM")
    (lyskom-doc . "\
These commands work outside editable text fields.
\\[lyskom-customize-save-and-quit] to save and quit,
\\[lyskom-customize-apply] to apply without saving or quitting,
\\[lyskom-customize-save] to save without quitting,
\\[lyskom-customize-quit] to quit without saving.
\\[widget-forward] moves to the next setting
\\[widget-button-press] changes the value
Press C-h m for a complete list of commands.

Documentation:  [?] Show documentation    [!] Hide documentation
Lists etc.   :  [INS] Add a line   [DEL] Remove a line   [*] Modify

If the box before the name of the setting is selected, the setting will
be saved in the server. Otherwise it will be saved in your .emacs.

Click prompts with the middle mouse button to change values and bring 
up menus.")




    (section . "------------------------------------------------------------------------------\n")
    (look-and-feel-misc . "Look and feel\n")
    (window-locations   . "Windows\n")
    (windows-where      . "How are windows created:\n")
    (reading .            "Reading\n")
    (writing .            "Writing\n")
    (urls .               "URL management\n")
    (personal-messages .  "Personal messages\n")
    (remote-control .     "Remote control of LysKOM\n")
    (hooks .              "Hook functions\n")

    (audio-cues .         "Audio cues\n")
    (audio-cues-when    . "Issue audio cues when:\n")
    (automatic-replies .  "Automatic replies\n")

    (new-window-netscape . "Open URLs in a new window")
    (new-tab-netscape . "Open URLs in a new tab")
    (default-netscape . "Open URLs wherever")

    (audio-cues-doc . "\
  The following group of settings controls how LysKOM issues audio cues in
  various situations. The following options are available for each setting:

    Off
        No audio cue is issued

    A Few Times
        Emacs will beep one or more times. You have to specify how many
        times Emacs is to beep.

    Audio File
        Emacs will attempt to play the specified audio file. The program
        used to play the file is specified by another setting.")
    

    (sending-doc . "\
  The following settings turn on or off certain checks that can be performed
  before sending a text to the server. The checks are designed to keep
  you from doing something stupid.

    Confirm multiple recipients
        If a text or comment has more than one recipient, LysKOM 
        can ask which of the recipients are relevant. This can either
        be done before you start writing the text, in which case 
        LysKOM will post a question for each recipient, or before sending
        the text to the server, in which case you may confirm all the
        recipients at once. It is also possible to turn this check off
        entirely.

    Check membership of commented author
        When on, LysKOM will check that the author of the comment you are
        writing is a member of at least one of the recipients of your comment.
        If not, LysKOM will offer to add the commented author as a recipient
        to the comment you are writing.

    Authors not to check
        A list of authors not to check even if check membership of
        commented author is on.

    Check for unread comments
        When this is on, LysKOM will check that you have read all the other
        comments to the text you are commenting before sending your
        comment to the server. This is supposed to help you avoid duplicating
        someone else's comment.")

    (windows-doc . "\
  The following settings control how windows are created in LysKOM. The
  available options are:


    Some other window
        In another window, but in the same frame as LysKOM. If there only
        is one window in the frame, a new window will be created (and will
        be removed when you are finished).

    Some other frame
        In a different frame than the one LysKOM is in. If there only is one
        frame, a new one will be created (and will be removed when you are
        finished).

    In a new frame
        A new frame is created (and will be removed when you are finished).

    The LysKOM buffer's window
        The LysKOM buffer's window will be used. LysKOM will be restored to
        the window when you're finished.

    A window displaying the buffer
        If there is a window anywhere displaying the named buffer, that 
        window will be used. This might be useful if you always have a
        particular buffer showing, but don't need it when executing some
        command.")


    ;;
    ;; Doc strings for variables
    ;;

    (kom-ansaphone-replies-doc . "\
  This setting controls automatic replies in detail. You can set replies
  for specific senders, recipients and even message contents. If the message
  being replied to does not match any element in this list, the default
  auto reply message above is sent instead.

  For each pattern in the list you can set the required message type, 
  sender the message must have, recipient the message must have, a regular
  expression the message must match and the reply to send.

  It is advisable to configure the client not to send a message to any
  group or alarm message.")

    (kom-bury-buffers-doc . "\
  Controls how the LysKOM buffer is handled when moving to another KOM buffer
  with the Next and Previous LysKOM commands. When turned on the current
  buffer is buried.")

    (kom-personal-messages-in-window-doc . "\
  Controls which window is used to display the buffer with personal messages.
  For this setting to have any effect, personal messages must be displayed
  in a separate buffer, and that buffer must be displayed automatically
  every time a message is received.")

    (kom-write-texts-in-window-doc . "\
  Controls which window is used to write new texts.")

    (kom-prioritize-in-window-doc . "\
  Controls which window is used to prioritize conferences.")

    (kom-edit-filters-in-window-doc . "\
  Controls which window is used for editing filters.")

    (kom-customize-in-window-doc . "\
  Controls which window is used to configure LysKOM.")

    (kom-view-commented-in-window-doc . "\
  Controls which window is used to show the commented text when commenting.")

    (kom-list-membership-in-window-doc . "\
  Controls in which window your membership list is shown.")


    (kom-user-prompt-format-doc . "\
  The format of the LysKOM prompt. Certain control sequences cause special
  text to be inserted:

    %c - Inserts the current default command.
    %[ - Inserts `[' if the ansaphone is on.
    %] - Inserts `]' is the ansaphone is on.
    %m - Inserts information about recorded messages.
    %s - Inserts the name of the LysKOM system
    %S - Inserts the server name.
    %p - Inserts the name of the user currently logged on.
    %w - Inserts the name of the current conference.
    %a - Inserts `anonymous'
    %A - Inserts `Anonymous'
    %# - Inserts the current session number.
    %  - Inserts a space if it seems necessary.
    %% - Inserts a percent sign.

  Here are a few examples:

    \"%[%c% %m%] - \"             The default prompt
    \"%[%s: %c% %m%] - \"         Could display \"LysKOM: Time - \"")

    (kom-user-prompt-format-executing-doc . "\
  The format of the LysKOM prompt when the default command is executing.
  Certain control sequences cause special text to be inserted:

    %c - Inserts the current default command.
    %[ - Inserts `[' if the ansaphone is on.
    %] - Inserts `]' is the ansaphone is on.
    %m - Inserts information about recorded messages.
    %s - Inserts the name of the LysKOM system
    %S - Inserts the server name.
    %p - Inserts the name of the user currently logged on.
    %w - Inserts the name of the current conference.
    %a - Inserts `anonymous'
    %A - Inserts `Anonymous'
    %# - Inserts the current session number.
    %  - Inserts a space if it seems necessary.
    %% - Inserts a percent sign.

  Here are a few examples:

    \"%[%c% %m%] - \"             The default prompt
    \"%[%s: %c% %m%] - \"         Could display \"LysKOM: Time - \"")

    (kom-anonymous-prompt-format-doc . "\
  The format of the LysKOM prompt used when anonymous. Certain control
  sequences cause special text to be inserted:

    %c - Inserts the current default command.
    %[ - Inserts `[' if the ansaphone is on.
    %] - Inserts `]' is the ansaphone is on.
    %m - Inserts information about recorded messages.
    %s - Inserts the name of the LysKOM system
    %S - Inserts the server name.
    %p - Inserts the name of the user currently logged on.
    %w - Inserts the name of the current conference.
    %a - Inserts `anonymous'
    %A - Inserts `Anonymous'
    %# - Inserts the current session number.
    %  - Inserts a space if it seems necessary.
    %% - Inserts a percent sign.

  Here are a few examples:

    \"%[%c% %m%] - \"             The default prompt
    \"%[%s: %c% %m%] - \"         Could display \"LysKOM: Time - \"")

    (kom-anonymous-prompt-format-executing-doc . "\
  The format of the LysKOM prompt when the default command is executing
  and the session is anonymous. Certain control sequences cause special 
  text to be inserted:

    %c - Inserts the current default command.
    %[ - Inserts `[' if the ansaphone is on.
    %] - Inserts `]' is the ansaphone is on.
    %m - Inserts information about recorded messages.
    %s - Inserts the name of the LysKOM system
    %S - Inserts the server name.
    %p - Inserts the name of the user currently logged on.
    %w - Inserts the name of the current conference.
    %a - Inserts `anonymous'
    %A - Inserts `Anonymous'
    %# - Inserts the current session number.
    %  - Inserts a space if it seems necessary.
    %% - Inserts a percent sign.

  Here are a few examples:

    \"%[%c% %m%] - \"             The default prompt
    \"%[%s: %c% %m%] - \"         Could display \"LysKOM: Time - \"")

    (kom-enabled-prompt-format-doc . "\
  The format of the LysKOM prompt when in administrator mode. Certain
  control sequences cause special text to be inserted:

    %c - Inserts the current default command.
    %[ - Inserts `[' if the ansaphone is on.
    %] - Inserts `]' is the ansaphone is on.
    %m - Inserts information about recorded messages.
    %s - Inserts the name of the LysKOM system
    %S - Inserts the server name.
    %p - Inserts the name of the user currently logged on.
    %w - Inserts the name of the current conference.
    %a - Inserts `anonymous'
    %A - Inserts `Anonymous'
    %# - Inserts the current session number.
    %  - Inserts a space if it seems necessary.
    %% - Inserts a percent sign.

  Here are a few examples:

    \"%[%c% %m%] - \"             The default prompt
    \"%[%s: %c% %m%] - \"         Could display \"LysKOM: Time - \"")

    (kom-enabled-prompt-format-executing-doc . "\
  The format of the LysKOM prompt when the default command is executing
  and the session is in administrator mode. Certain control sequences 
  cause special text to be inserted:

    %c - Inserts the current default command.
    %[ - Inserts `[' if the ansaphone is on.
    %] - Inserts `]' is the ansaphone is on.
    %m - Inserts information about recorded messages.
    %s - Inserts the name of the LysKOM system
    %S - Inserts the server name.
    %p - Inserts the name of the user currently logged on.
    %w - Inserts the name of the current conference.
    %a - Inserts `anonymous'
    %A - Inserts `Anonymous'
    %# - Inserts the current session number.
    %  - Inserts a space if it seems necessary.
    %% - Inserts a percent sign.

  Here are a few examples:

    \"%[%c% %m%] - \"             The default prompt
    \"%[%s: %c% %m%] - \"         Could display \"LysKOM: Time - \"")
    
    (kom-cite-string-doc . "\
  A string that is inserted before each line in a cited text. Normally this
  is set to something like \"> \". Note that it is not customary to cite
  commented texts the way it is done in e-mail or Usenet News since the
  commented text is always available in LysKOM.")


    (kom-created-texts-are-read-doc . "\
  When this is on, self-created texts are automatically marked as read. Turned
  off, those texts are presented as any other texts.")

    (kom-default-mark-doc . "\
  The default mark type used for marking texts. A mark type is an integer
  between 0 and 255 (inclusive). If no default mark type is selected, LysKOM
  asks for a mark type every time a text is to be marked. This can be used
  to separate different types of marks. You can, for example, mark texts
  which contain valuable information with a certain integer and texts which
  you should remember to comment some time with another integer.")

    (kom-symbolic-marks-alist-doc . "\
  To avoid having to memorize what the integer mark types are supposed to
  mean, you can define a list of symbolic mark types. A symbolic mark type
  is a descriptive string that is associated with an integer mark type.")

    (kom-reading-puts-comments-in-pointers-last-doc . "\
  Controls if comment links are listed before or after the body of a text.
  Normally comment links are listed after the text.

  Before:

    398331 1996-09-24  13:22  /2 lines/ George Berkeley
    Recipient: Philosophy <1226>
    Comment in text 398374 by John Locke
    Subject: 
    ------------------------------------------------------------
    An abstract idea is a contradiction in terms.
    (398331) -----------------------------------

  After:

    398331 1996-09-24  13:22  /2 lines/ George Berkeley
    Recipient: Philosophy <1226>
    Subject: 
    ------------------------------------------------------------
    An abstract idea is a contradiction in terms.
    (398331) -----------------------------------
    Comment in text 398374 by John Locke
")

    (kom-dashed-lines-doc . "\
  When this setting is turned on, dashed lines are displayed before and after
  the text body. When turned off, blank lines are used instead.

  On:

    892343 1996-09-24  19:21  /2 lines/ Tycho Brahe
    Recipien: Presentation (of new) Members
    Subject: Tycho Brahe
    ------------------------------------------------------------
    Astronomer and discoverer of stars resident on the island of Ven.
    (892343) -----------------------------------

  Off:

    892343 1996-09-24  19:21  /2 lines/ Tycho Brahe
    Recipien: Presentation (of new) Members
    Subject: Tycho Brahe

    Astronomer and discoverer of stars resident on the island of Ven.
    (892343)

  Most people have this turned on.")

    (kom-autowrap-doc . "\
  With this setting turned on, LysKOM will attempt to fill any
  paragraphs containing lines that too long. Paragraphs that appear to
  have been preformatted by the author are exempted from this treatment.
  If set to a number, only texts shorter than this number (in
  characters) will be filled. This may be useful to avoid filling very
  long texts.")

    (kom-autowrap-timeout-doc . "\
  This setting controls the maximum number of seconds the client may
  spend attempting to fill paragraphs of a text. This is useful to
  avoid long waits while the client is breaking lines in a very long
  text. The actual time spent filling paragraphs may be several
  seconds longer than this value. Unfortunately there is no way to
  avoid this. Furthermore, setting a limit slows down all text display
  slightly. When this is off, it is still possible to cancel paragraph
  filling by typing C-g.")

    (kom-show-author-at-end-doc . "\
  When this is turned on, the name of the author will be shown at the end
  of the text. The name is also shown before the text as usual.

  On (with dashed lines on):

    892342 1996-09-24  19:21  /2 lines/ Claude Shannon
    Mottagare: Presentation (of new) Members
    Ärende: Claude Shannon
    ------------------------------------------------------------
    Information theoretician
    (892342) /Claude Shannon/------------------------------

  Off:

    892342 1996-09-24  19:21  /2 lines/ Claude Shannon
    Recipient: Presentation (of new) Members
    Subject: Claude Shannon
    ------------------------------------------------------------
    Information theoretician
    (892342) -----------------------------------

  If dashed lines are off, the author's name will be shown as in this
  example, but the dashed lines are naturally not displayed.")

    (kom-truncate-threshold-doc . "\
  If this is activated, texts longer than the maximum number of lines will
  be truncated when you review comments, review marked texts or review the
  first or last texts in a conference.  Texts will not be truncated when
  you review a text, review a tree or read the next text.")

    (kom-truncate-show-lines-doc . "\
  The number of lines to show of truncated texts.")

    (kom-print-number-of-unread-on-entrance-doc . "\
  Determines whether the number of unread texts is shown when entering
  a conference:

  On:

    Go to next conference...
    Presentation (of new) Members - 3 unread
    Read next text - 

  Off:

    Go to next conference...
    Presentation (of new) Members
    Read next text - ")



    (kom-presence-messages-in-echo-area-doc . "\
  If you want messages in the echo area when somebody logs in, logs out or
  changes name, turn this setting on. Messages are shown in the minibuffer.
  If you want messages about a limited number of users, specify which ones.
  To not get any messages at all, just specify an empty list.")

    (kom-presence-messages-in-buffer-doc . "\
  If you want messages in the LysKOM buffer when somebody logs in, logs out
  or changes name, turn this setting on. Messages are shown in the LysKOM
  buffer. If you want messages about a limited number of users, specify which 
  ones. To not get any messages at all, just specify an empty list.")

    (kom-show-where-and-what-doc . "\
  If this is on, the list of active users will include the machine and user
  they are logged in from (if known) and what they are currently doing.

  On:

        User                                   Is in conference             
        At                                     Activity                        
    --------------------------------------------------------------------------
     6810 George Berkeley                      Philosophy                     
          berkeley@emp1.tcd.ie                 (Writing a comment.)           
     7571 John Locke                           Philosophy                     
          eridy@cc.ox.ac.uk                    (Waiting.)                     

  Off:

        User                                   Is in conference             
    --------------------------------------------------------------------------
     6810 George Berkeley                      Philosophy                     
     7571 John Locke                           Philosophy")

    (kom-show-since-and-when-doc . "\
  If this is on, the list of active users will include the connection time
  and the inactivity period for each user.")

    (kom-remember-password-doc . "\
  When this setting is active the password used to login to LysKOM will
  be saved in a local variable in the session buffer. This makes it 
  possible to relogin automatically if the session is lost. Note that the
  password is saved in clear text so it can be read by anyone who gains 
  access to your LysKOM session buffer (even disconnected).")

    (kom-idle-hide-doc . "\
  The listing of active users normally only shows those users who have been
  active recently. This setting determines how many minutes a user may be
  inactive without being excluded from the list of active users.")


    (kom-show-footnotes-immediately-doc . "\
  Footnotes can be shown either as comments or immediately when the text
  they are footnotes to is displayed. This setting controls which behavior
  is used.")


    (kom-follow-comments-outside-membership-doc . "\
  LysKOM will normally not follow chains of comments into conferences you are
  not a member of. If you do want to follow comment chains into other
  conferences, turn this setting on.")


    (kom-read-depth-first-doc . "\
  LysKOM can display texts either in the order they were written or in
  the order defined by the comment tree. For example, if texts 1003 and
  1004 are comments to text 1002, text 1006 is a comment to 1003 and 
  texts 1005 and 1007 are comments to 1004, the comment tree looks 
  something like this:
        
  1002 +-- 1003 --- 1006
       |
       +-- 1004 +-- 1005
                |
                +-- 1007

  Reading in order of creation will cause the texts to be displayed
  in numerical order: 1002, 1003, 1004, 1005, 1006 and finally 1007.
  Reading in comment order will give the order 1002, 1003, 1006, 1004,
  1005 and finally 1007.")

    (kom-read-related-first-doc . "\
  When LysKOM has decided what text to read next (using the algorithm
  specified by the kom-read-depth-first variable) it can optionally
  refine the choice.  This variable can have the following values:

    nil (Off)
        Do no more processing.

    'oldest-ancestor (Read unread commented first)
        If the text is a comment or footnote to a parent text, the
        user is a member of at least one recipient of the text, and
        the text is unread, it becomes a candidate to be used
        instead.  Repeat the process as long as any candidate has a
        parent that fulfills the requirements.  Use the
        lowest-numbered text.  This setting means that if a comment
        tree started in another conference than the one you are
        reading, you will see the entire unread part of the comment
        tree in order.

    'oldest-relative (Read unread relatives first)
        This is similar to 'oldest-ancestor, but also considers all
        children of all candidates, whether they are read or not.
        Only unread texts will be selected, but even read texts will
        be considered to ensure that the order you read texts in are
        as close to the depth-first order as possible.  The drawback
        of this method is that LysKOM may have to consider hundreds of
        texts, which may slow down the client.")


    (kom-continuous-scrolling-doc . "\
  Turned on means that LysKOM will scroll the buffer while new text is 
  being inserted, not just at the end of a command. This works well with
  faster terminals, but may be worth turning off if the terminal is so slow
  that scrolling Emacs buffers takes a long time.")


    (kom-deferred-printing-doc . "\
  In order to improve speed, LysKOM will not print certain things, such as
  the names of users and conferences immediately, but will delay printing
  to make time for other tasks. This improves response time in the client
  considerably, and should only be turned off if it causes problems.")


    (kom-higher-priority-breaks-doc . "\
  When texts are created in conferences that have a higher priority than
  the one currently being read, LysKOM will attempt to break the normal
  reading order to show these. This setting controls whether the reading
  order is broken immediately, after the current comment chain is read or
  when everything in the current conference has been read.")


    (kom-login-hook-doc . "\
  This hook lists commands to be run when logging in, before any input is
  accepted from the keyboard.")


    (kom-do-when-done-doc . "\
  This hook lists commands and keyboard macros that are to be executed when
  all texts have been read.")


    (kom-page-before-command-doc . "\
  The LysKOM buffer can be cleared before all commands, so text that is 
  inserted always appears at the top of the buffer's window. This variable
  controls before which commands the buffer is to be scrolled.")


    (kom-permissive-completion-doc . "\
  When this is on, TAB will only complete to the names of users that are 
  logged on when the command being invoked is only applicable to people
  that are logged on. When off, TAB will complete to names of everyone.")


    (kom-membership-default-priority-doc . "\
  This specifies how the initial priority of a conference is set when
  you first become a member. If it is a number between 1 and 255, that is
  the priority assigned. If it is something else, LysKOM will ask for a 
  priority every time you become a member of a conference.")

    (kom-membership-default-message-flag-doc . "\
  This specifies if group messages are received for conferences you join.
  `Yes' means group messages are received, `No' that they aren't and 
  `Ask every time' that the client will ask every time you become a member
  of a conference.")

    (kom-show-personal-messages-in-buffer-doc . "\
  This setting specifies how personal, group and public messages are shown.
  The messages can be displayed in the LysKOM buffer, simply thrown away or
  be shown in a named buffer.")

    (kom-pop-personal-messages-doc . "\
  If messages are shown in a named buffer and this setting is also on, then
  LysKOM will display that buffer whenever a message arrives.")

    (kom-audio-player-doc . "\
  If you want LysKOM to play audio files instead of simply beeping, this
  setting must specify the name of a program that can play the audio files.
  The program must take a single argument, the name of the file to play.")

    (kom-default-message-recipient-doc . "\
  This setting controls who will be the default recipient of messages. The
  default recipient may either be everyone, i.e. a public message; the
  sender of the most recently received message; the recipient of the most
  recently received group message; or the sender of the most recently
  received personal message.")

    (kom-filter-outgoing-messages-doc . "\
  If this is on, messages that are sent automatically, such as automatic
  replies and replies to remote control commands, will be shown as if you
  had sent them manually.")

    (kom-friends-doc . "\
  The users named in this list will be displayed using a special face in
  the LysKOM buffer. These users are also listed with the command Which 
  friends (and may be used in other similar commands).")

    (kom-morons-doc . "\
  The users named in this list will be displayed using a warning face in
  the LysKOM buffer.")

    (kom-person-list-1-doc . "\
  This list of users has no predefined meaning. It can be used to show
  the users with a special font (see kom-highlight-conferences) or to
  configure presence messages (see kom-presence-messages-in-buffer) or
  for whatever purpose you like.")

    (kom-person-list-2-doc . "\
  This list of users has no predefined meaning. It can be used to show
  the users with a special font (see kom-highlight-conferences) or to
  configure presence messages (see kom-presence-messages-in-buffer) or
  for whatever purpose you like.")

    (kom-person-list-3-doc . "\
  This list of users has no predefined meaning. It can be used to show
  the users with a special font (see kom-highlight-conferences) or to
  configure presence messages (see kom-presence-messages-in-buffer) or
  for whatever purpose you like.")

    (kom-person-list-4-doc . "\
  This list of users has no predefined meaning. It can be used to show
  the users with a special font (see kom-highlight-conferences) or to
  configure presence messages (see kom-presence-messages-in-buffer) or
  for whatever purpose you like.")

    (kom-url-viewer-preferences-doc . "\
  This setting controls which WWW browser that will be used to open URLs
  found in LysKOM. If the first browser in the list cannot handle the type
  of URL being opened, then the next browser is tried, and so on.")

    (kom-windows-browser-command-doc . "\
  This setting specifies the command to use to start a web browser
  under Windows. If empty, a couple of commands that are likely to
  work on Windows will be tried.")

    (kom-mosaic-command-doc . "\
  This setting specifies the command to use to start NCSA Mosaic.")

    (kom-netscape-command-doc . "\
  This setting specifies the command to use to start Firefox, Mozilla,
  Netscape or Opera.")

    (kom-galeon-command-doc . "\
  This setting specifies the command to use to start Galeon.")

    (kom-inhibit-typeahead-doc . "\
  Key presses are usually buffered while LysKOM is busy, and are executed
  as soon as possible. With this setting off, LysKOM discards any key presses
  received while the client was busy.")

    (kom-max-buffer-size-doc . "\
  It is possible to limit the size of the LysKOM buffer by specifying a
  maximum number of characters in this setting. When the buffer grows
  beyond this limit, text from the beginning of the buffer is removed.")

    (kom-ansaphone-record-messages-doc . "\
  LysKOM can record messages that arrive when the autoreply feature is on.
  This setting controls whether messages are recorded or not.")

    (kom-ansaphone-show-messages-doc . "\
  When this setting is on, LysKOM will display incoming messages even if
  the autoreply feature is turned on.")

    (kom-ansaphone-default-reply-doc . "\
  This is the message sent by the autoreply feature unless a different
  message has been specified using some other means (and other means are
  only for careful experts).")

    (kom-remote-control-doc . "\
  When turned on, it is possible to control the session using remote control
  commands. Only those users listed below may issue the commands.")

    (kom-remote-controllers-doc . "\
  The users listed here are permitted to issue remove control commands
  to your LysKOM session.")

    (kom-self-control-doc . "\
  When this is on, the user who is logged on may issue remote control
  commands. This is an alternative to adding yourself to the list of 
  permitted controllers.")

    (kom-customize-format-doc . "\
  The documentation for the various settings can be visible or hidden when
  you open the settings buffer. No matter if it starts hidden or visible, the
  documentation for individual settings can be shown and hidden by using the
  question mark/exclamation mark to the right of the setting.")

    (kom-default-language-doc . "\
  Default language to use in LysKOM. If you change this setting the new
  language will not be applied to the current setting. Use the Change
  language command to do that. The first langauge in the list will be used
  in all clients that support this function.")

    (kom-ispell-dictionary-doc . "\
  This specifies the dictionary ispell is to use for spell checking. If set
  to ispell-dictionary, then the variable ispell-dictionary will be used
  instead.")

    (kom-show-namedays-doc . "\
  When this is on, display the name of the day. You can choose to display
  names from a list appropriate to the currently selected language. You
  can also choose a specific list of names.")

    (kom-show-week-number-doc . "\
  The Time command shows week numbers if this is on.")

    (kom-relogin-behaviour-doc . "\
  This controls if the client will try to reattach when a command is given 
  in a disconnected session. On means the client tries to reattach
  automatically, Off that it doesn't even ask.")

    (kom-relogin-inhibit-commands-doc . "\
  Commands for which reattachment is disabled. These commands will never
  cause the client to reattach.")

    (kom-membership-default-placement-doc . "\
  This controls where new memberships are placed. First means before
  all existing memberships of the same priority. Last means after all
  existing memberships of the same priority. A number indicates a
  fixed placement (although the client will eventually sort the membership
  list in order of priority).")

    (kom-show-imported-importer-doc . "\
  The importer of an imported e-mail message is shown if this is on.
  This only works with compliant e-mail importers.")

    (kom-show-imported-envelope-sender-doc . "\
  The actual sender (the envelope sender) of imported e-mails is shown 
  if this is on. This only works with compliant e-mail importers.")

    (kom-show-imported-external-recipients-doc . "\
  Show external recipients and CC recipients of imported e-mails if this
  is on. This only works with compilant e-mail importers.")

    (kom-show-imported-message-id-doc . "\
  Show Message-Id of imported e-mails if this is on. This only works with 
  compilant e-mail importers.")

    (kom-agree-text-doc . "\
  Default text for the Agree command. It can either be a text or a list
  of texts. If it is a list, one of the texts will be chosen at random.")

    (kom-silent-ansaphone-doc . "\
  If this is off, the client will not beep when a personal, group or
  public message is received when the auto reply feature is on.")

    (kom-default-session-priority-doc . "\
  The session priority of new sessions. Conferences with a lower priority
  will not be read.")

    (kom-unsubscribe-makes-passive-doc . "\
  When this is on, the first time you leave a conference you become a
  passive member. Leaving the conference again unsubscribes you completely.
  When this is off, leaving a conference unsubscribes you immediately.")

    (kom-review-priority-doc . "\
  Priority for review commands. This setting can be used to give review
  commands a higher priority than normal. The default priority is the
  same as the conference currently being read. Set this to 256 or higher
  if you want review commands to take precedence over all conferences.")

    (kom-show-creating-software-doc . "\
  Show the name of the client used to create a text if the information
  is present and this setting is on.")

    (kom-text-footer-format-doc . "\
  Format for the text footer. In the format string, %n is replaced by the
  text number, %P with the author's name, %p with the author's number, %f
  with information about the text (HTML, filled, etc), and %- with a
  suitable dashed line. A number after the percent sign is the minimum
  number of characters to use. A minus sign before the number causes text
  to be left justified within the field. An equals sign causes text
  longer than the indicated width to be truncated.")
 
    (kom-long-lines-doc . "\
  When this is on, most of the dashed lines are made longer than normal.")

    (kom-postpone-default-doc . "\
  Number of texts to postpone with the Postpone reading command.")

    (kom-allow-incompleteness-doc . "\
  When this is on, the client will not wait for information about all
  unread texts, even if it is needed. The result is that List news and
  some other commands do not yield accurate results shortly after logging
  in. When this is off, the client will wait for information on all
  unread texts when it is needed.")

    (kom-smileys-doc . "\
  When this is on, Emacs supports it, and the appropriate package 
  (smiley.el, which is part of Gnus) is installed, smileys will be
  shown graphically.")
  
    (kom-ignore-message-senders-doc . "\
  Don't show personal, group or alarm messages from these senders.")

    (kom-ignore-message-recipients-doc . "\
  Don't show group messages to these recipients.")

    (kom-text-footer-dash-length-doc . "\
  The total length of the text footer when long dashed lines are not
  in effect and no custom format is being used.")

    (kom-text-header-dash-length-doc . "\
  The total length of the text header when long dashed lines are not
  in effect.")

    (kom-show-personal-message-date-doc . "\
  When this is on, the date and time is shown on all personal, group and
  alarm messages.")

    (kom-w3-simplify-body-doc . "\
  When this is on, the client will ignore colors set in the HTML body when
  displaying formatted HTML.")

    (kom-mercial-doc . "\
  This text is shown in the list of users when you have finished reading
  everything.")

    (kom-session-nickname-doc . "\
  How the session name is determined. This is the name displayed in the 
  mode line and by List sessions.")

    (kom-server-priority-doc . "\
  Priority of this LysKOM session. The client can prompt you to go to
  a session with unread texts when a text arrives in a session with a
  higher priority than the one currently being read.")

    (kom-server-priority-breaks-doc . "\
   This setting controls when a prompt to go to the next LysKOM session
   with unread texts is presented.

   Immediately                  Present a prompt if any text arrives in
                                a session with a higher priority.

   Immediately if letters arrive    Present a prompt is a letter arrives
                                in a session with a higher priority.

   After current comment chain  Present a prompt after the current chain
                                of comments has been read if a text
                                arrives in a session with a higher 
                                priority.

   After current comment chain if letters arrive    Present a prompt
                                after the current chain of comments has
                                been read if a letter arrives in a session
                                with a higher priority.

   After current conference     Present a prompt after all texts in the
                                current conference have been read if a
                                text arrives in a session with a higher
                                priority.

   After current conference if letters arrive       Present a prompt 
                                after all texts in the current conference
                                have been read if a letter arrives in a 
                                session with a higher priority.

   After everything has been read   Present a prompt to go to the next
                                session with unreads after everything
                                has been read. The prompt is presented
                                regardless of session priorities.

   Never                        Never prompt to go to another LysKOM
                                session.")

    (kom-ding-on-no-subject-doc . "")
    (kom-ding-on-personal-messages-doc . "")
    (kom-ding-on-group-messages-doc . "")
    (kom-ding-on-common-messages-doc . "")
    (kom-ding-on-no-subject-doc . "")
    (kom-ding-on-wait-done-doc . "")
    (kom-ding-on-priority-break-doc . "")
    (kom-ding-on-new-letter-doc . "")
    (kom-check-for-new-comments-doc . "")
    (kom-check-commented-author-membership . "")
    (kom-confirm-multiple-recipients-doc . "")
    (kom-check-commented-author-membership-doc . "")
    (kom-complete-numbers-before-names-doc . "\
   When this is on and you enter a text that can be either a conference
   number or a conference name, the client will accept it as a conference 
   number, if possible. When this is off, the text will be accepted as
   a conference name first and number second.")
    (kom-keep-alive-interval-doc . "\
  The number of seconds between calls to the server used to keep the
  network connection to the LysKOM server active. If your network connection
  shuts down after a period of inactivity, set this to approximately half
  that period and use the command `Keep connection alive'.")
    (kom-lost-session-notification-doc . "\
  This setting determines how notification of abnormally terminated
  sessions is done. A message is always displayed in the buffer of 
  the terminated session. In addition, if this is set to \"Beep\", 
  the client will beep.  If it is instead set to \"Show notification 
  in all active LysKOM buffers\", the client will beep and insert a 
  short notice in all active LysKOM buffers.")
    (kom-text-no-prompts-doc . "\
  Specifies which commands will always prompt for text numbers and which
  will not. Commands that are not listed here will use default behavior.")
    (kom-saved-file-name-doc . "\
  Specifies the default file to archive texts in. The client will always
  prompt for the file name, using this as the default.")
    (kom-follow-attachments-doc . "\
  Specifies whether to read imported e-mail attachments as regular
  comments. When off, imported attachments are marked as read when the
  e-mail they are attached to is read.")
    (kom-show-unread-in-frame-title-doc . "\
  When this is on, the indicator \"(Unread)\" is shown in the title
  bar of all windows whose active buffer belongs to a LysKOM session
  with unread texts. The title bar may not be updated unless the
  window is open.")
    (kom-unread-mode-line-type-doc . "\
  This settings controls how unread texts are indicated in the mode line.
  `Simple' meand there is a single indication for all active sessions that
  shows if there are any unread texts or letters.

  `Show all sessions with unread' means all sessions with unread texts 
  will be listed in the mode line. If there are unread letters in a 
  session it will be indicated by uppercasing the session name (e.g. 
  LysLys becomes LYSLYS). If the session name is upper cased already
  (e.g. LYSLYS) it will be marked with asterisks (e.g. *LYSLYS*).

  Note that this setting should be the same for all active LysKom
  sessions. That means it's probably wise to save it in your .emacs.")
    (kom-created-texts-are-saved-doc . "\
  If this variable is set to a file name, all texts you write will be 
  saved to that file. Texts that could not be created are not saved.")
    (kom-confirm-add-recipients-doc . "\
  When this is on and you add a recipient to a text, the client will
  ask if comments to the text should also be sent to the new
  recipient. When this is off, comments will always be sent to the new
  recipient. For most people it is a good idea to leave this on.")

    (kom-trim-buffer-minimum-doc . "\
  If you have limited the LysKOM buffer size, this variable determines
  how much larger than the limit the buffer has to be before it is
  trimmed back to the maximum size. The variable must be an integer;
  the default is 4096.")

    (kom-dont-check-commented-authors-doc . "")
    (kom-print-relative-dates-doc . "\
  The time fields of texts (and a couple of other things) will be
  \"today\" or \"yesterday\" if this setting is on and the text was
  written today or yesterday, respectively. If it is off, absolute
  dates will always be shown.")
    (kom-show-mx-date-as-local-doc . "\
  If this setting is on, display timestamps of imported texts in the local
  timezone instead of the timezone specified in the timestamp.")
    (kom-print-seconds-in-time-strings-doc . "\
  If this setting is on, some time strings (for example the creation
  time of texts and sent messages) will include seconds.")
    (kom-review-uses-cache-doc . "\
  If this is turned on, commands that review texts will use saved copies
  of the text rather than get new copies from the server. This makes the
  commands run significantly faster, but in some cases the texts may 
  have changed since they were copied, and you will not see those 
  changes.")
    (kom-review-marks-texts-as-read-doc . "\
  If this is turned on, commands that review texts will also mark them
  as read. Otherwise, the review commands will leave your reading history
  untouched, as usual.")
    (kom-auto-review-faqs-doc . "\
  If this is turned on, FAQs that are not marked as read will be reviewed
  automatically when you log on or go to a conference with a new FAQ.")
    (kom-auto-list-faqs-doc . "\
  If this is turned on, new FAQs will be listed automatically when you
  log on or go to a conference with a new FAQ.")
    (kom-extended-status-information-doc . "\
  When this is on, additional information may be shown by commands that
  display person, conference and server status.")
    (kom-highlight-first-line-doc . "\
  When this is on, the first line (with text number, date and author) is
  shown with a different format than normal text.")
    (kom-highlight-dashed-lines-doc . "\
  When this is on, the lines before and after the text body is shown with
  a different format than normal text.")
    (kom-highlight-text-body-doc . "\
  When this is on, the text body is shown with a different format than
  normal text.")
    (kom-async-highlight-dashed-lines-doc . "\
  When this is on, the lines before and after personal, group and alarm
  messages are shown with a different format than normal text.")
    (kom-async-highlight-text-body-doc . "\
  When this is on, the body of personal, group and alarm messages are
  shown with a different format than normal text.")
    (kom-edit-hide-add-button-doc . "\
  When this is on, an [Add...] button will be shown after the list of
  recipients and properties when writing a new text.")
    (kom-format-html-authors-doc . "\
  This setting controls which authors' HTML messages that the client will
  attempt to format.")
    (kom-format-show-images-doc . "\
  This setting controls if texts marked as images are treated
  specially or handled as normal texts.") 
    (kom-keyboard-menu-immediate-selection-doc . "\
  This setting controls whether keyboard shortcuts in text-based context
  menus require confirmation with return or not. With this on you have
  to confirm all selections with return.")
    (kom-max-overlays-doc . "\
  Use this setting to limit the number of overlays in the LysKOM buffer
  \(overlays are used to create the colored backgrounds for texts\). By
  limiting the number of overlays it is possible to speed up operations
  such as scrolling significantly. Each text requires 1-4 overlays for
  highlighting, depending on other settings.")
    (kom-mark-read-texts-as-read-in-new-recipient-doc . "\
  When this is on, texts that have been read will automatically be
  marked as read in any conferences they are added to. This only works
  as long as the LysKOM session is logged in. Texts are never 
  automatically marked as read in your mailbox.")
    (kom-url-transformation-rules-doc . "\
  Rules for transformation of URLs before they are sent to a web browser.
  \"From\" is a regexp that is replaced by \"To\". Regexps are documented
  in the Emacs manual. The string in \"To\" can contain the following
  special sequences:

      \\& means substitute original matched text,
      \\N means substitute match for (...) number N,
      \\\\ means insert one \\.")

    (kom-text-links-doc . "\
  Text link definitions. Each line defines one link for texts with a
  particular recipient. All occurrences of the link pattern will be shown
  as URL links. The URL pointed to by the link is generated from the text
  matching the link pattern, by substituting according to the link
  substitution. The text that is highlighted is determined by the 
  \"Highlight group\" setting: zero means highlight the entire match. A
  number 1-9 indicated the parenthesized group to highlight (and if that
  makes no sense to you, stick with zero). If \"Case insensitive\" is
  on, text will be matched without recard for character case.

  The link substitution can contain the following special sequences:

      \\& means substitute original matched text,
      \\N means substitute match for (...) number N,
      \\\\ means insert one \\.")

    (kom-active-face-doc . "\
  Face used for clickable areas other than URLs, conference and person
  names, friends, morons and text numbers.")
    (kom-active-highlight-face-doc . "\
  Face intended for clickable areas that are to be highlighted.")
    (kom-active-strikethrough-face-doc . "\
  Face intended for clickable areas that are to be indicated with
  strikethrough.")
    (kom-url-face-doc . "\
  Face used for clickable URLs.")
    (kom-me-face-doc . "\
  Face used for my own name.")
    (kom-highlight-face-doc . "\
  Face used for highlighting clickable areas as the mouse is moved
  over them.")
    (kom-subject-face-doc . "\
  Face used for subject lines.")
    (kom-text-no-face-doc . "\
  Face used for text numbers.")
    (kom-friends-face-doc . "\
  Face used for names of people listed in \"Friends and other 
  special people\".")
    (kom-morons-face-doc . "\
  Face used for names of people listed in \"Morons and other
  \"special\" people.")
    (kom-presence-face-doc . "\
  Face used for presence messages.")
    (kom-first-line-face-doc . "\
  Face used for first header line. This face is overlayed on other
  text. Properties not defined by this face will be retained. For
  example, if you only set the weight property, the colors normally
  used will not be changed.")
    (kom-warning-face-doc . "\
  Face used for important warnings.")
    (kom-mark-face-doc . "\
  Face used for various marks, including the indicator shown when the
  client scrolls less than a full page. This face is overlayed on other
  text. Properties not defined by this face will be retained. For
  example, if you only set the weight property, the colors normally
  used will not be changed.")
    (kom-dim-face-doc . "\
  Face used for various discreet texts.")
    (kom-text-body-face-doc . "\
  Face used for text bodies. This face is overlayed on other
  text. Properties not defined by this face will be retained. For
  example, if you only set the weight property, the colors normally
  used will not be changed.")
    (kom-dashed-lines-face-doc . "\
  Face used for dashed lines around texts.  This face is overlayed
  on other text. Properties not defined by this face will be retained. 
  For example, if you only set the weight property, the colors 
  normally used will not be changed.")
    (kom-async-text-body-face-doc . "\
  Face used for messages.  This face is overlayed on other
  text. Properties not defined by this face will be retained. For
  example, if you only set the weight property, the colors normally
  used will not be changed.")
    (kom-async-dashed-lines-face-doc . "\
  Face used for dashed lines around messages.  This face is overlayed
  on other text. Properties not defined by this face will be retained. 
  For example, if you only set the weight property, the colors normally
  used will not be changed.")
    (kom-lynx-terminal-doc . "\
  Lynx can run inside emacs or in an xterm (or similar). This setting
  decides which.")
    (kom-lynx-terminal-command-doc . "\
  This is the command to use to start Lynx in emacs terminal-mode.
  It should be something simple, like \"lynx\".")
    (kom-lynx-xterm-command-doc . "\
  This is the command to use to start Lynx in an xterm. The command
  neets to start both the terminal emulator and lynx.")
    (kom-show-sync-messages-doc . "\
  When this is on, the client will display a message when the LysKOM
  server signals that it is saving the database.")
    (kom-highlight-conferences-doc . "\
  This setting controls display of conference names. For each choice
  it is possible to select a face. The value must be either a face or
  the name of a variable that contains a face.")
    (kom-netscape-variant-doc . "\
  This setting controls where the client opens URLs. How well the
  settings work depends on which variant of Firefox, Mozilla, Opera 
  or Netscape you are using, and even on which operating system you 
  are on.")
    (kom-auto-confirm-new-conferences-doc . "\
  If this setting is on, the commands \"List new conferences\" and
  \"List new users\" will automatically mark new conferences and
  users as known.")
    (kom-text-properties-doc . "\
  If this setting is on, the client will display different types of
  things using different fonts and colors. For example, text numbers
  and other clickable things will be displayed using a special color.")
    (kom-fontify-text-doc . "\
  If this setting is on, words and phrases that are delimited by
  special characters will be shown in bold or italics. For example,
  words delimited by asterisks will be shown bold.")

    ;;
    ;; Tags for variables
    ;;

    (kom-ansaphone-replies-tag . "Automatic replies")
    (kom-bury-buffers-tag . "Bury buffers when changing LysKOM:")

  (kom-personal-messages-in-window-tag . "Personal messages:      ")
    (kom-customize-in-window-tag       . "LysKOM customization:   ")
    (kom-write-texts-in-window-tag     . "Author new texts:       ")
    (kom-prioritize-in-window-tag      . "Prioritize conferences: ")
    (kom-edit-filters-in-window-tag    . "Modify filters:         ")
    (kom-view-commented-in-window-tag  . "Review comments:        ")
    (kom-list-membership-in-window-tag . "List membership:        ")

    (kom-user-prompt-format-tag . "Prompt format:")
    (kom-user-prompt-format-executing-tag . "Prompt format when executing:")
    (kom-anonymous-prompt-format-tag . "Prompt format (anonymous):")
    (kom-anonymous-prompt-format-executing-tag . "Prompt format when executing (anonymous):")
    (kom-enabled-prompt-format-tag . "Prompt format (admin):")
    (kom-enabled-prompt-format-executing-tag . "Prompt format when executing (admin):")

    (kom-higher-priority-breaks-tag . 
"Read prioritized texts:                           ")
    (kom-created-texts-are-read-tag . 
"Automatically read created texts:                 ")
    (kom-default-mark-tag           . 
"Default mark:                                     ")
    (kom-print-number-of-unread-on-entrance-tag . 
"Show number of unread when entering a conference: ")
    (kom-follow-comments-outside-membership-tag .
"Follow comment chains outside membership:         ")
    (kom-show-footnotes-immediately-tag .
"Show footnotes immediately:                       ")
    (kom-membership-default-priority-tag . 
"Default priority for new memberships:             ")
   (kom-membership-default-message-flag-tag . 
"Receive group messages for new memberships:       ")
    (kom-dashed-lines-tag . 
"Dashed lines around the text body:                ")
    (kom-autowrap-tag . 
"Fill wide paragraphs before displaying:           ")
    (kom-autowrap-timeout-tag . 
"Maximum time spend filling paragraphs:            ")
    (kom-show-author-at-end-tag .
"Show the name of the author after the body:       ")

    (kom-truncate-threshold-tag .
"Truncate long texts:                              ")
    (kom-truncate-show-lines-tag .
"Lines to show of truncated texts:                 ")

    (kom-reading-puts-comments-in-pointers-last-tag . "Comment links are shown:")
    (kom-read-depth-first-tag . "Read order:")
    (kom-read-related-first-tag . "Read related texts first:")
    (kom-deferred-printing-tag . "Delayed display:")
    (kom-continuous-scrolling-tag . "Continuous scrolling:")

    (kom-presence-messages-in-echo-area-tag . 
"Presence messages in echo area:        ")
    (kom-presence-messages-in-buffer-tag .
"Presence messages in the LysKOM buffer:")
    (kom-page-before-command-tag . "Clear the screen:")

    (kom-remember-password-tag . "Remember LysKOM session password:")
    (kom-idle-hide-tag . 
"Number of minutes of inactivity before session is hidden:      ")
    (kom-show-where-and-what-tag . 
"Show where sessions are logged on from and what they are doing:    ")
    (kom-show-since-and-when-tag . 
"Show when sessions connected and how long they have been inactive: ")



    (kom-login-hook-tag . "Commands executed after logging on:")
    (kom-do-when-done-tag . "Commands to execute after reading everything:")
    (kom-permissive-completion-tag . "Fussy name completion:")
    (kom-show-personal-messages-in-buffer-tag . 
"Where are messages shown: ")
    (kom-pop-personal-messages-tag . 
"Pop up message buffer:    ")
    (kom-default-message-recipient-tag . 
"Default message recipient:")

    (kom-audio-player-tag . "Audio player program:")
    (kom-ding-on-new-letter-tag        . "When a letter arrives:            ")
    (kom-ding-on-priority-break-tag    . "When a prioritized text arrives:  ")
    (kom-ding-on-wait-done-tag         . "When done waiting:                ")
    (kom-ding-on-common-messages-tag   . "When a public message arrives:    ")
    (kom-ding-on-group-messages-tag    . "When a group message arrives:     ")
    (kom-ding-on-personal-messages-tag . "When a personal message arrives:  ")
    (kom-ding-on-no-subject-tag     .    "When you forget the subject line: ")

    (kom-filter-outgoing-messages-tag . "Show automatic messages:")
    (kom-friends-tag . "Friends and other special people:")
    (kom-morons-tag . "Morons and other \"special\" people:")
    (kom-person-list-1-tag . "Generlic list of conferences/persons (1):")
    (kom-person-list-2-tag . "Generlic list of conferences/persons (2):")
    (kom-person-list-3-tag . "Generlic list of conferences/persons (3):")
    (kom-person-list-4-tag . "Generlic list of conferences/persons (4):")
    (kom-url-viewer-preferences-tag . "Open URLs using the following program:")
    (kom-windows-browser-command-tag . "Command to start a web browser on Windows:")
    (kom-mosaic-command-tag . "Command to start NCSA Mosaic:")
    (kom-netscape-command-tag . "Command to start Netscape-like browsers:")
    (kom-galeon-command-tag . "Command to start Galeon:")

    (kom-symbolic-marks-alist-tag . "Symbolic mark types:")

    (kom-cite-string-tag . "Quotation indicator:")
    (kom-confirm-multiple-recipients-tag . 
"Confirm multiple recipients:         ")
    (kom-check-commented-author-membership-tag . 
"Check membership of commented author:")
    (kom-check-for-new-comments-tag . 
"Check for unread comments:           ")

    (kom-ansaphone-record-messages-tag . 
"Save messages when auto reply is on:    ")
    (kom-ansaphone-show-messages-tag . 
"Display messages when auto reply is on: ")
    (kom-ansaphone-default-reply-tag . "Auto reply message:")


    (kom-inhibit-typeahead-tag . "Buffer keypresses:")
    (kom-max-buffer-size-tag . "Maximum buffer size:")

    (kom-remote-control-tag .     "Remote commands on or off:             ")
    (kom-self-control-tag .       "Allow me to use remote commands:       ")
    (kom-remote-controllers-tag . "People allowed to use remove commands:")

    (kom-customize-format-tag . "Show documentation for all settings:")
    (kom-default-language-tag . "Language list:")
    (kom-show-namedays-tag    . "Show today's names:")
    (kom-ispell-dictionary-tag . "Spelling dictionary:")

    (kom-show-week-number-tag . "Show week number:")
    (kom-relogin-behaviour-tag . "Reattach automatically:")
    (kom-relogin-inhibit-commands-tag . "Commands for which reattachment is inhibited:")
    (kom-membership-default-placement-tag . "Placement of new memberships:")
    (kom-show-imported-importer-tag . "Show importer of imported messages:")
    (kom-show-imported-envelope-sender-tag . "Show sender of imported messages:")
    (kom-show-imported-external-recipients-tag . "Show external recipients of imported messages:")
    (kom-show-imported-message-id-tag . "Show message-id of imported messages:")
    (kom-agree-text-tag . "Default text for Agree:")
    (kom-silent-ansaphone-tag . "Beep when auto reply is on:")
    (kom-default-session-priority-tag . "Default session priority:")
    (kom-unsubscribe-makes-passive-tag . "Leaving a conference converts membership to passive:")
    (kom-review-priority-tag . "Priority for review commands:")
    (kom-show-creating-software-tag . "Show creating software:")
    (kom-text-footer-format-tag . "Text footer format:")
    (kom-long-lines-tag . "Long dashed lines:")
    (kom-postpone-default-tag . "Default number of texts to postpone:")
    (kom-allow-incompleteness-tag . "Allow incomplete information about unread texts:")
    (kom-smileys-tag . "Show smileys graphically:")
    (kom-ignore-message-senders-tag . "Don't show messages from:")
    (kom-ignore-message-recipients-tag . "Don't show messages to:")
    (kom-text-footer-dash-length-tag . "Text footer length:")
    (kom-text-header-dash-length-tag . "Length of dashed line before text:")
    (kom-show-personal-message-date-tag . "Show date and time of messages:")
    (kom-w3-simplify-body-tag . "Display HTML without document colors:")
    (kom-mercial-tag . "Text to display when all is read:")
    (kom-session-nickname-tag . "How session name is determined:")
    (kom-server-priority-tag . "Session priority:")
    (kom-server-priority-breaks-tag . "Prompt to go to next LysKOM:")
    (kom-complete-numbers-before-names-tag . "Read conference numbers before names:")
    (kom-keep-alive-interval-tag . "Keep connection alive interval:")
    (kom-lost-session-notification-tag . "Notification of lost sessions:")
    (kom-text-no-prompts-tag . "How commands prompt for text numbers:")
    (kom-saved-file-name-tag . "File to archive texts in:")
    (kom-follow-attachments-tag . "Read imported attachments as texts:")
    (kom-show-unread-in-frame-title-tag . "Show unread indicator in title bar:")
    (kom-unread-mode-line-type-tag . "How unread texts are indicated in the mode line:")
    (kom-created-texts-are-saved-tag . "Save created texts:")
    (kom-confirm-add-recipients-tag . "Ask if comments should be sent to new recipients:")
    (kom-trim-buffer-minimum-tag . "How small parts of the LysKOM buffer are trimmed:")
    (kom-dont-check-commented-authors-tag . "Authors not to check:")
    (kom-print-relative-dates-tag . "Show relative dates:")
    (kom-show-mx-date-as-local-tag . "Show timestamps in local timezone:")
    (kom-print-seconds-in-time-strings-tag . "Include seconds in time strings:")
    (kom-review-uses-cache-tag . "Review commands use cached texts:")
    (kom-review-marks-texts-as-read-tag . "Review commands mark texts as read:")
    (kom-auto-review-faqs-tag . "Review new FAQs automatically:")
    (kom-auto-list-faqs-tag . "List new FAQs automatically:")
    (kom-extended-status-information-tag . "Extended status information:")
    (kom-highlight-first-line-tag . "Color first line:")
    (kom-highlight-dashed-lines-tag . "Color dashed lines:")
    (kom-highlight-text-body-tag . "Color text backgrounds:")
    (kom-async-highlight-dashed-lines-tag . "Color dashed lines around messages:")
    (kom-async-highlight-text-body-tag . "Color message backgrounds:")
    (kom-edit-hide-add-button-tag . "Show add button when writing texts:")
    (kom-format-html-authors-tag . "Format HTML-messages per author:")
    (kom-format-show-images-tag . "Show texts with images as images:")
    (kom-keyboard-menu-immediate-selection-tag . "Shortcuts in text menus require confirmation:")
    (kom-max-overlays-tag . "Maximum number of overlays:")
    (kom-mark-read-texts-as-read-in-new-recipient-tag . "Mark read texts read when added to new recipients:")
    (kom-url-transformation-rules-tag . "Transformation of URLs:")
    (kom-text-links-tag . "Text links:")

    (kom-active-face-tag . "Other clickable areas:")
    (kom-active-highlight-face-tag . "Highlighted clickable areas:")
    (kom-active-strikethrough-face-tag . "Struck-through clickable areas:")
    (kom-url-face-tag . "Clickable URLs:")
    (kom-me-face-tag . "My own name:")
    (kom-highlight-face-tag . "Mouseover highlight:")
    (kom-subject-face-tag . "Subject lines:")
    (kom-text-no-face-tag . "Text numbers:")
    (kom-friends-face-tag . "Friends and other special people:")
    (kom-morons-face-tag . "Morons and other \"special\" people:")
    (kom-presence-face-tag . "Presence messages:")
    (kom-first-line-face-tag . "First header line:")
    (kom-warning-face-tag . "Important warnings:")
    (kom-mark-face-tag . "Various marks:")
    (kom-dim-face-tag . "Various discreet texts:")
    (kom-text-body-face-tag . "Text bodies:")
    (kom-dashed-lines-face-tag . "Dashed lines around texts:")
    (kom-async-text-body-face-tag . "Messages:")
    (kom-async-dashed-lines-face-tag . "Dashed lines around messages:")
    (kom-lynx-terminal-command-tag . "Command to run lynx in emacs:")
    (kom-lynx-terminal-tag . "Where to run lynx:")
    (kom-lynx-xterm-command-tag . "Command to run lynx in xterm:")
    (kom-show-sync-messages-tag . "Show database-saving message:")
    (kom-highlight-conferences-tag . "Display of names::")
    (kom-netscape-variant-tag . "Where Netscape-like browsers opens URLs:")
    (kom-auto-confirm-new-conferences-tag . "Always mark new conferences as known:")
    (kom-text-properties-tag . "Use fonts and colors:")
    (kom-fontify-text-tag . "Bold and italic fonts in texts:")
    )
)






;;;; ============================================================
;;;; The default Ansaphone message goes here. The more complex 
;;;; message specification probably should too, but it's not here
;;;; yet. People who know how to use it are smart enough to do it
;;;; right.

(lyskom-language-var local kom-ansaphone-default-reply en 
  "I am not reading LysKOM right now. Please write a letter instead.")
        
;;;; ============================================================
;;;; Other language-dependent variables
;;;;

(lyskom-language-var local kom-ispell-dictionary en
  "english")



;;;; ================================================================
;;;; Tell phrases should be configured with the default language used
;;;; at the server and not for person reading if they happens to
;;;; differ. This is of course because they are sent to the server for
;;;; everybody else to see.

;;;; Aronsson was here 4 DEC 1990, thus creating version 0.18
					; Created *-tell-*


;;; To coders of the elisp-client:
;;; You not only have to change the text here, you also have to modify
;;; kom-tell-phrases-validation-list in vars.el if you add or remove
;;; one of these.

(eval-when-compile (defvar kom-tell-phrases))

(lyskom-language-strings local kom-tell-phrases en
  '((kom-tell-silence		. "") ; Why ?
    (kom-tell-send		. "Is trying to post a text.")
    (kom-tell-login		. "Is entering LysKOM.")
    (kom-tell-read		. "Is reading.")
    (kom-tell-1st-pres		. "Is writing the first presentation.")
    (kom-tell-write-comment	. "Is writing a comment.")
    (kom-tell-write-footnote	. "Is writing a footnote.")
    (kom-tell-write-letter	. "Is writing a letter.")
    (kom-tell-write-reply	. "Is writing a personal reply.")
    (kom-tell-write-text	. "Is writing a text.")
    (kom-tell-conf-pres		. "Is writing the presentation for a new conference.")
    (kom-tell-recover		. "Is restarting KOM. Sigh.")
    (kom-tell-wait		. "Is waiting.")
    (kom-tell-regret		. "Decides to throw away the text.")
    (kom-tell-review		. "Is reviewing.")
    (kom-tell-change-name       . "Takes on a new name.")
    (kom-tell-change-supervisor . "Changes the supervisor of something.")
    (kom-tell-next-lyskom	. "Moves to a different LysKOM.")))

(if (and (boundp 'kom-tell-phrases)
         kom-tell-phrases)
    (lyskom-language-strings local kom-tell-phrases en
      (mapcar (function 
               (lambda (x)
                 (cond ((and (consp x)
                             (symbolp (car x))
                             (stringp (cdr x))) x)
                       ((and (consp x)
                             (symbolp (car x))
                             (consp (cdr x))
                             (stringp (car (cdr x))))
                        (cons (car x) (car (cdr x))))
                       (t nil))))
              kom-tell-phrases)))


;; Placed here because this must NOT be evaluated before 
;; kom-tell-phrases is defined:

(lyskom-language-var local kom-mercial en (lyskom-get-string 'kom-tell-wait 
                                                       'kom-tell-phrases))


(lyskom-language-strings local lyskom-error-texts en
  '((error-0 . "No error")
    (error-2 . "Not yet implemented by the server")
    (error-3 . "No longer implemented by the server")
    (error-4 . "Wrong password")
    (error-5 . "String too long")
    (error-6 . "You have not logged on")
    (error-7 . "Nobody may enter LysKOM at this time")
    (error-8 . "You attempted to use conference number 0")
    (error-9 . "Undefined or secret conference")
    (error-10 . "Undefined or secret user")
    (error-11 . "No read or write permission")
    (error-12 . "Illegal operation")
    (error-13 . "You are not a member of that conference")
    (error-14 . "There is no text with that number")
    (error-15 . "You cannot use global text number 0")
    (error-16 . "There is no text with that local number")
    (error-17 . "You cannot use local text number 0")
    (error-18 . "Name too short, too long or containing illegal characters")
    (error-19 . "Index out of bounds")
    (error-20 . "The conference already exists")
    (error-21 . "The user already exists")
    (error-22 . "Secret but not read-protected")
    (error-23 . "You are not allowed to change the person/conference flag")
    (error-24 . "Error in the database. Tough luck.")
    (error-25 . "Invalid recipient or comment type")
    (error-26 . "Illegal info type. (Bug in the client)")
    (error-27 . "Already recipient of this text")
    (error-28 . "Already comment to this text")
    (error-29 . "Already footnote to this text")
    (error-30 . "Not a recipient of this text")
    (error-31 . "Not a comment to this text")
    (error-32 . "Not a footnote to this text")
    (error-33 . "Too many recipients")
    (error-34 . "Too many commentsp")
    (error-35 . "Too many footnotes")
    (error-36 . "Too many marks")
    (error-37 . "You are not the author of that text")
    (error-38 . "You cannot connect to the server")
    (error-39 . "Out of memory")
    (error-40 . "The server is gone crazy")
    (error-41 . "The client thinks that the server says that it does not understand the client")
    (error-42 . "No such session")
    (error-43 . "Invalid regular expression")
    (error-44 . "Can't unmark a text that was not marked")
    (error-45 . "Temorary lossage. Please try again later")
    (error-46 . "Sending huge messages to the server is not a nice thing to do")
    (error-47 . "Anonymous texts are not accepted by all recipients")
    (error-48 . "Invalid property")
    (error-49 . "Change of property not permitted")
    (error-50 . "Unknown asynchronous message")
    (error-51 . "Internal server error")
    (error-52 . "Feature disabled in the server")
    (error-53 . "Unable to send message")
    (error-54 . "Invalid membership type")
    (error-55 . "Invalid range")
    (error-56 . "Invalidrange list")
    (error-57 . "Undefined measurement")
    (error-58 . "Priority denied")
    (error-59 . "Weight denied")
    (error-60 . "Zero weight")
    (error-61 . "Invalid boolean value")

    (error--1 . "You are not a member of any recipient")
))

(lyskom-language-var global lyskom-unread-mode-line en
  (lyskom-make-lyskom-unread-mode-line))

(lyskom-language-var global lyskom-unread-title-format en
  (lyskom-make-lyskom-unread-title-format))

(lyskom-language-var local lyskom-help-data en lyskom-en-help-data)


(provide 'lyskom-strings)

;;; english-strings ends here

(defvar lyskom-sv-help-data (quote ((help-on-command "Kommandobeskrivning" (section ((id . "help-on-command") (prompt . "Kommandobeskrivning")) ((call ((function . "lyskom-help-on-command")) nil)))) (commands "Snabbkommandon" (section ((id . "commands") (prompt . "Snabbkommandon")) ((p nil ((TEXT . "Snabbommandon:"))) (keymap nil nil)))) (language-help "" (section ((id . "language-help") (prompt . "")) ((h2 nil ((TEXT . "Language Settings"))) (p nil ((TEXT . " You can switch the language in this session by using the command ") (cref ((id . "kom-change-language")) nil) (TEXT . " or the Emacs command ") (i nil ((TEXT . "M-x kom-change-language"))) (TEXT . ". To also change the key bindings (this will affect all sessions), type C-u before the command (e.g. C-u x") (cref ((id . "kom-change-language")) nil) (TEXT . "). "))) (p nil ((TEXT . " For full information, including how to change the default language, see ") (refer ((id . "language")) nil) (TEXT . " (this information will be presented using the current language selection). ")))))) (language #("Språk i elispklienten" 0 5 (charset iso-8859-1) 6 7 (charset iso-8859-1) 8 21 (charset iso-8859-1)) (section ((id . "language") (prompt . #("Språk i elispklienten" 0 5 (charset iso-8859-1) 6 7 (charset iso-8859-1) 8 21 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Stöd för flera språk" 0 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 14 (charset iso-8859-1) 15 20 (charset iso-8859-1))))) (p nil ((TEXT . #(" Elispklienten kan byggas med stöd för flera språk. Standard är att bygga med svenska och engelska, med engelska förvalt. " 1 14 (charset iso-8859-1) 15 18 (charset iso-8859-1) 19 25 (charset iso-8859-1) 26 29 (charset iso-8859-1) 30 34 (charset iso-8859-1) 35 38 (charset iso-8859-1) 39 44 (charset iso-8859-1) 45 51 (charset iso-8859-1) 52 60 (charset iso-8859-1) 61 63 (charset iso-8859-1) 64 67 (charset iso-8859-1) 68 73 (charset iso-8859-1) 74 77 (charset iso-8859-1) 78 85 (charset iso-8859-1) 86 89 (charset iso-8859-1) 90 99 (charset iso-8859-1) 100 103 (charset iso-8859-1) 104 112 (charset iso-8859-1) 113 121 (charset iso-8859-1))))) (h2 nil ((TEXT . #("Tillfälligt språkbyte" 0 11 (charset iso-8859-1) 12 21 (charset iso-8859-1))))) (p nil ((TEXT . #(" Använd " 1 7 (charset iso-8859-1))) (cref ((id . "kom-change-language")) nil) (TEXT . #(" för att byta språk tillfälligt. Notera att det krävs ett prefixargument till " 1 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 19 (charset iso-8859-1) 20 32 (charset iso-8859-1) 33 39 (charset iso-8859-1) 40 43 (charset iso-8859-1) 44 47 (charset iso-8859-1) 48 53 (charset iso-8859-1) 54 57 (charset iso-8859-1) 58 72 (charset iso-8859-1) 73 77 (charset iso-8859-1))) (cref ((id . "kom-change-language")) nil) (TEXT . #(" för att byta tangentbindningar. Dessa byts då i " 1 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 32 (charset iso-8859-1) 33 38 (charset iso-8859-1) 39 43 (charset iso-8859-1) 44 46 (charset iso-8859-1) 47 48 (charset iso-8859-1))) (i nil ((TEXT . "alla"))) (TEXT . #(" sessioner, inte bara i den där man ger kommandot. " 1 11 (charset iso-8859-1) 12 16 (charset iso-8859-1) 17 21 (charset iso-8859-1) 22 23 (charset iso-8859-1) 24 27 (charset iso-8859-1) 28 31 (charset iso-8859-1) 32 35 (charset iso-8859-1) 36 39 (charset iso-8859-1) 40 50 (charset iso-8859-1))))) (h2 nil ((TEXT . #("Permanent språkbyte" 0 9 (charset iso-8859-1) 10 19 (charset iso-8859-1))))) (p nil ((TEXT . #(" Använd " 1 7 (charset iso-8859-1))) (cref ((id . "kom-customize")) nil) (TEXT . #(" för att byta språk permanent. Genom att spara språkinställningen i .emacs kommer det att bli standardspråk i klienten. " 1 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 19 (charset iso-8859-1) 20 30 (charset iso-8859-1) 31 36 (charset iso-8859-1) 37 40 (charset iso-8859-1) 41 46 (charset iso-8859-1) 47 65 (charset iso-8859-1) 66 67 (charset iso-8859-1) 68 74 (charset iso-8859-1) 75 81 (charset iso-8859-1) 82 85 (charset iso-8859-1) 86 89 (charset iso-8859-1) 90 93 (charset iso-8859-1) 94 107 (charset iso-8859-1) 108 109 (charset iso-8859-1) 110 119 (charset iso-8859-1))))) (h2 nil ((TEXT . #("Ändra standardspråk" 0 5 (charset iso-8859-1) 6 19 (charset iso-8859-1))))) (p nil ((TEXT . #(" Det finns många sätt att ändra standardspråk i klienten. Klienten tittar i tur och ordning på följande: " 1 4 (charset iso-8859-1) 5 10 (charset iso-8859-1) 11 16 (charset iso-8859-1) 17 21 (charset iso-8859-1) 22 25 (charset iso-8859-1) 26 31 (charset iso-8859-1) 32 45 (charset iso-8859-1) 46 47 (charset iso-8859-1) 48 57 (charset iso-8859-1) 58 66 (charset iso-8859-1) 67 73 (charset iso-8859-1) 74 75 (charset iso-8859-1) 76 79 (charset iso-8859-1) 80 83 (charset iso-8859-1) 84 91 (charset iso-8859-1) 92 94 (charset iso-8859-1) 95 104 (charset iso-8859-1))))) (list nil ((item nil ((TEXT . #("Variabeln kom-default-language (ändra med " 0 9 (charset iso-8859-1) 10 30 (charset iso-8859-1) 31 37 (charset iso-8859-1) 38 41 (charset iso-8859-1))) (cref ((id . "kom-customize")) nil) (TEXT . ")"))) (item nil ((TEXT . "Omgivningsvariabeln KOMLANGUAGE"))) (item nil ((TEXT . "Omgivningsvariabeln LC_ALL"))) (item nil ((TEXT . "Omgivningsvariabeln LC_MESSAGES"))) (item nil ((TEXT . "Omgivningsvariabeln LANG"))))) (p nil ((TEXT . #(" Variabeln kom-default-language kan sättas till en lista av symboler, där varje symbol motsvarar ett språk. Se nedan för en lista av språk som elispklienten kan ha stöd för. Det första språk på listan som elispklienten klarar kommer att användas. " 1 10 (charset iso-8859-1) 11 31 (charset iso-8859-1) 32 35 (charset iso-8859-1) 36 42 (charset iso-8859-1) 43 47 (charset iso-8859-1) 48 50 (charset iso-8859-1) 51 56 (charset iso-8859-1) 57 59 (charset iso-8859-1) 60 69 (charset iso-8859-1) 70 73 (charset iso-8859-1) 74 79 (charset iso-8859-1) 80 86 (charset iso-8859-1) 87 96 (charset iso-8859-1) 97 100 (charset iso-8859-1) 101 107 (charset iso-8859-1) 108 110 (charset iso-8859-1) 111 116 (charset iso-8859-1) 117 120 (charset iso-8859-1) 121 123 (charset iso-8859-1) 124 129 (charset iso-8859-1) 130 132 (charset iso-8859-1) 133 138 (charset iso-8859-1) 139 142 (charset iso-8859-1) 143 156 (charset iso-8859-1) 157 160 (charset iso-8859-1) 161 163 (charset iso-8859-1) 164 168 (charset iso-8859-1) 169 173 (charset iso-8859-1) 174 177 (charset iso-8859-1) 178 184 (charset iso-8859-1) 185 190 (charset iso-8859-1) 191 193 (charset iso-8859-1) 194 200 (charset iso-8859-1) 201 204 (charset iso-8859-1) 205 218 (charset iso-8859-1) 219 225 (charset iso-8859-1) 226 232 (charset iso-8859-1) 233 236 (charset iso-8859-1) 237 246 (charset iso-8859-1))))) (p nil ((TEXT . #(" Omgivningvariablerna skall vara satta till en språk-kod. Till exempel innebär värdet \"sv\" att elispklientn skall använda svenska som standardspråk. " 1 21 (charset iso-8859-1) 22 27 (charset iso-8859-1) 28 32 (charset iso-8859-1) 33 38 (charset iso-8859-1) 39 43 (charset iso-8859-1) 44 46 (charset iso-8859-1) 47 57 (charset iso-8859-1) 58 62 (charset iso-8859-1) 63 70 (charset iso-8859-1) 71 78 (charset iso-8859-1) 79 85 (charset iso-8859-1) 86 90 (charset iso-8859-1) 91 94 (charset iso-8859-1) 95 107 (charset iso-8859-1) 108 113 (charset iso-8859-1) 114 121 (charset iso-8859-1) 122 129 (charset iso-8859-1) 130 133 (charset iso-8859-1) 134 148 (charset iso-8859-1))))) (h2 nil ((TEXT . #("Kända språk" 0 5 (charset iso-8859-1) 6 11 (charset iso-8859-1))))) (list nil ((item nil ((TEXT . "sv - Svenska"))) (item nil ((TEXT . "en - Engelska")))))))) (conferences #("Veta mer om möten" 0 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 17 (charset iso-8859-1)) (section ((id . "conferences") (prompt . #("Veta mer om möten" 0 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 17 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Möten" 0 5 (charset iso-8859-1))))) (p nil ((TEXT . #(" Varje LysKOM-system är uppdelat i ett antal olika möten som alla behandlar olika ämnen. Vem som helst kan skapa möten, men man ska tänka efter ordenligt innan man skapar ett möte. I ett gammalt LysKOM-system så finns det ofta redan möten som handlar om det man är intresserad av. " 1 6 (charset iso-8859-1) 7 20 (charset iso-8859-1) 21 23 (charset iso-8859-1) 24 32 (charset iso-8859-1) 33 34 (charset iso-8859-1) 35 38 (charset iso-8859-1) 39 44 (charset iso-8859-1) 45 50 (charset iso-8859-1) 51 56 (charset iso-8859-1) 57 60 (charset iso-8859-1) 61 65 (charset iso-8859-1) 66 75 (charset iso-8859-1) 76 81 (charset iso-8859-1) 82 88 (charset iso-8859-1) 89 92 (charset iso-8859-1) 93 96 (charset iso-8859-1) 97 102 (charset iso-8859-1) 103 106 (charset iso-8859-1) 107 112 (charset iso-8859-1) 113 119 (charset iso-8859-1) 120 123 (charset iso-8859-1) 124 127 (charset iso-8859-1) 128 131 (charset iso-8859-1) 132 137 (charset iso-8859-1) 138 143 (charset iso-8859-1) 144 153 (charset iso-8859-1) 154 159 (charset iso-8859-1) 160 163 (charset iso-8859-1) 164 170 (charset iso-8859-1) 171 174 (charset iso-8859-1) 175 180 (charset iso-8859-1) 181 182 (charset iso-8859-1) 183 186 (charset iso-8859-1) 187 194 (charset iso-8859-1) 195 208 (charset iso-8859-1) 209 211 (charset iso-8859-1) 212 217 (charset iso-8859-1) 218 221 (charset iso-8859-1) 222 226 (charset iso-8859-1) 227 232 (charset iso-8859-1) 233 238 (charset iso-8859-1) 239 242 (charset iso-8859-1) 243 250 (charset iso-8859-1) 251 253 (charset iso-8859-1) 254 257 (charset iso-8859-1) 258 261 (charset iso-8859-1) 262 264 (charset iso-8859-1) 265 276 (charset iso-8859-1) 277 280 (charset iso-8859-1))))) (p nil ((TEXT . #(" Möten kan vara öppna, slutna och hemliga. Vem som helst kan gå med i och läsa inlägg i alla öppna möten. För att gå med i slutna möten måste mötesorganisatören lägga till medlemskapet. Hemliga möten går inte ens att se, och servern gör sitt bästa för att inte läcka någon information om dessa. De flesta möten brukar vara öppna. Slutna och hemliga möten är mycket ovanligare. " 1 6 (charset iso-8859-1) 7 10 (charset iso-8859-1) 11 15 (charset iso-8859-1) 16 22 (charset iso-8859-1) 23 29 (charset iso-8859-1) 30 33 (charset iso-8859-1) 34 42 (charset iso-8859-1) 43 46 (charset iso-8859-1) 47 50 (charset iso-8859-1) 51 56 (charset iso-8859-1) 57 60 (charset iso-8859-1) 61 63 (charset iso-8859-1) 64 67 (charset iso-8859-1) 68 69 (charset iso-8859-1) 70 73 (charset iso-8859-1) 74 78 (charset iso-8859-1) 79 85 (charset iso-8859-1) 86 87 (charset iso-8859-1) 88 92 (charset iso-8859-1) 93 98 (charset iso-8859-1) 99 105 (charset iso-8859-1) 106 109 (charset iso-8859-1) 110 113 (charset iso-8859-1) 114 116 (charset iso-8859-1) 117 120 (charset iso-8859-1) 121 122 (charset iso-8859-1) 123 129 (charset iso-8859-1) 130 135 (charset iso-8859-1) 136 141 (charset iso-8859-1) 142 160 (charset iso-8859-1) 161 166 (charset iso-8859-1) 167 171 (charset iso-8859-1) 172 185 (charset iso-8859-1) 186 193 (charset iso-8859-1) 194 199 (charset iso-8859-1) 200 203 (charset iso-8859-1) 204 208 (charset iso-8859-1) 209 212 (charset iso-8859-1) 213 216 (charset iso-8859-1) 217 220 (charset iso-8859-1) 221 224 (charset iso-8859-1) 225 232 (charset iso-8859-1) 233 236 (charset iso-8859-1) 237 241 (charset iso-8859-1) 242 247 (charset iso-8859-1) 248 251 (charset iso-8859-1) 252 255 (charset iso-8859-1) 256 260 (charset iso-8859-1) 261 266 (charset iso-8859-1) 267 272 (charset iso-8859-1) 273 284 (charset iso-8859-1) 285 287 (charset iso-8859-1) 288 294 (charset iso-8859-1) 295 297 (charset iso-8859-1) 298 304 (charset iso-8859-1) 305 310 (charset iso-8859-1) 311 317 (charset iso-8859-1) 318 322 (charset iso-8859-1) 323 329 (charset iso-8859-1) 330 336 (charset iso-8859-1) 337 340 (charset iso-8859-1) 341 348 (charset iso-8859-1) 349 354 (charset iso-8859-1) 355 357 (charset iso-8859-1) 358 364 (charset iso-8859-1) 365 376 (charset iso-8859-1))))) (p nil ((TEXT . #(" Följande kommandon kan vara av intresse för att komma igång: " 1 9 (charset iso-8859-1) 10 19 (charset iso-8859-1) 20 23 (charset iso-8859-1) 24 28 (charset iso-8859-1) 29 31 (charset iso-8859-1) 32 40 (charset iso-8859-1) 41 44 (charset iso-8859-1) 45 48 (charset iso-8859-1) 49 54 (charset iso-8859-1) 55 61 (charset iso-8859-1))))) (inline ((id . "kom-list-conferences")) nil) (inline ((id . "kom-list-re")) nil) (inline ((id . "kom-review-presentation")) nil) (inline ((id . "kom-add-self")) nil) (inline ((id . "kom-go-to-conf")) nil) (h2 nil ((TEXT . "Prioriteter"))) (p nil ((TEXT . #(" Dina medlemskap i möten har prioriteter. Möten med högre prioritet kommer att presenteras före möten med lägre prioritet när du loggar in. Använd " 1 5 (charset iso-8859-1) 6 16 (charset iso-8859-1) 17 18 (charset iso-8859-1) 19 24 (charset iso-8859-1) 25 28 (charset iso-8859-1) 29 41 (charset iso-8859-1) 42 47 (charset iso-8859-1) 48 51 (charset iso-8859-1) 52 57 (charset iso-8859-1) 58 67 (charset iso-8859-1) 68 74 (charset iso-8859-1) 75 78 (charset iso-8859-1) 79 90 (charset iso-8859-1) 91 95 (charset iso-8859-1) 96 101 (charset iso-8859-1) 102 105 (charset iso-8859-1) 106 111 (charset iso-8859-1) 112 121 (charset iso-8859-1) 122 125 (charset iso-8859-1) 126 128 (charset iso-8859-1) 129 135 (charset iso-8859-1) 136 139 (charset iso-8859-1) 140 146 (charset iso-8859-1))) (cref ((id . "kom-prioritize")) nil) (TEXT . #(" för att prioritera möten. " 1 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 19 (charset iso-8859-1) 20 26 (charset iso-8859-1)))))))) (persons "Veta mer om personer" (section ((id . "persons") (prompt . "Veta mer om personer")) ((h1 nil ((TEXT . "Personer"))) (p nil ((TEXT . #(" Varje användare i LysKOM har en person. En person innehåller en del statistik om vad användaren har gjort (skapade inlägg, lästa inlägg, närvarotid och en del annat). Varje person är dessutom kopplad till en brevlåda med samma namn som personen. Brevlådan är i princip ett vanligt möte, men från början är det slutet. " 1 6 (charset iso-8859-1) 7 16 (charset iso-8859-1) 17 18 (charset iso-8859-1) 19 25 (charset iso-8859-1) 26 29 (charset iso-8859-1) 30 32 (charset iso-8859-1) 33 40 (charset iso-8859-1) 41 43 (charset iso-8859-1) 44 50 (charset iso-8859-1) 51 61 (charset iso-8859-1) 62 64 (charset iso-8859-1) 65 68 (charset iso-8859-1) 69 78 (charset iso-8859-1) 79 81 (charset iso-8859-1) 82 85 (charset iso-8859-1) 86 96 (charset iso-8859-1) 97 100 (charset iso-8859-1) 101 106 (charset iso-8859-1) 107 115 (charset iso-8859-1) 116 123 (charset iso-8859-1) 124 129 (charset iso-8859-1) 130 137 (charset iso-8859-1) 138 148 (charset iso-8859-1) 149 152 (charset iso-8859-1) 153 155 (charset iso-8859-1) 156 159 (charset iso-8859-1) 160 167 (charset iso-8859-1) 168 173 (charset iso-8859-1) 174 180 (charset iso-8859-1) 181 183 (charset iso-8859-1) 184 192 (charset iso-8859-1) 193 200 (charset iso-8859-1) 201 205 (charset iso-8859-1) 206 208 (charset iso-8859-1) 209 217 (charset iso-8859-1) 218 221 (charset iso-8859-1) 222 227 (charset iso-8859-1) 228 232 (charset iso-8859-1) 233 236 (charset iso-8859-1) 237 246 (charset iso-8859-1) 247 256 (charset iso-8859-1) 257 259 (charset iso-8859-1) 260 261 (charset iso-8859-1) 262 269 (charset iso-8859-1) 270 273 (charset iso-8859-1) 274 281 (charset iso-8859-1) 282 287 (charset iso-8859-1) 288 291 (charset iso-8859-1) 292 296 (charset iso-8859-1) 297 303 (charset iso-8859-1) 304 306 (charset iso-8859-1) 307 310 (charset iso-8859-1) 311 318 (charset iso-8859-1)))))))) (texts #("Veta mer om inlägg" 0 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 18 (charset iso-8859-1)) (section ((id . "texts") (prompt . #("Veta mer om inlägg" 0 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 18 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Inlägg" 0 6 (charset iso-8859-1))))) (p nil ((TEXT . #(" Inlägg är LysKOMs syfte. Inlägg är helt enkelt texter skrivna av någon medlen i LysKOM. " 1 7 (charset iso-8859-1) 8 10 (charset iso-8859-1) 11 18 (charset iso-8859-1) 19 25 (charset iso-8859-1) 26 32 (charset iso-8859-1) 33 35 (charset iso-8859-1) 36 40 (charset iso-8859-1) 41 47 (charset iso-8859-1) 48 54 (charset iso-8859-1) 55 62 (charset iso-8859-1) 63 65 (charset iso-8859-1) 66 71 (charset iso-8859-1) 72 78 (charset iso-8859-1) 79 80 (charset iso-8859-1) 81 88 (charset iso-8859-1))))) (h2 nil ((TEXT . "Mottagare"))) (p nil ((TEXT . #(" Varje inlägg har en eller flera mottagare; möten som inlägget är skickat till. Mottagare finns i tre olika smaker: vanlig mottagare, extra-kopiemottagare och dold mottagare. " 1 6 (charset iso-8859-1) 7 13 (charset iso-8859-1) 14 17 (charset iso-8859-1) 18 20 (charset iso-8859-1) 21 26 (charset iso-8859-1) 27 32 (charset iso-8859-1) 33 43 (charset iso-8859-1) 44 49 (charset iso-8859-1) 50 53 (charset iso-8859-1) 54 62 (charset iso-8859-1) 63 65 (charset iso-8859-1) 66 73 (charset iso-8859-1) 74 79 (charset iso-8859-1) 80 89 (charset iso-8859-1) 90 95 (charset iso-8859-1) 96 97 (charset iso-8859-1) 98 101 (charset iso-8859-1) 102 107 (charset iso-8859-1) 108 115 (charset iso-8859-1) 116 122 (charset iso-8859-1) 123 133 (charset iso-8859-1) 134 154 (charset iso-8859-1) 155 158 (charset iso-8859-1) 159 163 (charset iso-8859-1) 164 174 (charset iso-8859-1))))) (p nil ((TEXT . " ") (b nil ((TEXT . "Vanlig mottagare"))) (TEXT . #(" är det normala. En vanlig mottagare betyder att inlägget hör hemma i det angivna mötet (eller i alla angivna möten). Kommentarer till inlägget kommer automatiskt att skickas till samma mottagare. " 1 3 (charset iso-8859-1) 4 7 (charset iso-8859-1) 8 16 (charset iso-8859-1) 17 19 (charset iso-8859-1) 20 26 (charset iso-8859-1) 27 36 (charset iso-8859-1) 37 44 (charset iso-8859-1) 45 48 (charset iso-8859-1) 49 57 (charset iso-8859-1) 58 61 (charset iso-8859-1) 62 67 (charset iso-8859-1) 68 69 (charset iso-8859-1) 70 73 (charset iso-8859-1) 74 81 (charset iso-8859-1) 82 87 (charset iso-8859-1) 88 94 (charset iso-8859-1) 95 96 (charset iso-8859-1) 97 101 (charset iso-8859-1) 102 109 (charset iso-8859-1) 110 117 (charset iso-8859-1) 118 129 (charset iso-8859-1) 130 134 (charset iso-8859-1) 135 143 (charset iso-8859-1) 144 150 (charset iso-8859-1) 151 162 (charset iso-8859-1) 163 166 (charset iso-8859-1) 167 174 (charset iso-8859-1) 175 179 (charset iso-8859-1) 180 185 (charset iso-8859-1) 186 196 (charset iso-8859-1))))) (p nil ((TEXT . " ") (b nil ((TEXT . "Extra-kopiemottagare"))) (TEXT . #(" används när man vill skicka en kopia av ett inlägg till ett möte utan att kommentarer till inlägget skall hamna i mötet. " 1 8 (charset iso-8859-1) 9 12 (charset iso-8859-1) 13 16 (charset iso-8859-1) 17 21 (charset iso-8859-1) 22 28 (charset iso-8859-1) 29 31 (charset iso-8859-1) 32 37 (charset iso-8859-1) 38 40 (charset iso-8859-1) 41 44 (charset iso-8859-1) 45 51 (charset iso-8859-1) 52 56 (charset iso-8859-1) 57 60 (charset iso-8859-1) 61 65 (charset iso-8859-1) 66 70 (charset iso-8859-1) 71 74 (charset iso-8859-1) 75 86 (charset iso-8859-1) 87 91 (charset iso-8859-1) 92 100 (charset iso-8859-1) 101 106 (charset iso-8859-1) 107 112 (charset iso-8859-1) 113 114 (charset iso-8859-1) 115 121 (charset iso-8859-1))))) (p nil ((TEXT . " ") (b nil ((TEXT . "Dold mottagare"))) (TEXT . #(" är ganska ovanligt. Bara den som har rätt att vara medlem i mötet som är dold mottagare kan överhuvudtaget se den dolda mottagaren. Detta kan alltså användas för att skicka inlägg till någons brevlåda eller till ett slutet möte utan att andra kan se det. " 1 3 (charset iso-8859-1) 4 10 (charset iso-8859-1) 11 20 (charset iso-8859-1) 21 25 (charset iso-8859-1) 26 29 (charset iso-8859-1) 30 33 (charset iso-8859-1) 34 37 (charset iso-8859-1) 38 42 (charset iso-8859-1) 43 46 (charset iso-8859-1) 47 51 (charset iso-8859-1) 52 58 (charset iso-8859-1) 59 60 (charset iso-8859-1) 61 66 (charset iso-8859-1) 67 70 (charset iso-8859-1) 71 73 (charset iso-8859-1) 74 78 (charset iso-8859-1) 79 88 (charset iso-8859-1) 89 92 (charset iso-8859-1) 93 107 (charset iso-8859-1) 108 110 (charset iso-8859-1) 111 114 (charset iso-8859-1) 115 120 (charset iso-8859-1) 121 132 (charset iso-8859-1) 133 138 (charset iso-8859-1) 139 142 (charset iso-8859-1) 143 149 (charset iso-8859-1) 150 158 (charset iso-8859-1) 159 162 (charset iso-8859-1) 163 166 (charset iso-8859-1) 167 173 (charset iso-8859-1) 174 180 (charset iso-8859-1) 181 185 (charset iso-8859-1) 186 192 (charset iso-8859-1) 193 201 (charset iso-8859-1) 202 207 (charset iso-8859-1) 208 212 (charset iso-8859-1) 213 216 (charset iso-8859-1) 217 223 (charset iso-8859-1) 224 228 (charset iso-8859-1) 229 233 (charset iso-8859-1) 234 237 (charset iso-8859-1) 238 243 (charset iso-8859-1) 244 247 (charset iso-8859-1) 248 250 (charset iso-8859-1) 251 255 (charset iso-8859-1))))) (h2 nil ((TEXT . "Kommentarer"))) (p nil ((TEXT . #(" Ett inlägg kan ha kommentarer. Kommentarer till kommentarer till kommentarer bildar trådar, kommentarskedjor, kommentarsträd och inläggsträd. Vem som helst kan skriva kommentarer till vilket inlägg som helst. Det finns inget krav på att kommentarer ska skickas till samma möten som det som kommenteras. Det är till exempel rätt vanligt med kommentarer som bara skickas till författaren för det kommenterade inlägget. " 1 4 (charset iso-8859-1) 5 11 (charset iso-8859-1) 12 15 (charset iso-8859-1) 16 18 (charset iso-8859-1) 19 31 (charset iso-8859-1) 32 43 (charset iso-8859-1) 44 48 (charset iso-8859-1) 49 60 (charset iso-8859-1) 61 65 (charset iso-8859-1) 66 77 (charset iso-8859-1) 78 84 (charset iso-8859-1) 85 92 (charset iso-8859-1) 93 110 (charset iso-8859-1) 111 125 (charset iso-8859-1) 126 129 (charset iso-8859-1) 130 142 (charset iso-8859-1) 143 146 (charset iso-8859-1) 147 150 (charset iso-8859-1) 151 156 (charset iso-8859-1) 157 160 (charset iso-8859-1) 161 167 (charset iso-8859-1) 168 179 (charset iso-8859-1) 180 184 (charset iso-8859-1) 185 191 (charset iso-8859-1) 192 198 (charset iso-8859-1) 199 202 (charset iso-8859-1) 203 209 (charset iso-8859-1) 210 213 (charset iso-8859-1) 214 219 (charset iso-8859-1) 220 225 (charset iso-8859-1) 226 230 (charset iso-8859-1) 231 233 (charset iso-8859-1) 234 237 (charset iso-8859-1) 238 249 (charset iso-8859-1) 250 253 (charset iso-8859-1) 254 261 (charset iso-8859-1) 262 266 (charset iso-8859-1) 267 272 (charset iso-8859-1) 273 278 (charset iso-8859-1) 279 282 (charset iso-8859-1) 283 286 (charset iso-8859-1) 287 290 (charset iso-8859-1) 291 303 (charset iso-8859-1) 304 307 (charset iso-8859-1) 308 310 (charset iso-8859-1) 311 315 (charset iso-8859-1) 316 323 (charset iso-8859-1) 324 328 (charset iso-8859-1) 329 336 (charset iso-8859-1) 337 340 (charset iso-8859-1) 341 352 (charset iso-8859-1) 353 356 (charset iso-8859-1) 357 361 (charset iso-8859-1) 362 369 (charset iso-8859-1) 370 374 (charset iso-8859-1) 375 386 (charset iso-8859-1) 387 390 (charset iso-8859-1) 391 394 (charset iso-8859-1) 395 407 (charset iso-8859-1) 408 417 (charset iso-8859-1))))) (h2 nil ((TEXT . #("Speciella inlägg" 0 9 (charset iso-8859-1) 10 16 (charset iso-8859-1))))) (p nil ((TEXT . #(" Det finns tre speciella inläggstyper: presentationer, FAQer och Lappar. En presentation är ett inlägg kopplat till ett möte eller en brevlåda som innehåller en presentation av mötet eller personen. Dessa ligger normalt i speciella möten. En FAQ är ett inlägg som svarar på frågor som ofta ställs i mötet. Varje FAQ är kopplad till ett eller flera möten, och man erbjuds att läsa nya FAQer så fort elispklienten ser att de har skapats. Lappar är inlägg som är kopplade till möten, brevlådor eller till hela servern som innehåller viktig information. Till exempel är det vanligt att tala om att man reser bort och inte kan läsa LysKOM genom att sätta en lapp på sin brevlåda. " 1 4 (charset iso-8859-1) 5 10 (charset iso-8859-1) 11 14 (charset iso-8859-1) 15 24 (charset iso-8859-1) 25 38 (charset iso-8859-1) 39 54 (charset iso-8859-1) 55 60 (charset iso-8859-1) 61 64 (charset iso-8859-1) 65 72 (charset iso-8859-1) 73 75 (charset iso-8859-1) 76 88 (charset iso-8859-1) 89 91 (charset iso-8859-1) 92 95 (charset iso-8859-1) 96 102 (charset iso-8859-1) 103 110 (charset iso-8859-1) 111 115 (charset iso-8859-1) 116 119 (charset iso-8859-1) 120 124 (charset iso-8859-1) 125 130 (charset iso-8859-1) 131 133 (charset iso-8859-1) 134 142 (charset iso-8859-1) 143 146 (charset iso-8859-1) 147 157 (charset iso-8859-1) 158 160 (charset iso-8859-1) 161 173 (charset iso-8859-1) 174 176 (charset iso-8859-1) 177 182 (charset iso-8859-1) 183 188 (charset iso-8859-1) 189 198 (charset iso-8859-1) 199 204 (charset iso-8859-1) 205 211 (charset iso-8859-1) 212 219 (charset iso-8859-1) 220 221 (charset iso-8859-1) 222 231 (charset iso-8859-1) 232 238 (charset iso-8859-1) 239 241 (charset iso-8859-1) 242 245 (charset iso-8859-1) 246 248 (charset iso-8859-1) 249 252 (charset iso-8859-1) 253 259 (charset iso-8859-1) 260 263 (charset iso-8859-1) 264 270 (charset iso-8859-1) 271 273 (charset iso-8859-1) 274 280 (charset iso-8859-1) 281 284 (charset iso-8859-1) 285 289 (charset iso-8859-1) 290 296 (charset iso-8859-1) 297 298 (charset iso-8859-1) 299 305 (charset iso-8859-1) 306 311 (charset iso-8859-1) 312 315 (charset iso-8859-1) 316 318 (charset iso-8859-1) 319 326 (charset iso-8859-1) 327 331 (charset iso-8859-1) 332 335 (charset iso-8859-1) 336 341 (charset iso-8859-1) 342 347 (charset iso-8859-1) 348 354 (charset iso-8859-1) 355 358 (charset iso-8859-1) 359 362 (charset iso-8859-1) 363 370 (charset iso-8859-1) 371 374 (charset iso-8859-1) 375 379 (charset iso-8859-1) 380 383 (charset iso-8859-1) 384 389 (charset iso-8859-1) 390 392 (charset iso-8859-1) 393 397 (charset iso-8859-1) 398 411 (charset iso-8859-1) 412 415 (charset iso-8859-1) 416 419 (charset iso-8859-1) 420 422 (charset iso-8859-1) 423 426 (charset iso-8859-1) 427 435 (charset iso-8859-1) 436 442 (charset iso-8859-1) 443 445 (charset iso-8859-1) 446 452 (charset iso-8859-1) 453 456 (charset iso-8859-1) 457 459 (charset iso-8859-1) 460 468 (charset iso-8859-1) 469 473 (charset iso-8859-1) 474 480 (charset iso-8859-1) 481 490 (charset iso-8859-1) 491 496 (charset iso-8859-1) 497 501 (charset iso-8859-1) 502 506 (charset iso-8859-1) 507 514 (charset iso-8859-1) 515 518 (charset iso-8859-1) 519 529 (charset iso-8859-1) 530 536 (charset iso-8859-1) 537 549 (charset iso-8859-1) 550 554 (charset iso-8859-1) 555 562 (charset iso-8859-1) 563 565 (charset iso-8859-1) 566 569 (charset iso-8859-1) 570 577 (charset iso-8859-1) 578 581 (charset iso-8859-1) 582 586 (charset iso-8859-1) 587 589 (charset iso-8859-1) 590 593 (charset iso-8859-1) 594 597 (charset iso-8859-1) 598 603 (charset iso-8859-1) 604 608 (charset iso-8859-1) 609 612 (charset iso-8859-1) 613 617 (charset iso-8859-1) 618 621 (charset iso-8859-1) 622 626 (charset iso-8859-1) 627 633 (charset iso-8859-1) 634 639 (charset iso-8859-1) 640 643 (charset iso-8859-1) 644 649 (charset iso-8859-1) 650 652 (charset iso-8859-1) 653 657 (charset iso-8859-1) 658 660 (charset iso-8859-1) 661 664 (charset iso-8859-1) 665 674 (charset iso-8859-1)))))))) (settings #("Veta mer om inställningar" 0 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 25 (charset iso-8859-1)) (section ((id . "settings") (prompt . #("Veta mer om inställningar" 0 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 25 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Inställningar" 0 13 (charset iso-8859-1))))) (p nil ((TEXT . #(" Elispklienten har ett stort antal inställningar. Med några få undantag kan man ändra alla via kommandot " 1 14 (charset iso-8859-1) 15 18 (charset iso-8859-1) 19 22 (charset iso-8859-1) 23 28 (charset iso-8859-1) 29 34 (charset iso-8859-1) 35 49 (charset iso-8859-1) 50 53 (charset iso-8859-1) 54 59 (charset iso-8859-1) 60 62 (charset iso-8859-1) 63 71 (charset iso-8859-1) 72 75 (charset iso-8859-1) 76 79 (charset iso-8859-1) 80 85 (charset iso-8859-1) 86 90 (charset iso-8859-1) 91 94 (charset iso-8859-1) 95 104 (charset iso-8859-1))) (cref ((id . "kom-customize")) nil) (TEXT . ". "))) (p nil ((TEXT . #(" Inställningar kan sparas antingen i servern eller i din .emacs. Inställningar som sparas i servern gäller per användare och endast för en session. Inställningar som sparas i .emacs gäller i alla sessioner, oavsett om samma inställningar även har sparats i servern. " 1 14 (charset iso-8859-1) 15 18 (charset iso-8859-1) 19 25 (charset iso-8859-1) 26 34 (charset iso-8859-1) 35 36 (charset iso-8859-1) 37 44 (charset iso-8859-1) 45 50 (charset iso-8859-1) 51 52 (charset iso-8859-1) 53 56 (charset iso-8859-1) 57 64 (charset iso-8859-1) 65 78 (charset iso-8859-1) 79 82 (charset iso-8859-1) 83 89 (charset iso-8859-1) 90 91 (charset iso-8859-1) 92 99 (charset iso-8859-1) 100 106 (charset iso-8859-1) 107 110 (charset iso-8859-1) 111 120 (charset iso-8859-1) 121 124 (charset iso-8859-1) 125 131 (charset iso-8859-1) 132 135 (charset iso-8859-1) 136 138 (charset iso-8859-1) 139 147 (charset iso-8859-1) 148 161 (charset iso-8859-1) 162 165 (charset iso-8859-1) 166 172 (charset iso-8859-1) 173 174 (charset iso-8859-1) 175 181 (charset iso-8859-1) 182 188 (charset iso-8859-1) 189 190 (charset iso-8859-1) 191 195 (charset iso-8859-1) 196 206 (charset iso-8859-1) 207 214 (charset iso-8859-1) 215 217 (charset iso-8859-1) 218 223 (charset iso-8859-1) 224 237 (charset iso-8859-1) 238 242 (charset iso-8859-1) 243 246 (charset iso-8859-1) 247 254 (charset iso-8859-1) 255 256 (charset iso-8859-1) 257 265 (charset iso-8859-1)))))))) (reading #("Läsa inlägg" 0 4 (charset iso-8859-1) 5 11 (charset iso-8859-1)) (section ((id . "reading") (prompt . #("Läsa inlägg" 0 4 (charset iso-8859-1) 5 11 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Att läsa inlägg" 0 3 (charset iso-8859-1) 4 8 (charset iso-8859-1) 9 15 (charset iso-8859-1))))) (p nil ((TEXT . #(" För att bara läsa inlägg i den ordning de presenteras i LysKOM räcker det att trycka på SPC. Ett inlägg i taget kommer att visas tills det inte finns några kvar. Det finns även ett antal mer avancerade kommandon. Använd kommandot " 1 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 18 (charset iso-8859-1) 19 25 (charset iso-8859-1) 26 27 (charset iso-8859-1) 28 31 (charset iso-8859-1) 32 39 (charset iso-8859-1) 40 42 (charset iso-8859-1) 43 54 (charset iso-8859-1) 55 56 (charset iso-8859-1) 57 63 (charset iso-8859-1) 64 70 (charset iso-8859-1) 71 74 (charset iso-8859-1) 75 78 (charset iso-8859-1) 79 85 (charset iso-8859-1) 86 88 (charset iso-8859-1) 89 93 (charset iso-8859-1) 94 97 (charset iso-8859-1) 98 104 (charset iso-8859-1) 105 106 (charset iso-8859-1) 107 112 (charset iso-8859-1) 113 119 (charset iso-8859-1) 120 123 (charset iso-8859-1) 124 129 (charset iso-8859-1) 130 135 (charset iso-8859-1) 136 139 (charset iso-8859-1) 140 144 (charset iso-8859-1) 145 150 (charset iso-8859-1) 151 156 (charset iso-8859-1) 157 162 (charset iso-8859-1) 163 166 (charset iso-8859-1) 167 172 (charset iso-8859-1) 173 177 (charset iso-8859-1) 178 181 (charset iso-8859-1) 182 187 (charset iso-8859-1) 188 191 (charset iso-8859-1) 192 202 (charset iso-8859-1) 203 213 (charset iso-8859-1) 214 220 (charset iso-8859-1) 221 230 (charset iso-8859-1))) (cref ((id . "kom-list-summary")) nil) (TEXT . #(" för att se vilka inlägg som ligger på kö för att läsas. " 1 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 17 (charset iso-8859-1) 18 24 (charset iso-8859-1) 25 28 (charset iso-8859-1) 29 35 (charset iso-8859-1) 36 38 (charset iso-8859-1) 39 41 (charset iso-8859-1) 42 45 (charset iso-8859-1) 46 49 (charset iso-8859-1) 50 56 (charset iso-8859-1))))) (list ((header . #("Se även:" 0 2 (charset iso-8859-1) 3 8 (charset iso-8859-1)))) ((item nil ((refer ((id . "filter")) nil))) (item nil ((refer ((id . "review")) nil))) (item nil ((refer ((id . "mark")) nil)))))))) (filter #("Slippa läsa inlägg" 0 6 (charset iso-8859-1) 7 11 (charset iso-8859-1) 12 18 (charset iso-8859-1)) (section ((id . "filter") (prompt . #("Slippa läsa inlägg" 0 6 (charset iso-8859-1) 7 11 (charset iso-8859-1) 12 18 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Kommandon för att slippa läsa inlägg" 0 9 (charset iso-8859-1) 10 13 (charset iso-8859-1) 14 17 (charset iso-8859-1) 18 24 (charset iso-8859-1) 25 29 (charset iso-8859-1) 30 36 (charset iso-8859-1))))) (p nil ((TEXT . #(" Ibland vill man inte läsa en del inlägg. Då kan följande kommandon komma väl till pass. Förutom dessa finns ett antal kommandon för att filtrera inlägg, om det är någon typ av inlägg som man aldrig vill se. " 1 7 (charset iso-8859-1) 8 12 (charset iso-8859-1) 13 16 (charset iso-8859-1) 17 21 (charset iso-8859-1) 22 26 (charset iso-8859-1) 27 29 (charset iso-8859-1) 30 33 (charset iso-8859-1) 34 41 (charset iso-8859-1) 42 44 (charset iso-8859-1) 45 48 (charset iso-8859-1) 49 57 (charset iso-8859-1) 58 67 (charset iso-8859-1) 68 73 (charset iso-8859-1) 74 77 (charset iso-8859-1) 78 82 (charset iso-8859-1) 83 88 (charset iso-8859-1) 89 96 (charset iso-8859-1) 97 102 (charset iso-8859-1) 103 108 (charset iso-8859-1) 109 112 (charset iso-8859-1) 113 118 (charset iso-8859-1) 119 128 (charset iso-8859-1) 129 132 (charset iso-8859-1) 133 136 (charset iso-8859-1) 137 145 (charset iso-8859-1) 146 153 (charset iso-8859-1) 154 156 (charset iso-8859-1) 157 160 (charset iso-8859-1) 161 163 (charset iso-8859-1) 164 169 (charset iso-8859-1) 170 173 (charset iso-8859-1) 174 176 (charset iso-8859-1) 177 183 (charset iso-8859-1) 184 187 (charset iso-8859-1) 188 191 (charset iso-8859-1) 192 198 (charset iso-8859-1) 199 203 (charset iso-8859-1) 204 207 (charset iso-8859-1))))) (inline ((id . "kom-set-unread")) nil) (inline ((id . "kom-jump")) nil) (inline ((id . "kom-super-jump")) nil)))) (mark #("Komma ihåg inlägg" 0 5 (charset iso-8859-1) 6 10 (charset iso-8859-1) 11 17 (charset iso-8859-1)) (section ((id . "mark") (prompt . #("Komma ihåg inlägg" 0 5 (charset iso-8859-1) 6 10 (charset iso-8859-1) 11 17 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Kommandon för att komma ihåg inlägg" 0 9 (charset iso-8859-1) 10 13 (charset iso-8859-1) 14 17 (charset iso-8859-1) 18 23 (charset iso-8859-1) 24 28 (charset iso-8859-1) 29 35 (charset iso-8859-1))))) (p nil ((TEXT . #(" Ibland hittar man ett inlägg som man vill komma ihåg. Då kan man markera inlägget. I grund och botten är varje markering ett heltal mellan 0 och 255, men med elispklienten kan man ge ett namn till varje typ av markering. " 1 7 (charset iso-8859-1) 8 14 (charset iso-8859-1) 15 18 (charset iso-8859-1) 19 22 (charset iso-8859-1) 23 29 (charset iso-8859-1) 30 33 (charset iso-8859-1) 34 37 (charset iso-8859-1) 38 42 (charset iso-8859-1) 43 48 (charset iso-8859-1) 49 54 (charset iso-8859-1) 55 57 (charset iso-8859-1) 58 61 (charset iso-8859-1) 62 65 (charset iso-8859-1) 66 73 (charset iso-8859-1) 74 83 (charset iso-8859-1) 84 85 (charset iso-8859-1) 86 91 (charset iso-8859-1) 92 95 (charset iso-8859-1) 96 102 (charset iso-8859-1) 103 105 (charset iso-8859-1) 106 111 (charset iso-8859-1) 112 121 (charset iso-8859-1) 122 125 (charset iso-8859-1) 126 132 (charset iso-8859-1) 133 139 (charset iso-8859-1) 140 141 (charset iso-8859-1) 142 145 (charset iso-8859-1) 146 150 (charset iso-8859-1) 151 154 (charset iso-8859-1) 155 158 (charset iso-8859-1) 159 172 (charset iso-8859-1) 173 176 (charset iso-8859-1) 177 180 (charset iso-8859-1) 181 183 (charset iso-8859-1) 184 187 (charset iso-8859-1) 188 192 (charset iso-8859-1) 193 197 (charset iso-8859-1) 198 203 (charset iso-8859-1) 204 207 (charset iso-8859-1) 208 210 (charset iso-8859-1) 211 221 (charset iso-8859-1))))) (inline ((id . "kom-mark-text")) nil) (inline ((id . "kom-unmark-text")) nil) (inline ((id . "kom-review-all-marked-texts")) nil) (inline ((id . "kom-review-marked-texts")) nil) (inline ((id . "kom-list-marks")) nil)))) (review #("Hitta gamla inlägg" 0 5 (charset iso-8859-1) 6 11 (charset iso-8859-1) 12 18 (charset iso-8859-1)) (section ((id . "review") (prompt . #("Hitta gamla inlägg" 0 5 (charset iso-8859-1) 6 11 (charset iso-8859-1) 12 18 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Kommandon för att hitta gamla inlägg" 0 9 (charset iso-8859-1) 10 13 (charset iso-8859-1) 14 17 (charset iso-8859-1) 18 23 (charset iso-8859-1) 24 29 (charset iso-8859-1) 30 36 (charset iso-8859-1))))) (p nil ((TEXT . #(" Ibland vill man hitta gamla inlägg. Det finns ett stort antal kommadon för att göra detta, och alla har namn som börjar med \"Återse\". " 1 7 (charset iso-8859-1) 8 12 (charset iso-8859-1) 13 16 (charset iso-8859-1) 17 22 (charset iso-8859-1) 23 28 (charset iso-8859-1) 29 36 (charset iso-8859-1) 37 40 (charset iso-8859-1) 41 46 (charset iso-8859-1) 47 50 (charset iso-8859-1) 51 56 (charset iso-8859-1) 57 62 (charset iso-8859-1) 63 71 (charset iso-8859-1) 72 75 (charset iso-8859-1) 76 79 (charset iso-8859-1) 80 84 (charset iso-8859-1) 85 91 (charset iso-8859-1) 92 95 (charset iso-8859-1) 96 100 (charset iso-8859-1) 101 104 (charset iso-8859-1) 105 109 (charset iso-8859-1) 110 113 (charset iso-8859-1) 114 120 (charset iso-8859-1) 121 124 (charset iso-8859-1) 125 134 (charset iso-8859-1))))) (inline ((id . "kom-review-by-to")) nil) (inline ((id . "kom-review-first")) nil) (inline ((id . "kom-review-more")) nil) (inline ((id . "kom-review-clear")) nil) (inline ((id . "kom-view-commented-text")) nil) (inline ((id . "kom-review-comments")) nil) (inline ((id . "kom-find-root-review")) nil) (inline ((id . "kom-find-root")) nil) (inline ((id . "kom-review-tree")) nil)))) (writing #("Skriva eller ändra inlägg" 0 6 (charset iso-8859-1) 7 12 (charset iso-8859-1) 13 18 (charset iso-8859-1) 19 25 (charset iso-8859-1)) (section ((id . "writing") (prompt . #("Skriva eller ändra inlägg" 0 6 (charset iso-8859-1) 7 12 (charset iso-8859-1) 13 18 (charset iso-8859-1) 19 25 (charset iso-8859-1)))) ((h1 nil ((TEXT . #("Att skriva eller ändra inlägg" 0 3 (charset iso-8859-1) 4 10 (charset iso-8859-1) 11 16 (charset iso-8859-1) 17 22 (charset iso-8859-1) 23 29 (charset iso-8859-1))))) (p nil ((TEXT . #(" Det finns i grund och botten tre sorters inlägg: urinlägg, kommentarer och fotnoter. Alla är inlägg, men de hänger ihop med andra inlägg på olika sätt. " 1 4 (charset iso-8859-1) 5 10 (charset iso-8859-1) 11 12 (charset iso-8859-1) 13 18 (charset iso-8859-1) 19 22 (charset iso-8859-1) 23 29 (charset iso-8859-1) 30 33 (charset iso-8859-1) 34 41 (charset iso-8859-1) 42 49 (charset iso-8859-1) 50 59 (charset iso-8859-1) 60 71 (charset iso-8859-1) 72 75 (charset iso-8859-1) 76 85 (charset iso-8859-1) 86 90 (charset iso-8859-1) 91 93 (charset iso-8859-1) 94 101 (charset iso-8859-1) 102 105 (charset iso-8859-1) 106 108 (charset iso-8859-1) 109 115 (charset iso-8859-1) 116 120 (charset iso-8859-1) 121 124 (charset iso-8859-1) 125 130 (charset iso-8859-1) 131 137 (charset iso-8859-1) 138 140 (charset iso-8859-1) 141 146 (charset iso-8859-1) 147 152 (charset iso-8859-1))))) (h2 nil ((TEXT . #("Urinlägg" 0 8 (charset iso-8859-1))))) (p nil ((TEXT . #(" Urinlägg är inlägg som inte är kommentarer till något annat inlägg. Dessa inleder i allmänhet nya diskussioner. Kom ihåg att skriva en vettig ärenderad när du skriver nya urinlägg. Brev är vanliga inlägg som skickas till någons brevlåda. " 1 9 (charset iso-8859-1) 10 12 (charset iso-8859-1) 13 19 (charset iso-8859-1) 20 23 (charset iso-8859-1) 24 28 (charset iso-8859-1) 29 31 (charset iso-8859-1) 32 43 (charset iso-8859-1) 44 48 (charset iso-8859-1) 49 54 (charset iso-8859-1) 55 60 (charset iso-8859-1) 61 68 (charset iso-8859-1) 69 74 (charset iso-8859-1) 75 82 (charset iso-8859-1) 83 84 (charset iso-8859-1) 85 94 (charset iso-8859-1) 95 98 (charset iso-8859-1) 99 112 (charset iso-8859-1) 113 116 (charset iso-8859-1) 117 121 (charset iso-8859-1) 122 125 (charset iso-8859-1) 126 132 (charset iso-8859-1) 133 135 (charset iso-8859-1) 136 142 (charset iso-8859-1) 143 152 (charset iso-8859-1) 153 156 (charset iso-8859-1) 157 159 (charset iso-8859-1) 160 167 (charset iso-8859-1) 168 171 (charset iso-8859-1) 172 181 (charset iso-8859-1) 182 186 (charset iso-8859-1) 187 189 (charset iso-8859-1) 190 197 (charset iso-8859-1) 198 204 (charset iso-8859-1) 205 208 (charset iso-8859-1) 209 216 (charset iso-8859-1) 217 221 (charset iso-8859-1) 222 228 (charset iso-8859-1) 229 238 (charset iso-8859-1))))) (inline ((id . "kom-write-text")) nil) (inline ((id . "kom-send-letter")) nil) (h2 nil ((TEXT . "Kommentarer"))) (p nil ((TEXT . #(" Kommentarer är, som det låter, kommentarer till andra inlägg. När du skriver en kommentar, kolla att ärenderaden fortfarande är vettig, och kolla att mottagarna till inlägget är vettiga. Ändra det som inte är vettigt. Personliga svar är kommentarer som skickas till författarens brevlåda. Det är egentligen bara vanliga kommentarer där man har bytt mottagare. " 1 12 (charset iso-8859-1) 13 16 (charset iso-8859-1) 17 20 (charset iso-8859-1) 21 24 (charset iso-8859-1) 25 31 (charset iso-8859-1) 32 43 (charset iso-8859-1) 44 48 (charset iso-8859-1) 49 54 (charset iso-8859-1) 55 62 (charset iso-8859-1) 63 66 (charset iso-8859-1) 67 69 (charset iso-8859-1) 70 77 (charset iso-8859-1) 78 80 (charset iso-8859-1) 81 91 (charset iso-8859-1) 92 97 (charset iso-8859-1) 98 101 (charset iso-8859-1) 102 113 (charset iso-8859-1) 114 125 (charset iso-8859-1) 126 128 (charset iso-8859-1) 129 136 (charset iso-8859-1) 137 140 (charset iso-8859-1) 141 146 (charset iso-8859-1) 147 150 (charset iso-8859-1) 151 161 (charset iso-8859-1) 162 166 (charset iso-8859-1) 167 175 (charset iso-8859-1) 176 178 (charset iso-8859-1) 179 187 (charset iso-8859-1) 188 193 (charset iso-8859-1) 194 197 (charset iso-8859-1) 198 201 (charset iso-8859-1) 202 206 (charset iso-8859-1) 207 209 (charset iso-8859-1) 210 218 (charset iso-8859-1) 219 229 (charset iso-8859-1) 230 234 (charset iso-8859-1) 235 237 (charset iso-8859-1) 238 249 (charset iso-8859-1) 250 253 (charset iso-8859-1) 254 261 (charset iso-8859-1) 262 266 (charset iso-8859-1) 267 279 (charset iso-8859-1) 280 289 (charset iso-8859-1) 290 293 (charset iso-8859-1) 294 296 (charset iso-8859-1) 297 307 (charset iso-8859-1) 308 312 (charset iso-8859-1) 313 320 (charset iso-8859-1) 321 332 (charset iso-8859-1) 333 336 (charset iso-8859-1) 337 340 (charset iso-8859-1) 341 344 (charset iso-8859-1) 345 349 (charset iso-8859-1) 350 360 (charset iso-8859-1))))) (inline ((id . "kom-write-comment")) nil) (inline ((id . "kom-private-answer")) nil) (h2 nil ((TEXT . "Fotnoter"))) (p nil ((TEXT . #(" Fotnoter är speciella kommentarer som bara författaren till ett inlägg kan skriva. De visas före vanliga kommentarer och är bra när man vill klargöra något man har skrivit i ett inlägg så att det visas innan folk börjar kommentera inlägget. " 1 9 (charset iso-8859-1) 10 12 (charset iso-8859-1) 13 22 (charset iso-8859-1) 23 34 (charset iso-8859-1) 35 38 (charset iso-8859-1) 39 43 (charset iso-8859-1) 44 55 (charset iso-8859-1) 56 60 (charset iso-8859-1) 61 64 (charset iso-8859-1) 65 71 (charset iso-8859-1) 72 75 (charset iso-8859-1) 76 83 (charset iso-8859-1) 84 86 (charset iso-8859-1) 87 92 (charset iso-8859-1) 93 97 (charset iso-8859-1) 98 105 (charset iso-8859-1) 106 117 (charset iso-8859-1) 118 121 (charset iso-8859-1) 122 124 (charset iso-8859-1) 125 128 (charset iso-8859-1) 129 132 (charset iso-8859-1) 133 136 (charset iso-8859-1) 137 141 (charset iso-8859-1) 142 150 (charset iso-8859-1) 151 156 (charset iso-8859-1) 157 160 (charset iso-8859-1) 161 164 (charset iso-8859-1) 165 172 (charset iso-8859-1) 173 174 (charset iso-8859-1) 175 178 (charset iso-8859-1) 179 185 (charset iso-8859-1) 186 188 (charset iso-8859-1) 189 192 (charset iso-8859-1) 193 196 (charset iso-8859-1) 197 202 (charset iso-8859-1) 203 208 (charset iso-8859-1) 209 213 (charset iso-8859-1) 214 220 (charset iso-8859-1) 221 231 (charset iso-8859-1) 232 241 (charset iso-8859-1))))) (inline ((id . "kom-write-footnote")) nil) (h2 nil ((TEXT . "Prefixargument"))) (p nil ((TEXT . #(" Det finns flera sätt att ange vilket inlägg man vill kommentera eller skriva en fotnot till. När man ger något av ovanstående kommando utan extra argument så gäller kommandot det senaste inlägg som man läste (för fotnoter det senaste man skrev eller läste som man var författare till). " 1 4 (charset iso-8859-1) 5 10 (charset iso-8859-1) 11 16 (charset iso-8859-1) 17 21 (charset iso-8859-1) 22 25 (charset iso-8859-1) 26 30 (charset iso-8859-1) 31 37 (charset iso-8859-1) 38 44 (charset iso-8859-1) 45 48 (charset iso-8859-1) 49 53 (charset iso-8859-1) 54 64 (charset iso-8859-1) 65 70 (charset iso-8859-1) 71 77 (charset iso-8859-1) 78 80 (charset iso-8859-1) 81 87 (charset iso-8859-1) 88 93 (charset iso-8859-1) 94 97 (charset iso-8859-1) 98 101 (charset iso-8859-1) 102 105 (charset iso-8859-1) 106 111 (charset iso-8859-1) 112 114 (charset iso-8859-1) 115 126 (charset iso-8859-1) 127 135 (charset iso-8859-1) 136 140 (charset iso-8859-1) 141 146 (charset iso-8859-1) 147 155 (charset iso-8859-1) 156 158 (charset iso-8859-1) 159 165 (charset iso-8859-1) 166 175 (charset iso-8859-1) 176 179 (charset iso-8859-1) 180 187 (charset iso-8859-1) 188 194 (charset iso-8859-1) 195 198 (charset iso-8859-1) 199 202 (charset iso-8859-1) 203 208 (charset iso-8859-1) 209 213 (charset iso-8859-1) 214 222 (charset iso-8859-1) 223 226 (charset iso-8859-1) 227 234 (charset iso-8859-1) 235 238 (charset iso-8859-1) 239 244 (charset iso-8859-1) 245 250 (charset iso-8859-1) 251 256 (charset iso-8859-1) 257 260 (charset iso-8859-1) 261 264 (charset iso-8859-1) 265 268 (charset iso-8859-1) 269 279 (charset iso-8859-1) 280 286 (charset iso-8859-1))))) (p nil ((TEXT . #(" Ibland vill man ha en annan text. Dels finns kommandon som kommenterar den näst sista text man läste (" 1 7 (charset iso-8859-1) 8 12 (charset iso-8859-1) 13 16 (charset iso-8859-1) 17 19 (charset iso-8859-1) 20 22 (charset iso-8859-1) 23 28 (charset iso-8859-1) 29 34 (charset iso-8859-1) 35 39 (charset iso-8859-1) 40 45 (charset iso-8859-1) 46 55 (charset iso-8859-1) 56 59 (charset iso-8859-1) 60 71 (charset iso-8859-1) 72 75 (charset iso-8859-1) 76 80 (charset iso-8859-1) 81 86 (charset iso-8859-1) 87 91 (charset iso-8859-1) 92 95 (charset iso-8859-1) 96 101 (charset iso-8859-1) 102 103 (charset iso-8859-1))) (cref ((id . "kom-comment-previous")) nil) (TEXT . ", ") (cref ((id . "kom-private-answer-previous")) nil) (TEXT . #("). Dessutom finns möjligheter att ge prefixargument till alla kommandon som hanterar inlägg. Ange prefixargument som vanligt i Emacs genom C-u följt av argumentets värde, eller genom att bara skriva in argumentets värde direkt. " 0 2 (charset iso-8859-1) 3 11 (charset iso-8859-1) 12 17 (charset iso-8859-1) 18 29 (charset iso-8859-1) 30 33 (charset iso-8859-1) 34 36 (charset iso-8859-1) 37 51 (charset iso-8859-1) 52 56 (charset iso-8859-1) 57 61 (charset iso-8859-1) 62 71 (charset iso-8859-1) 72 75 (charset iso-8859-1) 76 84 (charset iso-8859-1) 85 92 (charset iso-8859-1) 93 97 (charset iso-8859-1) 98 112 (charset iso-8859-1) 113 116 (charset iso-8859-1) 117 124 (charset iso-8859-1) 125 126 (charset iso-8859-1) 127 132 (charset iso-8859-1) 133 138 (charset iso-8859-1) 139 142 (charset iso-8859-1) 143 148 (charset iso-8859-1) 149 151 (charset iso-8859-1) 152 163 (charset iso-8859-1) 164 170 (charset iso-8859-1) 171 176 (charset iso-8859-1) 177 182 (charset iso-8859-1) 183 186 (charset iso-8859-1) 187 191 (charset iso-8859-1) 192 198 (charset iso-8859-1) 199 201 (charset iso-8859-1) 202 213 (charset iso-8859-1) 214 219 (charset iso-8859-1) 220 227 (charset iso-8859-1))))) (p nil ((TEXT . " Ange ett textnummer ") (i nil ((TEXT . "N"))) (TEXT . #(" genom att ge inläggsnumret som prefixargument. Ange inlägget som markören står i genom att ge prefixargument 0. Ange inlägget " 1 6 (charset iso-8859-1) 7 10 (charset iso-8859-1) 11 13 (charset iso-8859-1) 14 27 (charset iso-8859-1) 28 31 (charset iso-8859-1) 32 47 (charset iso-8859-1) 48 52 (charset iso-8859-1) 53 61 (charset iso-8859-1) 62 65 (charset iso-8859-1) 66 74 (charset iso-8859-1) 75 79 (charset iso-8859-1) 80 81 (charset iso-8859-1) 82 87 (charset iso-8859-1) 88 91 (charset iso-8859-1) 92 94 (charset iso-8859-1) 95 109 (charset iso-8859-1) 110 112 (charset iso-8859-1) 113 117 (charset iso-8859-1) 118 126 (charset iso-8859-1))) (i nil ((TEXT . "N"))) (TEXT . #(" inlägg bakåt i bufferten från där markören står genom att ge argument -" 1 7 (charset iso-8859-1) 8 13 (charset iso-8859-1) 14 15 (charset iso-8859-1) 16 25 (charset iso-8859-1) 26 30 (charset iso-8859-1) 31 34 (charset iso-8859-1) 35 43 (charset iso-8859-1) 44 48 (charset iso-8859-1) 49 54 (charset iso-8859-1) 55 58 (charset iso-8859-1) 59 61 (charset iso-8859-1) 62 70 (charset iso-8859-1) 71 72 (charset iso-8859-1))) (i nil ((TEXT . "N"))) (TEXT . #(". Få en prompt att mata in inläggsnumret vid genom att skriva C-u före kommandot. " 0 1 (charset iso-8859-1) 2 4 (charset iso-8859-1) 5 7 (charset iso-8859-1) 8 14 (charset iso-8859-1) 15 18 (charset iso-8859-1) 19 23 (charset iso-8859-1) 24 26 (charset iso-8859-1) 27 40 (charset iso-8859-1) 41 44 (charset iso-8859-1) 45 50 (charset iso-8859-1) 51 54 (charset iso-8859-1) 55 61 (charset iso-8859-1) 62 65 (charset iso-8859-1) 66 70 (charset iso-8859-1) 71 81 (charset iso-8859-1))))) (h2 nil ((TEXT . "Hantera mottagare"))) (p nil ((TEXT . #(" Ibland vill man ändra mottagare på ett inlägg. Till det finns kommandona " 1 7 (charset iso-8859-1) 8 12 (charset iso-8859-1) 13 16 (charset iso-8859-1) 17 22 (charset iso-8859-1) 23 32 (charset iso-8859-1) 33 35 (charset iso-8859-1) 36 39 (charset iso-8859-1) 40 47 (charset iso-8859-1) 48 52 (charset iso-8859-1) 53 56 (charset iso-8859-1) 57 62 (charset iso-8859-1) 63 73 (charset iso-8859-1))) (cref ((id . "kom-add-recipient")) nil) (TEXT . #(" för att lägga till och " 1 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 14 (charset iso-8859-1) 15 19 (charset iso-8859-1) 20 23 (charset iso-8859-1))) (cref ((id . "kom-sub-recipient")) nil) (TEXT . #(" för att ta bort mottagare. " 1 4 (charset iso-8859-1) 5 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 16 (charset iso-8859-1) 17 27 (charset iso-8859-1))))) (h2 nil ((TEXT . #("Meddelanden och anmärkningar" 0 11 (charset iso-8859-1) 12 15 (charset iso-8859-1) 16 28 (charset iso-8859-1))))) (p nil ((TEXT . #(" Använd aldrig meddelanden eller anmärkningar istället för vanliga inlägg. Det fungerar inte och folk blir sura. " 1 7 (charset iso-8859-1) 8 14 (charset iso-8859-1) 15 26 (charset iso-8859-1) 27 32 (charset iso-8859-1) 33 45 (charset iso-8859-1) 46 54 (charset iso-8859-1) 55 58 (charset iso-8859-1) 59 66 (charset iso-8859-1) 67 74 (charset iso-8859-1) 75 78 (charset iso-8859-1) 79 87 (charset iso-8859-1) 88 92 (charset iso-8859-1) 93 96 (charset iso-8859-1) 97 101 (charset iso-8859-1) 102 106 (charset iso-8859-1) 107 112 (charset iso-8859-1)))))))) (kom-list-conferences "" (section ((id . "kom-list-conferences") (prompt . "")) ((h3 nil ((cref ((id . "kom-list-conferences")) nil))) (p nil ((TEXT . #(" Lista möten som finns i systemet. Använd detta kommando med fantasifulla sökbegrepp för att hitta möten som du är intresserad av. " 1 6 (charset iso-8859-1) 7 12 (charset iso-8859-1) 13 16 (charset iso-8859-1) 17 22 (charset iso-8859-1) 23 24 (charset iso-8859-1) 25 34 (charset iso-8859-1) 35 41 (charset iso-8859-1) 42 47 (charset iso-8859-1) 48 56 (charset iso-8859-1) 57 60 (charset iso-8859-1) 61 73 (charset iso-8859-1) 74 84 (charset iso-8859-1) 85 88 (charset iso-8859-1) 89 92 (charset iso-8859-1) 93 98 (charset iso-8859-1) 99 104 (charset iso-8859-1) 105 108 (charset iso-8859-1) 109 111 (charset iso-8859-1) 112 114 (charset iso-8859-1) 115 126 (charset iso-8859-1) 127 130 (charset iso-8859-1)))))))) (kom-list-re "" (section ((id . "kom-list-re") (prompt . "")) ((h3 nil ((cref ((id . "kom-list-re")) nil))) (p nil ((TEXT . #(" Lista möten som finns i systemet. Använd detta kommando med fantasifulla sökbegrepp för att hitta möten som du är intresserad av. " 1 6 (charset iso-8859-1) 7 12 (charset iso-8859-1) 13 16 (charset iso-8859-1) 17 22 (charset iso-8859-1) 23 24 (charset iso-8859-1) 25 34 (charset iso-8859-1) 35 41 (charset iso-8859-1) 42 47 (charset iso-8859-1) 48 56 (charset iso-8859-1) 57 60 (charset iso-8859-1) 61 73 (charset iso-8859-1) 74 84 (charset iso-8859-1) 85 88 (charset iso-8859-1) 89 92 (charset iso-8859-1) 93 98 (charset iso-8859-1) 99 104 (charset iso-8859-1) 105 108 (charset iso-8859-1) 109 111 (charset iso-8859-1) 112 114 (charset iso-8859-1) 115 126 (charset iso-8859-1) 127 130 (charset iso-8859-1)))))))) (kom-review-presentation "" (section ((id . "kom-review-presentation") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-presentation")) nil))) (p nil ((TEXT . #(" Visa presentationen för ett möte eller en person. Använd detta för att få mer information om ett möte. " 1 5 (charset iso-8859-1) 6 20 (charset iso-8859-1) 21 24 (charset iso-8859-1) 25 28 (charset iso-8859-1) 29 33 (charset iso-8859-1) 34 39 (charset iso-8859-1) 40 42 (charset iso-8859-1) 43 50 (charset iso-8859-1) 51 57 (charset iso-8859-1) 58 63 (charset iso-8859-1) 64 67 (charset iso-8859-1) 68 71 (charset iso-8859-1) 72 74 (charset iso-8859-1) 75 78 (charset iso-8859-1) 79 90 (charset iso-8859-1) 91 93 (charset iso-8859-1) 94 97 (charset iso-8859-1) 98 103 (charset iso-8859-1)))))))) (kom-add-self "" (section ((id . "kom-add-self") (prompt . "")) ((h3 nil ((cref ((id . "kom-add-self")) nil))) (p nil ((TEXT . #(" Gå med i ett möte så att du kan läsa inläggen i mötet och så du får veta när det kommer nya inlägg. " 1 3 (charset iso-8859-1) 4 7 (charset iso-8859-1) 8 9 (charset iso-8859-1) 10 13 (charset iso-8859-1) 14 18 (charset iso-8859-1) 19 21 (charset iso-8859-1) 22 25 (charset iso-8859-1) 26 28 (charset iso-8859-1) 29 32 (charset iso-8859-1) 33 37 (charset iso-8859-1) 38 46 (charset iso-8859-1) 47 48 (charset iso-8859-1) 49 54 (charset iso-8859-1) 55 58 (charset iso-8859-1) 59 61 (charset iso-8859-1) 62 64 (charset iso-8859-1) 65 68 (charset iso-8859-1) 69 73 (charset iso-8859-1) 74 77 (charset iso-8859-1) 78 81 (charset iso-8859-1) 82 88 (charset iso-8859-1) 89 92 (charset iso-8859-1) 93 100 (charset iso-8859-1)))))))) (kom-go-to-conf "" (section ((id . "kom-go-to-conf") (prompt . "")) ((h3 nil ((cref ((id . "kom-go-to-conf")) nil))) (p nil ((TEXT . #(" Gå till ett möte som du vill läsa inlägg i. Om du inte redan är medlem i mötet så kommer du att erbjudas att bli medlem. " 1 3 (charset iso-8859-1) 4 8 (charset iso-8859-1) 9 12 (charset iso-8859-1) 13 17 (charset iso-8859-1) 18 21 (charset iso-8859-1) 22 24 (charset iso-8859-1) 25 29 (charset iso-8859-1) 30 34 (charset iso-8859-1) 35 41 (charset iso-8859-1) 42 44 (charset iso-8859-1) 45 47 (charset iso-8859-1) 48 50 (charset iso-8859-1) 51 55 (charset iso-8859-1) 56 61 (charset iso-8859-1) 62 64 (charset iso-8859-1) 65 71 (charset iso-8859-1) 72 73 (charset iso-8859-1) 74 79 (charset iso-8859-1) 80 82 (charset iso-8859-1) 83 89 (charset iso-8859-1) 90 92 (charset iso-8859-1) 93 96 (charset iso-8859-1) 97 105 (charset iso-8859-1) 106 109 (charset iso-8859-1) 110 113 (charset iso-8859-1) 114 121 (charset iso-8859-1)))))))) (kom-write-footnote "" (section ((id . "kom-write-footnote") (prompt . "")) ((h3 nil ((cref ((id . "kom-write-footnote")) nil))) (p nil ((TEXT . #(" Skapar en ny fotnot till ett befintligt inlägg. " 1 7 (charset iso-8859-1) 8 10 (charset iso-8859-1) 11 13 (charset iso-8859-1) 14 20 (charset iso-8859-1) 21 25 (charset iso-8859-1) 26 29 (charset iso-8859-1) 30 40 (charset iso-8859-1) 41 48 (charset iso-8859-1)))))))) (kom-private-answer "" (section ((id . "kom-private-answer") (prompt . "")) ((h3 nil ((cref ((id . "kom-private-answer")) nil))) (p nil ((TEXT . #(" Skapar ett personligt svar till ett befintligt inlägg. " 1 7 (charset iso-8859-1) 8 11 (charset iso-8859-1) 12 22 (charset iso-8859-1) 23 27 (charset iso-8859-1) 28 32 (charset iso-8859-1) 33 36 (charset iso-8859-1) 37 47 (charset iso-8859-1) 48 55 (charset iso-8859-1)))))))) (kom-write-comment "" (section ((id . "kom-write-comment") (prompt . "")) ((h3 nil ((cref ((id . "kom-write-comment")) nil))) (p nil ((TEXT . #(" Skapar en ny kommentar till ett befintligt inlägg. " 1 7 (charset iso-8859-1) 8 10 (charset iso-8859-1) 11 13 (charset iso-8859-1) 14 23 (charset iso-8859-1) 24 28 (charset iso-8859-1) 29 32 (charset iso-8859-1) 33 43 (charset iso-8859-1) 44 51 (charset iso-8859-1)))))))) (kom-send-letter "" (section ((id . "kom-send-letter") (prompt . "")) ((h3 nil ((cref ((id . "kom-send-letter")) nil))) (p nil ((TEXT . " Skapar ett nytt brev. ")))))) (kom-write-text "" (section ((id . "kom-write-text") (prompt . "")) ((h3 nil ((cref ((id . "kom-write-text")) nil))) (p nil ((TEXT . #(" Skapar ett nytt urinlägg. " 1 7 (charset iso-8859-1) 8 11 (charset iso-8859-1) 12 16 (charset iso-8859-1) 17 26 (charset iso-8859-1)))))))) (kom-review-tree "" (section ((id . "kom-review-tree") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-tree")) nil))) (p nil ((TEXT . #(" Återser hela kommentarsträdet under det angivna inlägget. " 1 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 30 (charset iso-8859-1) 31 36 (charset iso-8859-1) 37 40 (charset iso-8859-1) 41 48 (charset iso-8859-1) 49 58 (charset iso-8859-1)))))))) (kom-find-root "" (section ((id . "kom-find-root") (prompt . "")) ((h3 nil ((cref ((id . "kom-find-root")) nil))) (p nil ((TEXT . #(" Återser urinlägget till det angivna inlägget. " 1 8 (charset iso-8859-1) 9 19 (charset iso-8859-1) 20 24 (charset iso-8859-1) 25 28 (charset iso-8859-1) 29 36 (charset iso-8859-1) 37 46 (charset iso-8859-1)))))))) (kom-find-root-review "" (section ((id . "kom-find-root-review") (prompt . "")) ((h3 nil ((cref ((id . "kom-find-root-review")) nil))) (p nil ((TEXT . #(" Återser hela kommentarsträdet som det angivna inlägget är en del av. Ungefär samma sak som att göra " 1 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 30 (charset iso-8859-1) 31 34 (charset iso-8859-1) 35 38 (charset iso-8859-1) 39 46 (charset iso-8859-1) 47 55 (charset iso-8859-1) 56 58 (charset iso-8859-1) 59 61 (charset iso-8859-1) 62 65 (charset iso-8859-1) 66 69 (charset iso-8859-1) 70 77 (charset iso-8859-1) 78 83 (charset iso-8859-1) 84 87 (charset iso-8859-1) 88 91 (charset iso-8859-1) 92 95 (charset iso-8859-1) 96 100 (charset iso-8859-1))) (cref ((id . "kom-find-root")) nil) (TEXT . #(" följt av " 1 6 (charset iso-8859-1) 7 9 (charset iso-8859-1))) (cref ((id . "kom-review-tree")) nil) (TEXT . ". ")))))) (kom-review-comments "" (section ((id . "kom-review-comments") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-comments")) nil))) (p nil ((TEXT . #(" Återser alla kommentarer till det angivna inlägget, men inte kommentarer till kommentarerna. " 1 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 25 (charset iso-8859-1) 26 30 (charset iso-8859-1) 31 34 (charset iso-8859-1) 35 42 (charset iso-8859-1) 43 52 (charset iso-8859-1) 53 56 (charset iso-8859-1) 57 61 (charset iso-8859-1) 62 73 (charset iso-8859-1) 74 78 (charset iso-8859-1) 79 93 (charset iso-8859-1)))))))) (kom-view-commented-text "" (section ((id . "kom-view-commented-text") (prompt . "")) ((h3 nil ((cref ((id . "kom-view-commented-text")) nil))) (p nil ((TEXT . #(" Återser inlägget som det angivna inlägget är en kommentar till. " 1 8 (charset iso-8859-1) 9 17 (charset iso-8859-1) 18 21 (charset iso-8859-1) 22 25 (charset iso-8859-1) 26 33 (charset iso-8859-1) 34 42 (charset iso-8859-1) 43 45 (charset iso-8859-1) 46 48 (charset iso-8859-1) 49 58 (charset iso-8859-1) 59 64 (charset iso-8859-1)))))))) (kom-review-clear "" (section ((id . "kom-review-clear") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-clear")) nil))) (p nil ((TEXT . #(" Avbryter alla återsekommandon som är aktiva. " 1 9 (charset iso-8859-1) 10 14 (charset iso-8859-1) 15 30 (charset iso-8859-1) 31 34 (charset iso-8859-1) 35 37 (charset iso-8859-1) 38 45 (charset iso-8859-1)))))))) (kom-review-more "" (section ((id . "kom-review-more") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-more")) nil))) (p nil ((TEXT . #(" Återser fler inlägg med samma kriterier som senaste " 1 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 20 (charset iso-8859-1) 21 24 (charset iso-8859-1) 25 30 (charset iso-8859-1) 31 40 (charset iso-8859-1) 41 44 (charset iso-8859-1) 45 52 (charset iso-8859-1))) (cref ((id . "kom-review-by-to")) nil) (TEXT . ", eller ") (cref ((id . "kom-review-first")) nil) (TEXT . ". ")))))) (kom-review-first "" (section ((id . "kom-review-first") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-first")) nil))) (p nil ((TEXT . #(" Återser de första inläggen skrivna av en viss person till ett visst möte. Om du inte anger en viss parameter (eller sätter antal till noll) så tas ingen hänsyn till den parametern, så man kan återse allt av en viss person, fösta 5 av en viss person till alla möten och så vidare. " 1 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 18 (charset iso-8859-1) 19 27 (charset iso-8859-1) 28 35 (charset iso-8859-1) 36 38 (charset iso-8859-1) 39 41 (charset iso-8859-1) 42 46 (charset iso-8859-1) 47 53 (charset iso-8859-1) 54 58 (charset iso-8859-1) 59 62 (charset iso-8859-1) 63 68 (charset iso-8859-1) 69 74 (charset iso-8859-1) 75 77 (charset iso-8859-1) 78 80 (charset iso-8859-1) 81 85 (charset iso-8859-1) 86 91 (charset iso-8859-1) 92 94 (charset iso-8859-1) 95 99 (charset iso-8859-1) 100 109 (charset iso-8859-1) 110 116 (charset iso-8859-1) 117 123 (charset iso-8859-1) 124 129 (charset iso-8859-1) 130 134 (charset iso-8859-1) 135 140 (charset iso-8859-1) 141 143 (charset iso-8859-1) 144 147 (charset iso-8859-1) 148 153 (charset iso-8859-1) 154 160 (charset iso-8859-1) 161 165 (charset iso-8859-1) 166 169 (charset iso-8859-1) 170 181 (charset iso-8859-1) 182 184 (charset iso-8859-1) 185 188 (charset iso-8859-1) 189 192 (charset iso-8859-1) 193 199 (charset iso-8859-1) 200 204 (charset iso-8859-1) 205 207 (charset iso-8859-1) 208 210 (charset iso-8859-1) 211 215 (charset iso-8859-1) 216 223 (charset iso-8859-1) 224 229 (charset iso-8859-1) 230 231 (charset iso-8859-1) 232 234 (charset iso-8859-1) 235 237 (charset iso-8859-1) 238 242 (charset iso-8859-1) 243 249 (charset iso-8859-1) 250 254 (charset iso-8859-1) 255 259 (charset iso-8859-1) 260 265 (charset iso-8859-1) 266 269 (charset iso-8859-1) 270 272 (charset iso-8859-1) 273 280 (charset iso-8859-1)))))))) (kom-review-by-to "" (section ((id . "kom-review-by-to") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-by-to")) nil))) (p nil ((TEXT . #(" Återser de senaste inläggen skrivna av en viss person till ett visst möte. Om du inte anger en viss parameter (eller sätter antal till noll) så tas ingen hänsyn till den parametern, så man kan återse allt av en viss person, fösta 5 av en viss person till alla möten och så vidare. " 1 8 (charset iso-8859-1) 9 11 (charset iso-8859-1) 12 19 (charset iso-8859-1) 20 28 (charset iso-8859-1) 29 36 (charset iso-8859-1) 37 39 (charset iso-8859-1) 40 42 (charset iso-8859-1) 43 47 (charset iso-8859-1) 48 54 (charset iso-8859-1) 55 59 (charset iso-8859-1) 60 63 (charset iso-8859-1) 64 69 (charset iso-8859-1) 70 75 (charset iso-8859-1) 76 78 (charset iso-8859-1) 79 81 (charset iso-8859-1) 82 86 (charset iso-8859-1) 87 92 (charset iso-8859-1) 93 95 (charset iso-8859-1) 96 100 (charset iso-8859-1) 101 110 (charset iso-8859-1) 111 117 (charset iso-8859-1) 118 124 (charset iso-8859-1) 125 130 (charset iso-8859-1) 131 135 (charset iso-8859-1) 136 141 (charset iso-8859-1) 142 144 (charset iso-8859-1) 145 148 (charset iso-8859-1) 149 154 (charset iso-8859-1) 155 161 (charset iso-8859-1) 162 166 (charset iso-8859-1) 167 170 (charset iso-8859-1) 171 182 (charset iso-8859-1) 183 185 (charset iso-8859-1) 186 189 (charset iso-8859-1) 190 193 (charset iso-8859-1) 194 200 (charset iso-8859-1) 201 205 (charset iso-8859-1) 206 208 (charset iso-8859-1) 209 211 (charset iso-8859-1) 212 216 (charset iso-8859-1) 217 224 (charset iso-8859-1) 225 230 (charset iso-8859-1) 231 232 (charset iso-8859-1) 233 235 (charset iso-8859-1) 236 238 (charset iso-8859-1) 239 243 (charset iso-8859-1) 244 250 (charset iso-8859-1) 251 255 (charset iso-8859-1) 256 260 (charset iso-8859-1) 261 266 (charset iso-8859-1) 267 270 (charset iso-8859-1) 271 273 (charset iso-8859-1) 274 281 (charset iso-8859-1)))))))) (kom-list-marks "" (section ((id . "kom-list-marks") (prompt . "")) ((h3 nil ((cref ((id . "kom-list-marks")) nil))) (p nil ((TEXT . #(" Listar alla inlägg markerade med en viss markeringstyp. " 1 7 (charset iso-8859-1) 8 12 (charset iso-8859-1) 13 19 (charset iso-8859-1) 20 29 (charset iso-8859-1) 30 33 (charset iso-8859-1) 34 36 (charset iso-8859-1) 37 41 (charset iso-8859-1) 42 56 (charset iso-8859-1)))))))) (kom-review-marked-texts "" (section ((id . "kom-review-marked-texts") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-marked-texts")) nil))) (p nil ((TEXT . #(" Återser inlägg markerade med en viss markeringstyp. " 1 8 (charset iso-8859-1) 9 15 (charset iso-8859-1) 16 25 (charset iso-8859-1) 26 29 (charset iso-8859-1) 30 32 (charset iso-8859-1) 33 37 (charset iso-8859-1) 38 52 (charset iso-8859-1)))))))) (kom-review-all-marked-texts "" (section ((id . "kom-review-all-marked-texts") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-all-marked-texts")) nil))) (p nil ((TEXT . #(" Återser alla markerade inlägg. " 1 8 (charset iso-8859-1) 9 13 (charset iso-8859-1) 14 23 (charset iso-8859-1) 24 31 (charset iso-8859-1)))))))) (kom-unmark-text "" (section ((id . "kom-unmark-text") (prompt . "")) ((h3 nil ((cref ((id . "kom-unmark-text")) nil))) (p nil ((TEXT . #(" Avmarkerar ett inlägg. " 1 11 (charset iso-8859-1) 12 15 (charset iso-8859-1) 16 23 (charset iso-8859-1)))))))) (kom-mark-text "" (section ((id . "kom-mark-text") (prompt . "")) ((h3 nil ((cref ((id . "kom-mark-text")) nil))) (p nil ((TEXT . #(" Markerar ett inlägg. Frågar normalt efter inläggstyp. " 1 9 (charset iso-8859-1) 10 13 (charset iso-8859-1) 14 21 (charset iso-8859-1) 22 28 (charset iso-8859-1) 29 36 (charset iso-8859-1) 37 42 (charset iso-8859-1) 43 54 (charset iso-8859-1)))))))) (kom-super-jump "" (section ((id . "kom-super-jump") (prompt . "")) ((h3 nil ((cref ((id . "kom-super-jump")) nil))) (p nil ((TEXT . #(" Skapar ett filter som hoppar över inlägg som har samma ärenderad som det angivna inlägget och som har aktuellt möte som mottagare. " 1 7 (charset iso-8859-1) 8 11 (charset iso-8859-1) 12 18 (charset iso-8859-1) 19 22 (charset iso-8859-1) 23 29 (charset iso-8859-1) 30 34 (charset iso-8859-1) 35 41 (charset iso-8859-1) 42 45 (charset iso-8859-1) 46 49 (charset iso-8859-1) 50 55 (charset iso-8859-1) 56 65 (charset iso-8859-1) 66 69 (charset iso-8859-1) 70 73 (charset iso-8859-1) 74 81 (charset iso-8859-1) 82 90 (charset iso-8859-1) 91 94 (charset iso-8859-1) 95 98 (charset iso-8859-1) 99 102 (charset iso-8859-1) 103 111 (charset iso-8859-1) 112 116 (charset iso-8859-1) 117 120 (charset iso-8859-1) 121 131 (charset iso-8859-1)))))))) (kom-jump "" (section ((id . "kom-jump") (prompt . "")) ((h3 nil ((cref ((id . "kom-jump")) nil))) (p nil ((TEXT . #(" Hoppar över kommentarsträdet rotat i det senast lästa inlägget. Praktiskt när man ser att ett inlägg inleder en diskussion som man inte är intresserad av. " 1 7 (charset iso-8859-1) 8 12 (charset iso-8859-1) 13 29 (charset iso-8859-1) 30 35 (charset iso-8859-1) 36 37 (charset iso-8859-1) 38 41 (charset iso-8859-1) 42 48 (charset iso-8859-1) 49 54 (charset iso-8859-1) 55 64 (charset iso-8859-1) 65 74 (charset iso-8859-1) 75 78 (charset iso-8859-1) 79 82 (charset iso-8859-1) 83 86 (charset iso-8859-1) 87 90 (charset iso-8859-1) 91 94 (charset iso-8859-1) 95 101 (charset iso-8859-1) 102 109 (charset iso-8859-1) 110 112 (charset iso-8859-1) 113 123 (charset iso-8859-1) 124 127 (charset iso-8859-1) 128 131 (charset iso-8859-1) 132 136 (charset iso-8859-1) 137 139 (charset iso-8859-1) 140 151 (charset iso-8859-1) 152 155 (charset iso-8859-1)))))))) (kom-set-unread "" (section ((id . "kom-set-unread") (prompt . "")) ((h3 nil ((cref ((id . "kom-set-unread")) nil))) (p nil ((TEXT . #(" Sätter antalet olästa i aktuellt möte. Kan vara bra om man har många gamla inlägg att läsa ikapp eller helt enkelt inte är intresserad av någonting i mötet. " 1 7 (charset iso-8859-1) 8 15 (charset iso-8859-1) 16 22 (charset iso-8859-1) 23 24 (charset iso-8859-1) 25 33 (charset iso-8859-1) 34 39 (charset iso-8859-1) 40 43 (charset iso-8859-1) 44 48 (charset iso-8859-1) 49 52 (charset iso-8859-1) 53 55 (charset iso-8859-1) 56 59 (charset iso-8859-1) 60 63 (charset iso-8859-1) 64 69 (charset iso-8859-1) 70 75 (charset iso-8859-1) 76 82 (charset iso-8859-1) 83 86 (charset iso-8859-1) 87 91 (charset iso-8859-1) 92 97 (charset iso-8859-1) 98 103 (charset iso-8859-1) 104 108 (charset iso-8859-1) 109 115 (charset iso-8859-1) 116 120 (charset iso-8859-1) 121 123 (charset iso-8859-1) 124 135 (charset iso-8859-1) 136 138 (charset iso-8859-1) 139 148 (charset iso-8859-1) 149 150 (charset iso-8859-1) 151 157 (charset iso-8859-1)))))))) (kom-prioritize "" (section ((id . "kom-prioritize") (prompt . "")) ((h3 nil ((cref ((id . "kom-prioritize")) nil))) (p nil ((TEXT . #(" Ändra interaktivt prioriteter och placeringar på dina medlemskap i olika möen. Tryck C-h m eller ? när du har gett kommandot för mer information. " 1 6 (charset iso-8859-1) 7 18 (charset iso-8859-1) 19 30 (charset iso-8859-1) 31 34 (charset iso-8859-1) 35 46 (charset iso-8859-1) 47 49 (charset iso-8859-1) 50 54 (charset iso-8859-1) 55 65 (charset iso-8859-1) 66 67 (charset iso-8859-1) 68 73 (charset iso-8859-1) 74 79 (charset iso-8859-1) 80 85 (charset iso-8859-1) 86 89 (charset iso-8859-1) 90 91 (charset iso-8859-1) 92 97 (charset iso-8859-1) 98 99 (charset iso-8859-1) 100 103 (charset iso-8859-1) 104 106 (charset iso-8859-1) 107 110 (charset iso-8859-1) 111 115 (charset iso-8859-1) 116 125 (charset iso-8859-1) 126 129 (charset iso-8859-1) 130 133 (charset iso-8859-1) 134 146 (charset iso-8859-1)))))))))))

(defvar lyskom-en-help-data (quote ((help-on-command "Command description" (section ((id . "help-on-command") (prompt . "Command description")) ((call ((function . "lyskom-help-on-command")) nil)))) (commands "Keyboard commands" (section ((id . "commands") (prompt . "Keyboard commands")) ((p nil ((TEXT . "Keyboard commands:"))) (keymap nil nil)))) (language-help "" (section ((id . "language-help") (prompt . "")) ((h2 nil ((TEXT . "Language Settings"))) (p nil ((TEXT . " You can switch the language in this session by using the command ") (cref ((id . "kom-change-language")) nil) (TEXT . " or the Emacs command ") (i nil ((TEXT . "M-x kom-change-language"))) (TEXT . ". To also change the key bindings (this will affect all sessions), type C-u before the command (e.g. C-u x") (cref ((id . "kom-change-language")) nil) (TEXT . "). "))) (p nil ((TEXT . " For full information, including how to change the default language, see ") (refer ((id . "language")) nil) (TEXT . " (this information will be presented using the current language selection). ")))))) (language "Language" (section ((id . "language") (prompt . "Language")) ((h1 nil ((TEXT . "Multiple language support"))) (p nil ((TEXT . " The elisp client can be built to support multiple languages. The default is to build with support for english and swedish, with english as the default language selection. "))) (h2 nil ((TEXT . "Temporary Language Change"))) (p nil ((TEXT . " Use ") (cref ((id . "kom-change-language")) nil) (TEXT . " to temporarily switch languages. Please note that you must give a prefix argument to ") (cref ((id . "kom-change-language")) nil) (TEXT . " to change the key binding. Key bindings are changed in ") (i nil ((TEXT . "all"))) (TEXT . " sessions, not just the one where the command is given. "))) (h2 nil ((TEXT . "Permanent Language Change"))) (p nil ((TEXT . " Use ") (cref ((id . "kom-customize")) nil) (TEXT . " to change languages permanently. By saving the language selection in .emacs, that language will be the default the next time you start Emacs and load the elisp client. "))) (h2 nil ((TEXT . "Changing the default language"))) (p nil ((TEXT . " There are a number of ways to change the default language in the elisp client. The client looks at the following, in order, until it finds a useful setting: "))) (list nil ((item nil ((TEXT . "The variable kom-default-language (change it with ") (cref ((id . "kom-customize")) nil) (TEXT . ")"))) (item nil ((TEXT . "The environment variable KOMLANGUAGE"))) (item nil ((TEXT . "The environment variable LC_ALL"))) (item nil ((TEXT . "The environment variable LC_MESSAGES"))) (item nil ((TEXT . "The environment variable LANG"))))) (p nil ((TEXT . " The variable kom-default-language can be set to a list of symbols, where each symbol corresponds to a language. Se the list below for available languages. The first language on the list that is supported by the client will be used. "))) (p nil ((TEXT . " The environment variables should be set to a language code. For example, the value \"sv\" will cause the client to use Swedish. "))) (h2 nil ((TEXT . "Known languages"))) (list nil ((item nil ((TEXT . "sv - Swedish"))) (item nil ((TEXT . "en - English")))))))) (conferences "Conferences" (section ((id . "conferences") (prompt . "Conferences")) ((h1 nil ((TEXT . "Conferences"))) (p nil ((TEXT . " Every LysKOM system is organized as a set of conferences, each covering a separate topic. Anybody can create a conference, but you should think twice before doing so. In a mature LysKOM system the conference you want to create probably already exists. "))) (p nil ((TEXT . " Conferences can be open, closed and secret. Anyone can join and read texts in all open conferences. To join a closed conference, the conference administractor has to grant membership. Secret conferences are secret: you can't even see them unless you're a member. Most conferences are open. Closed and secret conferences are much less common. "))) (p nil ((TEXT . " The following commands might be useful to get started: "))) (inline ((id . "kom-list-conferences")) nil) (inline ((id . "kom-review-presentation")) nil) (inline ((id . "kom-add-self")) nil) (inline ((id . "kom-go-to-conf")) nil) (h2 nil ((TEXT . "Priorities"))) (p nil ((TEXT . " Your memberships have priorities. When you log on, conferences with higher priority will be presented before conferences with lower priority. Use ") (cref ((id . "kom-prioritize")) nil) (TEXT . " to change your priorities. ")))))) (persons "Persons" (section ((id . "persons") (prompt . "Persons")) ((h1 nil ((TEXT . "Persons"))) (p nil ((TEXT . " Every LysKOM user has a person. A person contains some statistics about what the user has done (texts created, texts read, logon time, and some other stuff). Every person is also associated with a letterbox with the same name of the person. The letterbox is essentially a regular closed conference. ")))))) (texts "Texts" (section ((id . "texts") (prompt . "Texts")) ((h1 nil ((TEXT . "Texts"))) (p nil ((TEXT . " The purpose of LysKOM is to communicate, and communication is done through texts. Texts are simply texts written by a LysKOM member. "))) (h2 nil ((TEXT . "Recipients"))) (p nil ((TEXT . " Every text has one or more recipients: conferences to which the text has been sent. There are three kinds of recipients: regular recipients, cc-recipients and bcc-recipients. "))) (p nil ((TEXT . " ") (b nil ((TEXT . "Regular recipients"))) (TEXT . " are the most common. A regular recipient means that the text belongs in the specified conference (or conferences). Comments to the text will automatically be sent to the same set of recipients. "))) (p nil ((TEXT . " ") (b nil ((TEXT . "CC recipients"))) (TEXT . " are used when you want to send a copy of a text to a conference, but don't want followups sent to that conference. "))) (p nil ((TEXT . " ") (b nil ((TEXT . "BCC recipients"))) (TEXT . " are fairly unusual. To even ") (i nil ((TEXT . "see"))) (TEXT . " a BCC recipient you have to have permission to be a member in the recipient conference. This kind of recipient can be used when you want to send a copy of a text to somebody's letterbox, but don't want other readers to know. "))) (h2 nil ((TEXT . "Comments"))) (p nil ((TEXT . " A text can have comments. A thread of comments to comments to comments is often called comment thread, comment chain or comment tree (strictly speaking a comment thread is a path down the comment tree). Anyone can write comments to any text. There is not requirement that comments be sent to the same conferences as the commented texts. "))) (h2 nil ((TEXT . "Special texts"))) (p nil ((TEXT . " There are three kinds of special texts: presentations, FAQs and notices. A presentation is a text associated with a conference or letterbox, that contains a presentation of that conference or person. These are normally sent to special conferences. A FAQ is a text with frequently asked questions (and answers) in a particular conference. Normally, new FAQs will be displayed when entering the associated conference. Notices are texts that contain urgent information about a person or conference. You can use a notice when you will be away from LysKOM for a while, and similar occasions. ")))))) (settings "Settings" (section ((id . "settings") (prompt . "Settings")) ((h1 nil ((TEXT . "Settings"))) (p nil ((TEXT . " The elisp client has a large number of user-configurable settings. With a few exceptions you can change these by using the command ") (cref ((id . "kom-customize")) nil) (TEXT . ". "))) (p nil ((TEXT . " Settings can be saved in the server or in your .emacs. Settings that are saved in the server will apply only for a particular user on a particular server. Settings saved in .emacs take precedence over settings stored in the server. ")))))) (reading "Reading texts" (section ((id . "reading") (prompt . "Reading texts")) ((h1 nil ((TEXT . "Reading texts"))) (p nil ((TEXT . " To just read texts in the default order, simply press SPC until all texts have been displayed. The elisp client will display one text at a time until you have read all texts in all conferences you are a member of. Use the command ") (cref ((id . "kom-list-summary")) nil) (TEXT . " to see which texts will be shown. There are a large number of more advanced commands, some of which are listed in the sections mentioned below. "))) (list ((header . "See also:")) ((item nil ((refer ((id . "filter")) nil))) (item nil ((refer ((id . "review")) nil))) (item nil ((refer ((id . "mark")) nil)))))))) (filter "Avoiding certain texts" (section ((id . "filter") (prompt . "Avoiding certain texts")) ((h1 nil ((TEXT . "Avoiding certain texts"))) (p nil ((TEXT . " Sometimes you don't want to read certain texts. In those cases the following commands may be useful. Besides these, there are a number of commands for filtering texts, if there is a certain type of text you never want to see. "))) (inline ((id . "kom-set-unread")) nil) (inline ((id . "kom-jump")) nil) (inline ((id . "kom-super-jump")) nil)))) (mark "Remembering texts" (section ((id . "mark") (prompt . "Remembering texts")) ((h1 nil ((TEXT . "Remembering texts"))) (p nil ((TEXT . " Sometimes you might find a text that you want to remember. In these cases, mark the text. A mark is really just a number from 0 to 255, but the elisp client supports assigning easy-to-remember names to marks. "))) (inline ((id . "kom-mark-text")) nil) (inline ((id . "kom-unmark-text")) nil) (inline ((id . "kom-review-all-marked-texts")) nil) (inline ((id . "kom-review-marked-texts")) nil) (inline ((id . "kom-list-marks")) nil)))) (review "Finding old texts" (section ((id . "review") (prompt . "Finding old texts")) ((h1 nil ((TEXT . "Finding old texts"))) (p nil ((TEXT . " There are a large number of commands for finding old texts. All of these start with the word \"Review\". This is just a small sampling: "))) (inline ((id . "kom-review-by-to")) nil) (inline ((id . "kom-review-first")) nil) (inline ((id . "kom-review-more")) nil) (inline ((id . "kom-review-clear")) nil) (inline ((id . "kom-view-commented-text")) nil) (inline ((id . "kom-review-comments")) nil) (inline ((id . "kom-find-root-review")) nil) (inline ((id . "kom-find-root")) nil) (inline ((id . "kom-review-tree")) nil)))) (writing "Writing or changing texts" (section ((id . "writing") (prompt . "Writing or changing texts")) ((h1 nil ((TEXT . "Writing or changing texts"))) (p nil ((TEXT . " There are three basic types of texts: texts, comments and footnotes. All of them are texts, but they are associated to ") (i nil ((TEXT . "other"))) (TEXT . " texts in different ways. "))) (h2 nil ((TEXT . "Texts"))) (p nil ((TEXT . " These are texts that aren't comments to other texts. They start a new thread of discussion. Remember to choose a good subject line when you create new texts. "))) (inline ((id . "kom-write-text")) nil) (inline ((id . "kom-send-letter")) nil) (h2 nil ((TEXT . "Comments"))) (p nil ((TEXT . " Comments are texts that are comments to other texts. When you write a comment, check that the subject line and recipients are still suitable. Personal replies are simply comments sent to somebody's letterbox. "))) (inline ((id . "kom-write-comment")) nil) (inline ((id . "kom-private-answer")) nil) (h2 nil ((TEXT . "Footnotes"))) (p nil ((TEXT . " Footnotes are special comments that only the author of a text can create. Since they are presented before regular comments, they are useful when you want to clarify something you've written before people start writing comments to the text. "))) (inline ((id . "kom-write-footnote")) nil) (h2 nil ((TEXT . "Prefix argument"))) (p nil ((TEXT . " There are several ways to specify which text you want to comment or write a footnote to. When you give any of the comments listed above without an argument, they will apply to the most recenty read text. "))) (p nil ((TEXT . " Sometimes you want a different text. There are a few commands that apply to the next-to-most-recently read (") (cref ((id . "kom-comment-previous")) nil) (TEXT . ", ") (cref ((id . "kom-private-answer-previous")) nil) (TEXT . "), and by using prefix arguments you can specify any text to any command. Supply a prefix argument as usual in Emacs by C-u followed by the argument, or simply type the argument directly. "))) (p nil ((TEXT . " Specify text ") (i nil ((TEXT . "N"))) (TEXT . " by giving the text number as the prefix argument. Specify the text where the cursor is by giving prefix argument 0. Specify the text ") (i nil ((TEXT . "N"))) (TEXT . " texts back in the buffer from the point where the cursor is by giving the prefix argument -") (i nil ((TEXT . "N"))) (TEXT . ". To get a prompt at which you can type the text number, simply hit C-u before giving the command. "))) (h2 nil ((TEXT . "Manage recipients"))) (p nil ((TEXT . " To change the recipients on a text you have to be the author of the text or administrator of the recipient you want to change. Use the commands ") (cref ((id . "kom-add-recipient")) nil) (TEXT . " to add and ") (cref ((id . "kom-sub-recipient")) nil) (TEXT . " to remove recipients. "))) (h2 nil ((TEXT . "Messages and remarks"))) (p nil ((TEXT . " Never use messages (personal, group or alarm messages) or remarks in place of regular texts. It won't work and people will get upset. ")))))) (kom-list-conferences "" (section ((id . "kom-list-conferences") (prompt . "")) ((h3 nil ((cref ((id . "kom-list-conferences")) nil))) (p nil ((TEXT . " List conferences in LysKOM. Use this command with creative arguments to find conferences that you're interested in. ")))))) (kom-list-re "" (section ((id . "kom-list-re") (prompt . "")) ((h3 nil ((cref ((id . "kom-list-re")) nil))) (p nil ((TEXT . " List conferences in LysKOM. Use this command with creative arguments to find conferences that you're interested in. ")))))) (kom-review-presentation "" (section ((id . "kom-review-presentation") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-presentation")) nil))) (p nil ((TEXT . " Show the presentation for a conference or person. Use this command to get more information about a conference. ")))))) (kom-add-self "" (section ((id . "kom-add-self") (prompt . "")) ((h3 nil ((cref ((id . "kom-add-self")) nil))) (p nil ((TEXT . " Join a conference so you can read the texts in the conference and so you'll find out when new texts are created. ")))))) (kom-go-to-conf "" (section ((id . "kom-go-to-conf") (prompt . "")) ((h3 nil ((cref ((id . "kom-go-to-conf")) nil))) (p nil ((TEXT . " Go to a conference you want to read texts in. If you aren't already a member of the conference, you will be asked if you want to join the conference. ")))))) (kom-write-footnote "" (section ((id . "kom-write-footnote") (prompt . "")) ((h3 nil ((cref ((id . "kom-write-footnote")) nil))) (p nil ((TEXT . " Creates a new footnote to an existing text. ")))))) (kom-private-answer "" (section ((id . "kom-private-answer") (prompt . "")) ((h3 nil ((cref ((id . "kom-private-answer")) nil))) (p nil ((TEXT . " Creates a private reply to the author of an existing text. ")))))) (kom-write-comment "" (section ((id . "kom-write-comment") (prompt . "")) ((h3 nil ((cref ((id . "kom-write-comment")) nil))) (p nil ((TEXT . " Creates a new comment to an existing text. ")))))) (kom-send-letter "" (section ((id . "kom-send-letter") (prompt . "")) ((h3 nil ((cref ((id . "kom-send-letter")) nil))) (p nil ((TEXT . " Creates a new letter. ")))))) (kom-write-text "" (section ((id . "kom-write-text") (prompt . "")) ((h3 nil ((cref ((id . "kom-write-text")) nil))) (p nil ((TEXT . " Creates a new text that is not a comment to any other text. ")))))) (kom-review-tree "" (section ((id . "kom-review-tree") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-tree")) nil))) (p nil ((TEXT . " Reviews the entire tree of comments under the specified text. ")))))) (kom-find-root "" (section ((id . "kom-find-root") (prompt . "")) ((h3 nil ((cref ((id . "kom-find-root")) nil))) (p nil ((TEXT . " Finds the text that started the discussion that the specified text is part of. ")))))) (kom-find-root-review "" (section ((id . "kom-find-root-review") (prompt . "")) ((h3 nil ((cref ((id . "kom-find-root-review")) nil))) (p nil ((TEXT . " Reviews the entire tree of comments that the specified text is part of. Essentially the same as doing ") (cref ((id . "kom-find-root")) nil) (TEXT . " followed by ") (cref ((id . "kom-review-tree")) nil) (TEXT . ". ")))))) (kom-review-comments "" (section ((id . "kom-review-comments") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-comments")) nil))) (p nil ((TEXT . " Reviews all comments to the specified text, but not the comments to the comments. ")))))) (kom-view-commented-text "" (section ((id . "kom-view-commented-text") (prompt . "")) ((h3 nil ((cref ((id . "kom-view-commented-text")) nil))) (p nil ((TEXT . " Reviews the text to which the specified text is a comment. ")))))) (kom-review-clear "" (section ((id . "kom-review-clear") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-clear")) nil))) (p nil ((TEXT . " Aborts all review commands that are active. ")))))) (kom-review-more "" (section ((id . "kom-review-more") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-more")) nil))) (p nil ((TEXT . " Reviews more texts using the same criteria as the most recent ") (cref ((id . "kom-review-by-to")) nil) (TEXT . ", or ") (cref ((id . "kom-review-first")) nil) (TEXT . " command. ")))))) (kom-review-first "" (section ((id . "kom-review-first") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-first")) nil))) (p nil ((TEXT . " Reviews the first text(s) created by a particular person sent to a particular conference. Any parameter can be ignored by simply not supplying a value (use zero for the number of texts) in order to do things like review all texts to a particular conference, the first five texts written by a particular person and so forth. ")))))) (kom-review-by-to "" (section ((id . "kom-review-by-to") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-by-to")) nil))) (p nil ((TEXT . " Reviews the text(s) most recently created by a particular person sent to a particular conference. Any parameter can be ignored by simply not supplying a value (use zero for the number of texts) in order to do things like review all texts to a particular conference, the last five texts written by a particular person and so forth. ")))))) (kom-list-marks "" (section ((id . "kom-list-marks") (prompt . "")) ((h3 nil ((cref ((id . "kom-list-marks")) nil))) (p nil ((TEXT . " Lists all texts marked with a particular mark. ")))))) (kom-review-marked-texts "" (section ((id . "kom-review-marked-texts") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-marked-texts")) nil))) (p nil ((TEXT . " Reviews all texts marked with a particular mark. ")))))) (kom-review-all-marked-texts "" (section ((id . "kom-review-all-marked-texts") (prompt . "")) ((h3 nil ((cref ((id . "kom-review-all-marked-texts")) nil))) (p nil ((TEXT . " Reviews all marked texts. ")))))) (kom-unmark-text "" (section ((id . "kom-unmark-text") (prompt . "")) ((h3 nil ((cref ((id . "kom-unmark-text")) nil))) (p nil ((TEXT . " Removes your marks from a texts. ")))))) (kom-mark-text "" (section ((id . "kom-mark-text") (prompt . "")) ((h3 nil ((cref ((id . "kom-mark-text")) nil))) (p nil ((TEXT . " Marks a texts. Normally this command will ask for the mark to use. ")))))) (kom-super-jump "" (section ((id . "kom-super-jump") (prompt . "")) ((h3 nil ((cref ((id . "kom-super-jump")) nil))) (p nil ((TEXT . " Creates a filter that will skip texts that have the same subject line as the specified text and are sent to the current conference. ")))))) (kom-jump "" (section ((id . "kom-jump") (prompt . "")) ((h3 nil ((cref ((id . "kom-jump")) nil))) (p nil ((TEXT . " Skips all texts in the comment tree below the most recently read text. This is useful when you see a text that starts a discussion that you aren't interested in. ")))))) (kom-set-unread "" (section ((id . "kom-set-unread") (prompt . "")) ((h3 nil ((cref ((id . "kom-set-unread")) nil))) (p nil ((TEXT . " Set the number of unread texts in a particular conference. Can be useful when you have lots of old texts to catch up with. ")))))) (kom-prioritize "" (section ((id . "kom-prioritize") (prompt . "")) ((h3 nil ((cref ((id . "kom-prioritize")) nil))) (p nil ((TEXT . " Interactively change priorities and placements of your memberships. For more information, press C-h m or ? when you have given the command. ")))))))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; This file contains primitives for the different data types
;;;; in the lyskom system. All types here have their origin in
;;;; the server. Compare the file clienttypes.el.
;;;;
;;;; Author: ceder
;;;;


;;; ============================================================
;;; Black magic...

(defmacro def-komtype (type args &rest flags)
  "Define a new type named TYPE with fields ARGS and documentation DOC.
TYPE is the name of the new type; a symbol.
ARGS is a list of arguments. Each element can be a symbol or a cons whose
car is the name of the field and the cdr is a property list for the field.

The special symbol &optional indicates start of fields that will be
optional in the constructor argument list.

Legal fiels properties are:

:default val        In the constructor, if the value supplied for the 
                    field is nil, use VAL instead.
:read-only t        Do not create a mutator. Note that t must be specified.
:write-only t       Do not create an accessor
:automatic val      Do not include field in constructor. Use VAL for
                    the initial field value.
:filter code        Use CODE as a filter for the field value supplied to
                    the constructor.

Only one of :automatic, :filter or :default may be supplied.


Optional FLAGS are additional modifiers.

If :nil-safe is included, then calling accessors on nil object will
return nil and not signal an error.

If :create-hook HOOK is included, HOOK will be inserted into the
constructor function. When HOOK is evaluated, OBJECT (uppercase) is
bound to the newly created object. It may be modified.
"
  (let ((accessors nil)
        (mutators nil)
        (predicate nil)
        (constructor nil)
        (access-method 'aref)
        (type-sym (intern (upcase (symbol-name type))))
        (constructor-body nil)
        (create-hook nil))

    (while flags
      (cond ((eq (car flags) ':nil-safe) (setq access-method 'elt))
            ((eq (car flags) ':create-hook)
             (setq flags (cdr flags))
             (setq create-hook (car flags))))
      (setq flags (cdr flags)))

    ;; Create constructor

    (setq constructor-body
          `(cons ',type-sym
                 (vector
                  ,@(delq '&optional
                          (mapcar 
                           (lambda (arg)
                             (cond ((plist-member (cdr-safe arg) ':automatic)
                                    (plist-get (cdr-safe arg) ':automatic))
                                   ((plist-member (cdr-safe arg) ':filter)
                                    (plist-get (cdr-safe arg) ':filter))
                                   ((plist-get (cdr-safe arg) ':default)
                                    `(or ,(car arg) ,(plist-get (cdr arg) :default)))
                                   (t (or (car-safe arg) arg))
                                   ))
                           args)))))

    (when create-hook
      (setq constructor-body
            `(let ((OBJECT ,constructor-body))
               ,create-hook
               OBJECT)))

    (setq constructor
          `(defsubst ,(intern (format "lyskom-create-%S" type))
             ,(delq nil (mapcar (lambda (arg)
                                  (unless (plist-member (cdr-safe arg) ':automatic)
                                    (or (car-safe arg) arg)))
                                args))
             ,(format "Create a `%S' from arguments.
Automatically created with def-komtype." type)
             ,constructor-body))

    ;; Create predicate

    (setq predicate
        `(defsubst ,(intern (format "lyskom-%S-p" type)) (obj)
           ,(format "Return non-nil if OBJ is a %S
Automatically created with def-komtype" type)
           (and (consp obj) (eq (car obj) ',type-sym))))

    ;; Create accessors and mutators

    (let ((field-index 0))
      (mapc
       (lambda (arg)
         (unless (eq arg '&optional)
           (let ((field (or (car-safe arg) arg)))
             (unless (plist-get (cdr-safe arg) ':write-only)
               (setq accessors
                     (cons `(defsubst ,(intern (format "%S->%S" type field)) (obj)
                              (lyskom-assert (or (null obj) (,(intern (format "lyskom-%S-p" type)) obj))
                                             "Assertion failed in %s: got %S" ,(format "%S->%S" type field) obj)
                              ,(format "Return field `%s' from OBJ." field)
                              (,access-method (cdr obj) ,field-index))
                           accessors)))
             (unless (plist-get (cdr-safe arg) ':read-only)
               (setq mutators
                     (cons `(defsubst ,(intern (format "set-%S->%S" type field)) (obj val)
                              (lyskom-assert (or (null obj) (,(intern (format "lyskom-%S-p" type)) obj))
                                             "Assertion failed in %s: got %S" ,(format "%S->%S" type field) obj)
                              ,(format "Set field `%s' of OBJ to VAL." field)
                              (aset (cdr obj) ,field-index val))
                           mutators))))
           (setq field-index (1+ field-index))))
       args))

    ;; Return the forms

    `(progn ,constructor
            ,predicate
            ,@accessors
            ,@mutators)))




;;; ================================================================
;;;                            conf-no-list


;;; Constructor:

(def-komtype conf-no-list 
  ((conf-nos :filter (cond ((vectorp conf-nos) (append conf-nos nil))
                           (t conf-nos))
             :read-only t))
  :nil-safe)


;;; ================================================================
;;;                            uconf-stat

(def-komtype uconf-stat 
  ((conf-no          :read-only t)
   name
   (conf-type        :read-only t)
   (highest-local-no :read-only t)
   (nice             :read-only t))
  :nil-safe)

;;; ================================================================
;;;                            conf-stat

;;; Constructor:

(def-komtype conf-stat 
  ((conf-no              :read-only t)
   name
   (conf-type            :read-only t)
   (creation-time        :read-only t)
   (last-written         :read-only t)
   (creator              :read-only t)
   (presentation)
   (supervisor           :read-only t)
   (permitted-submitters :read-only t)
   (super-conf           :read-only t)
   (msg-of-day)
   (garb-nice            :read-only t)
   (keep-commented       :read-only t)
   (no-of-members        :read-only t)
   (first-local-no       :read-only t)
   no-of-texts
   &optional
   (expire               :read-only t :default 0)
   (aux-items            :read-only t))
  :nil-safe)


;;; ================================================================
;;;                          conf-type.

(def-komtype conf-type
  ((rd_prot         :read-only t)
   (original        :read-only t)
   (secret          :read-only t)
   (letterbox       :read-only t)
   &optional 
   (anarchy         :read-only t)
   (forbid-secret   :read-only t)
   (rsv2            :read-only t)
   (rsv3            :read-only t))
  :nil-safe)



;;;; This field is just simulation of a field in the conf-stat
;;;; that not yet exist.

(defsubst conf-stat->comm-conf (conf-stat)
  (if (and (conf-type->original (conf-stat->conf-type conf-stat))
	   (not (zerop (conf-stat->super-conf conf-stat))))
      (conf-stat->super-conf conf-stat)
    (conf-stat->conf-no conf-stat)))




;;; ================================================================
;;;                         Conf-list


(def-komtype conf-list
  ((conf-nos    :read-only t) 
   (conf-types  :read-only t))
  :nil-safe)


;;; ================================================================
;;;                             pers-stat


;;; Constructor:

(def-komtype pers-stat 
  ((pers-no                 :read-only t) 
   (username                :read-only t)
   privileges
   (flags                   :read-only t)
   (last-login              :read-only t)
   user-area
   (total-time-present      :read-only t)
   (sessions                :read-only t)
   (created-lines           :read-only t)
   (created-bytes           :read-only t)
   (read-texts              :read-only t)
   (no-of-text-fetches      :read-only t)
   (created-persons         :read-only t)
   (created-confs           :read-only t)
   (first-created-text      :read-only t)
   (no-of-created-texts     :read-only t)
   (no-of-marks             :read-only t)
   (no-of-confs             :read-only t))
  :nil-safe)


;;; ================================================================
;;;                           text-stat


;;; Constructor:

(def-komtype text-stat 
  ((text-no             :read-only t)
   (creation-time       :read-only t)
   (author              :read-only t)
   (no-of-lines         :read-only t)
   (no-of-chars         :read-only t)
   (no-of-marks         :read-only t)
   (misc-info-list      :read-only t)
   &optional 
   (aux-items))
  :nil-safe)

;;; ================================================================
;;;                              aux-item

(def-komtype aux-item-flags
  ((deleted     :read-only t)
   inherit
   secret
   anonymous
   dont-garb
   (reserved2   :read-only t)
   (reserved3   :read-only t)
   (reserved4   :read-only t))
  :nil-safe)

(def-komtype aux-item 
  ((aux-no          :read-only t)
   (tag             :read-only t)
   (creator         :read-only t)
   (sent-at         :read-only t)
   (flags           :read-only t)
   (inherit-limit   :read-only t)
   data)
  :nil-safe)



;;; ================================================================
;;;                            text

(def-komtype text
  ((text-no         :read-only t)
   text-mass)
  :nil-safe)

;;; Utilities

(defun text->decoded-text-mass (text text-stat)
  "Get the text mass of a text after decoding according to its content type"
  (save-match-data 
    (let* ((str (text->text-mass text))
           (item (lyskom-get-aux-item
                  (text-stat->aux-items text-stat) 1))
           (content-type (and (car item)
                              (lyskom-mime-decode-content-type
                               (aux-item->data (car item)))))
           (charset (lyskom-mime-content-type-get content-type 'charset)))
      (condition-case nil
          (cond (charset (lyskom-mime-decode-string str charset))
                ((and (setq charset (lyskom-detect-coding-string str t))
                      (not (memq charset '(raw-text undecided))))
                 (lyskom-mime-decode-string str charset))
                ((setq charset lyskom-server-coding-system)
                 (lyskom-mime-decode-string str charset))
                (t (lyskom-mime-decode-string str 'iso-8859-1)))
        (error str)))))


;;; ================================================================
;;;                          misc-info


(def-komtype misc-info 
  (type
   recipient-no
   local-no
   rec-time
   comm-to
   comm-in
   footn-to
   footn-in
   sender
   sent-at)
  :nil-safe)

(defsubst lyskom-create-empty-misc-info ()
  "Create an empty misc-info."
  (lyskom-create-misc-info nil nil nil nil nil nil nil nil nil nil))



;;; ================================================================
;;;                                time

(def-komtype time 
  ((sec         :read-only t)
   (min         :read-only t)
   (hour        :read-only t)
   (mday        :read-only t)
   (mon         :read-only t)
   (year        :read-only t)
   (wday        :read-only t)
   (yday        :read-only t :write-only t)
   (isdst       :read-only t :write-only t)
   &optional
   (zone        :read-only t))
  :nil-safe)

(defun lyskom-create-time-from-utc (sec min hour mday mon year 
                                        wday yday isdst &optional zone)
  (if lyskom-server-uses-utc
      (let* ((date (decode-time (encode-time sec min hour mday mon year 0))))
        (unless (eq mday (elt date 3))
          (setq yday (if (< (or (car (current-time-zone)) 0) 0)
                         (- yday 1) (+ yday 1)))
          (cond ((< yday 1) (setq yday (lyskom-days-in-year (elt date 5))))
                ((> yday (lyskom-days-in-year year)) (setq yday 1))))

        (lyskom-create-time (elt date 0) ; sec
                            (elt date 1) ; min
                            (elt date 2) ; hour
                            (elt date 3) ; mday
                            (elt date 4) ; mon
                            (elt date 5) ; year
                            (elt date 6) ; dow
                            yday        ; yday
                            (elt date 7) ; dst
                            nil)
        )
    (lyskom-create-time sec min hour mday mon year
                        wday yday isdst nil)))


;;; ================================================================
;;;                               privs

(def-komtype privs 
  ((wheel       :read-only t)
   (admin       :read-only t)
   (statistic   :read-only t)
   (create_pers :read-only t)
   (create_conf :read-only t)
   (change_name :read-only t)
   (flg7        :read-only t)
   (flg8        :read-only t)
   (flg9        :read-only t)
   (flg10       :read-only t)
   (flg11       :read-only t)
   (flg12       :read-only t)
   (flg13       :read-only t)
   (flg14       :read-only t)
   (flg15       :read-only t)
   (flg16       :read-only t))
  :nil-safe)


;;; ================================================================
;;;                            flags

(def-komtype session-flags
  ((invisible           :read-only t)
   (user_active_used    :read-only t)
   (user_absent         :read-only t)
   (reserved3           :read-only t)
   (reserved4           :read-only t)
   (reserved5           :read-only t)
   (reserved6           :read-only t)
   (reserved7           :read-only t))
  :nil-safe)

(def-komtype dynamic-session-info
  ((session             :read-only t)
   (person              :read-only t)
   (working-conference  :read-only t)
   (idle-time           :read-only t)
   (flags               :read-only t)
   (what-am-i-doing     :read-only t))
  :nil-safe)

(def-komtype static-session-info
  ((username            :read-only t)
   (hostname            :read-only t)
   (ident-user          :read-only t)
   (connection-time     :read-only t))
  :nil-safe)

;;; ================================================================
;;;                            flags

(def-komtype flags
  ((unread_is_secret    :read-only t)
   (flg2                :read-only t)
   (flg3                :read-only t)
   (flg4                :read-only t)
   (flg5                :read-only t)
   (flg6                :read-only t)
   (flg7                :read-only t)
   (flg8                :read-only t))
  :nil-safe)


;;; ================================================================
;;;                             membership


;;; Constructor:

(def-komtype member-list 
  ((members     :read-only t))
  :nil-safe)

(def-komtype member
  ((pers-no             :read-only t)
   (created-by          :read-only t)
   (created-at          :read-only t)
   (membership-type     :read-only t))
  :nil-safe)

;;;
;;; Note that message-flag here is *not* the same as in the
;;; protocol. The value of message-flag is computed from
;;; message-invert in the protocol when received and when
;;; sent.
;;;

(def-komtype membership-type
  (invitation
   passive
   secret
   message-flag
   (rsv2                :read-only t)
   (rsv3                :read-only t)
   (rsv4                :read-only t)
   (rsv5                :read-only t))
  :nil-safe)

(def-komtype membership 
  (position 
   (last-time-read)
   (conf-no             :read-only t)
   priority 
   (last-text-read      :read-only t)
   read-texts
   (created-by          :read-only t)
   (created-at          :read-only t)
   (type                :read-only t))
  :nil-safe)

;;; Special stuff

(defun lyskom-member-list-find-member (person members)
  (when members
    (lyskom-traverse member (member-list->members members)
      (when (eq person (member->pers-no member))
        (lyskom-traverse-break member)))))



;;; ================================================================
;;;                               map


(def-komtype map 
  ((first-local         :read-only t)
   (text-nos            :read-only t))
  :nil-safe)

;;; Concat:

(defsubst lyskom-map-concat (&rest maps)
  "Take any number of MAPS and return a new map which is the sum of the maps.
Args: &rest MAPS.
The MAPS must be consecutive. No gaps or overlaps are currently allowed."
  (if (null maps)
      (lyskom-create-map 1 [])
    (let* ((first (map->first-local (car maps)))
	   (high (+ first (length (map->text-nos (car maps)))))
	   (maplist (list (map->text-nos (car maps))))
	   (maps (cdr maps)))
      (while maps
	(if (/= (map->first-local (car maps))
		high)
	    (signal 'lyskom-internal-error '("lyskom-map-concat")))
	(setq maplist (nconc maplist (list (map->text-nos (car maps)))))
	(setq high (+ high (length (map->text-nos (car maps)))))
	(setq maps (cdr maps)))
      (lyskom-create-map first (apply 'vconcat maplist)))))


;;; ================================================================
;;; Text-Mapping support

(def-komtype text-mapping
  ((range-begin         :read-only t)
   (range-end           :read-only t)
   (size                :read-only t)
   (later-texts-exist   :read-only t)
   (type                :read-only t)
   (block               :read-only nil)))

(defsubst lyskom-create-text-pair (local global) (cons local global))
;;UNUSED: text-pair->local-number
(defsubst text-pair->local-number (pair) (car pair))
;;UNUSED: text-pair->global-number
(defsubst text-pair->global-number (pair) (cdr pair))

(defsubst text-mapping->block-size (map)
  (if (eq (text-mapping->type map) 'dense)
      (length (map->text-nos (text-mapping->block map)))
    (length (text-mapping->block map))))

;;UNUSED: text-mapping->local-to-global
(defun text-mapping->local-to-global (map local)
  (cond ((or (< local (text-mapping->range-begin map))
             (> local (text-mapping->range-end map))) nil)
        ((eq (text-mapping->type map) 'sparse)
         (cdr (assq local (text-mapping->block map))))

        ((eq (text-mapping->type map) 'dense)
         (let ((result (aref (text-mapping->block map)
                             (- local (text-mapping->range-begin map)))))
           (and (not (zerop result)) result)))))

(defun text-mapping->global-numbers (map)
  (cond ((eq (text-mapping->type map) 'sparse)
         (mapcar 'cdr (text-mapping->block map)))
        ((eq (text-mapping->type map) 'dense)
         (let ((result nil))
           (lyskom-traverse el (map->text-nos (text-mapping->block map))
             (unless (zerop el) (setq result (cons el result))))
           (nreverse result)))))

(defun text-mapping->global-to-local (map global)
  (cond ((eq (text-mapping->type map) 'sparse)
         (cdr (rassq global (text-mapping->block map))))
        ((eq (text-mapping->type map) 'dense)
         (let ((i (text-mapping->range-begin map))
               (result nil))
           (while (< i (text-mapping->range-end map))
             (if (eq (aref (map->text-nos (text-mapping->block map)) 
                           (- i (text-mapping->range-begin map)))
                     global)
                 (setq result i i (text-mapping->range-end map))
               (setq i (1+ i))))
           result))))

(defun text-mapping->remove-local (map local)
  (cond ((eq (text-mapping->type map) 'dense)
         (setq local (- local (text-mapping->range-begin map)))
         (when (and (>= local 0)
                    (< local (text-mapping->block-size map)))
           (aset (map->text-nos (text-mapping->block map)) local 0)))

        ((eq (text-mapping->type map) 'sparse)
         (let ((el (assq local (text-mapping->block map))))
           (when el
             (set-text-mapping->block map 
                                      (delq el (text-mapping->block map))))))))

(def-komtype    text-mapping-iterator
  ((map         :read-only t)
   (next-value  :automatic nil)
   (state       :automatic nil))
  :create-hook (text-mapping-iterator->init OBJECT))

(defun text-mapping->iterator (map)
  (lyskom-create-text-mapping-iterator map))

(defun text-mapping-iterator->init (iter)
  (if (eq (text-mapping->type (text-mapping-iterator->map iter)) 'dense)
      (set-text-mapping-iterator->state iter 0)
    (set-text-mapping-iterator->state 
     iter (text-mapping->block (text-mapping-iterator->map iter)))))

(defun text-mapping-iterator->next (iter)
  (when (text-mapping-iterator->state iter)
    (let ((map (text-mapping-iterator->map iter)))
      (prog1
          (if (eq (text-mapping->type map) 'dense)
              (lyskom-create-text-pair (+ (text-mapping-iterator->state iter)
                                          (text-mapping->range-begin map))
                                       (aref (map->text-nos (text-mapping->block map))
                                             (text-mapping-iterator->state iter)))
            (car (text-mapping-iterator->state iter)))
        (text-mapping-iterator->step iter)))))

(defun text-mapping-iterator->step (iter)
  (let ((map (text-mapping-iterator->map iter))
        (state (text-mapping-iterator->state iter)))
    (cond 
     ((eq (text-mapping->type map) 'dense)
      (setq state (1+ state))
      (while (and (< state (text-mapping->block-size map))
                  (eq 0 (aref (map->text-nos (text-mapping->block map)) state)))
        (setq state (1+ state)))
      (set-text-mapping-iterator->state 
       iter (and (< state (text-mapping->block-size map)) state)))

     ((eq (text-mapping->type map) 'sparse)
      (set-text-mapping-iterator->state iter (cdr state))))))



;;; ================================================================
;;;                            mark

(def-komtype mark 
  ((text-no     :read-only t)
   (mark-type   :read-only t))
  :nil-safe)

;;; Utilities

(defun mark->symbolic-mark-type (mark)
  (lyskom-symbolic-mark-type-string (mark->mark-type mark)))

;;; ================================================================
;;;                           who-info

(def-komtype who-info 
  ((pers-no         :read-only t)
   (working-conf    :read-only t)
   (connection      :read-only t)
   (doing-what      :read-only t)
   (username        :read-only t)
   &optional
   (hostname        :read-only t)
   (ident-user      :read-only t))
  :nil-safe)


;;; ================================================================
;;;                         session-info

(def-komtype session-info 
  ((pers-no         :read-only t)
   (working-conf    :read-only t)
   (connection      :read-only t)
   (doing           :read-only t)
   (username        :read-only t)
   (hostname        :read-only t)
   (ident-user      :read-only t)
   (idletime        :read-only t)
   (connect-time    :read-only t))
  :nil-safe)



;;; ================================================================
;;;                            text-list

;;; Constructor:

(def-komtype text-list 
  (texts-internal 
   (tail :automatic (last texts-internal))
   (length-internal :automatic (length texts-internal)))
  :nil-safe)

(defsubst text-list->texts (text-list)
  (text-list->texts-internal text-list))

(defsubst set-text-list->texts (text-list texts)
  (set-text-list->texts-internal text-list texts)
  (set-text-list->tail text-list (last texts))
  (set-text-list->length-internal text-list (length texts)))

(defsubst text-list->empty (text-list)
  "Return t if TEXT-LIST is empty."
  (null (text-list->texts text-list)))

(defsubst text-list->length (text-list)
  "Return the length of TEXT-LIST."
  (unless (text-list->length-internal text-list)
    (set-text-list->length-internal text-list
                                    (length (text-list->texts text-list))))
  (text-list->length-internal text-list))

(defsubst text-list->memq (text-list no)
  "Return non-nil if TEXT-LIST contains TEXT-NO."
  (memq no (text-list->texts text-list)))

(defsubst text-list->delq (text-list no)
  "Remove text NO from TEXT-LIST."
  (set-text-list->texts-internal text-list (delq no (text-list->texts text-list)))
  (when (eq no (car (text-list->tail text-list)))
    (set-text-list->tail text-list (last (text-list->texts text-list))))
  (set-text-list->length-internal text-list nil))

;;; FIXME: It would be useful if text-list->append would only append
;;; FIXME: unique numbers and not blindnly concatenate the two lists.
;;; FIXME: That would probably take care of any remaining problems
;;; FIXME: where some texts are listed twice in the to do list.

(defsubst text-list->append (text-list texts)
  "Destructively append TEXTS to the end of TEXT-LIST."
  (if (text-list->texts text-list)
      (progn
        (nconc (or (text-list->tail text-list)
                   (text-list->texts-internal text-list))
               texts)
        (when (text-list->length-internal text-list)
          (set-text-list->length-internal 
           text-list
           (+ (text-list->length-internal text-list)
              (length texts)))))
    (set-text-list->texts-internal text-list texts)
    (set-text-list->length-internal text-list nil))
  (set-text-list->tail text-list (last texts)))

(defun text-list->trim-head (tlist n)
  "Destructively remove all but the N last elements from TLIST.
Do nothing if the TLIST is less than N elements long."
  (set-text-list->texts-internal tlist (nthcdr (max (- (text-list->length tlist) n) 0)
                                               (text-list->texts tlist)))
  (when (text-list->length-internal tlist)
    (set-text-list->length-internal tlist n))
  (unless (text-list->texts tlist)
    (set-text-list->length-internal tlist 0)
    (set-text-list->tail tlist nil)))


;;; ================================================================
;;;                         version-info


;;; Constructor:

(def-komtype version-info 
  ((protocol-version    :read-only t)
   (server-software     :read-only t)
   (software-version    :read-only t))
  :nil-safe)


;;; ================================================================
;;;                          server-info

(def-komtype server-info 
  ((version         :read-only t)
   (conf-pres-conf  :read-only t)
   (pers-pres-conf  :read-only t)
   (motd-conf       :read-only t)
   (kom-news-conf   :read-only t)
   motd-of-lyskom
   &optional
   (aux-item-list   :read-only t))
  :nil-safe)


;;; ================================================================
;;;                            conf-z-info-list

(def-komtype conf-z-info-list
  ((conf-z-infos :read-only t))
  :nil-safe)


;;; ================================================================
;;;                            conf-z-info

;;; Constructor:

(def-komtype conf-z-info 
  ((name            :read-only t)
   (conf-type       :read-only t)
   (conf-no         :read-only t))
  :nil-safe)


(def-komtype stats-description
  ((what            :read-only t)
   (when            :read-only t))
  :nil-safe)

(def-komtype stats
  ((average         :read-only t)
   (ascent-rate     :read-only t)
   (descent-rate    :read-only t))
  :nil-safe)

(def-komtype server-stats
  (&optional
   (what            :default nil)
   (when            :default nil)
   (values          :default nil))
  :nil-safe)

(def-komtype static-server-info
  ((boot-time       :read-only t)
   (save-time       :read-only t)
   (db-status       :read-only t)
   (existing-texts  :read-only t)
   (highest-text-no :read-only t)
   (existing-confs  :read-only t)
   (existing-persons :read-only t)
   (highest-conf-no :read-only t))
  :nil-safe)

(def-komtype scheduling-info
  ((priority        :read-only t)
   (weight          :read-only t))
  :nil-safe)


;;; ================================================================



;;; Utilities

(defun text-stat-find-aux (text-stat tag &optional person)
  "Return a list containing the aux items in TEXT-STAT with tag TAG.
If PERSON is non-nil return only those items created by person.
Args: TEXT-STAT TAG PERSON"
  (let ((result nil)
        (items (text-stat->aux-items text-stat)))
    (while items
      (when (and (eq tag (aux-item->tag (car items)))
                 (not (aux-item-flags->deleted
                       (aux-item->flags (car items))))
                 (or (null person)
                     (eq person (aux-item->creator (car items)))))
        (setq result (cons (car items) result)))
      (setq items (cdr items)))
    (nreverse result)))

(defun conf-stat-find-aux (conf-stat tag &optional person)
  "Return a list containing the aux items in CONF-STAT with tag TAG.
If PERSON is non-nil return only those items created by person.
Args: CONF-STAT TAG PERSON"
  (let ((result nil)
        (items (conf-stat->aux-items conf-stat)))
    (while items
      (when (and (eq tag (aux-item->tag (car items)))
                 (not (aux-item-flags->deleted
                       (aux-item->flags (car items))))
                 (or (null person)
                     (eq person (aux-item->creator (car items)))))
        (setq result (cons (car items) result)))
      (setq items (cdr items)))
    (nreverse result)))

(defun lyskom-is-recipient (text-stat conf-no)
  "Return non-nil if TEXT-STAT has CONF-NO as a recipient."
  (let ((result nil))
    (lyskom-traverse misc
        (text-stat->misc-info-list text-stat)
      (when (and (memq (misc-info->type misc) lyskom-recpt-types-list)
                 (eq (misc-info->recipient-no misc) conf-no))
        (setq result t)))
    result))



(provide 'lyskom-types)
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: clienttypes.el
;;;;
;;;; This file contains primitives for the different data types
;;;; in the lyskom elisp client. The types in here are only used
;;;; within this lyskom client.
;;;;
;;;; Authors: Linus Tolke and Inge Wallin
;;;;

;;; ================================================================
;;;                            read-info

;;; A read-info is used by to tell the client which text to 
;;; show next and in which order to step through the conferences 
;;; with unread texts. New read-infos are created and deleted
;;; all the time within a session.
;;;
;;; The type is one of the following:
;;;   REVIEW      - Default review type, created by the kom-review-by-to
;;;		    and the kom-review-comments
;;;   REVIEW-TREE - List of texts created by one of the tree-reading 
;;;		    commands: kom-find-root-review, kom-review-tree
;;;   REVIEW-MARK - List of texts created by the review-mark command
;;;   REVIEW-FAQ  - List of texts created by kom-review-faq or similar
;;;   REVIEW-FAQ-TREE  - Like REVIEW-TREE but for FAQs
;;;   COMM-IN     - Type containing the list of comments to a text
;;;   FOOTN-IN    - Type containing the list of footnotes to a text
;;;   CONF        - Basic type of unread in a conf.
;;;   RE-EDIT-TEXT - Re-edit a failed submission. Buffer is in misc.
;;;   PRI-SESSION - Go to prioritized session.
;;;
;;;   The types REVIEW-TREE, COMM-IN and FOOTN-IN are created for new for
;;;   every text read (recursively) when appropriate.
;;;
;;;   The difference between REVIEW, REVIEW-MARK and REVIEW-FAQ is just
;;;   that there generate different prompts and different text from
;;;   kom-review-stack.
;;;


;;; read-info

(def-komtype read-info 
  ((type                :read-only t)
   (conf-stat           :read-only t)
   priority 
   (text-list           :read-only t)
   &optional 
   (comm-to             :read-only t)
   forward 
   (unfetched-texts     :read-only t :automatic nil)
   (misc                :read-only t))
  :nil-safe)


(defsubst read-info-append-text-list (read-info texts)
  (text-list->append (read-info->text-list read-info) texts))

(defsubst read-info-enter-text-last (read-info text-no)
  (read-info-append-text-list read-info (list text-no)))


;;; ================================================================
;;;                          read-list
;;;
;;; We don't use def-komtype here because the code below relies on 
;;; read-lists being a cons of READ-LIST and the data.
;;;

;;; Constructor:

(defsubst lyskom-create-read-list ()
  "Create an empty read-list."
  (cons 'READ-LIST nil))


;;; Predicates:

(defsubst read-list-isempty (read-list)
  "Return t if READ-LIST is empty, otherwise return nil."
  (null (cdr read-list)))


;;; Selectors:

(defsubst read-list->first (read-list)
  "Return the first entry in READ-LIST, or nil if empty."
  (car-safe (cdr read-list)))


(defsubst read-list->nth (read-list n)
  "Args: READ-LIST N
Return element N in READ-LIST or nil if outside the range.
The range of valid values for N is [0, num-entries - 1]."
  (elt (cdr read-list) n))


(defsubst read-list->all-entries (read-list)
  "Return a list of all entries in READ-LIST."
  (cdr read-list))


;;; Other functions:

(defsubst read-list-length (read-list)
  "Return the number of entries in READ-LIST."
  (length (read-list->all-entries read-list)))


;;; Modifiers:

(defsubst set-read-list-empty (read-list)
  "Empty READ-LIST destructively."
  (let ((tmp (cdr read-list)))
    (setcdr read-list nil)
    (lyskom-traverse read-info tmp
      (when (eq 'CONF (read-info->type read-info))
        (lp--maybe-update-unreads (conf-stat->conf-no
                                   (read-info->conf-stat read-info)))))))

(defsubst set-read-list-del-first (read-list)
  "Delete the first entry of READ-LIST if there is one."
  (let ((el (car (cdr read-list))))
    (if (cdr read-list)
        (setcdr read-list (cdr (cdr read-list))))
    (when (and el (eq (read-info->type el) 'CONF))
      (lp--maybe-update-unreads (conf-stat->conf-no
                                 (read-info->conf-stat el))))))


(defsubst read-list-enter-first (read-info read-list)
  "Enter READ-INFO first into READ-LIST."
  (setcdr read-list (cons read-info (cdr read-list)))
  (when (eq 'CONF (read-info->type read-info))
    (lp--maybe-update-unreads (conf-stat->conf-no
                               (read-info->conf-stat read-info))))
  )


(defun read-list-enter-text (text-no recipient rlist)
  "Args: TEXT-NO RECIPIENT RLIST.
Add the new text TEXT-NO to any RECIPIENT found in RLIST.
RECIPIENT is a conf-stat.
Returns t if there was a conference to insert this text into."
  (let ((inserted nil))
    (lyskom-traverse
     read-info (cdr rlist)
     (cond
      ((and (eq 'CONF (read-info->type read-info))
	    (= (conf-stat->conf-no recipient)
	       (conf-stat->conf-no (read-info->conf-stat read-info))))
       (read-info-enter-text-last read-info text-no)
       (setq inserted t))))
    (when inserted (lp--maybe-update-unreads (conf-stat->conf-no recipient)))
    inserted))


(defun read-list-delete-text (text-no rlist)
  "Destructively delete all occurances of TEXT-NO from RLIST.
RLIST is a list of read-info.
Entries of the type REVIEW, REVIEW-FAQ, REVIEW-TREE or REVIEW-MARK are
not changed except if they were empty in which case they are removed.
Returns the modified RLIST.
TEXT-NO may be nil, in which case only empty read-infos on RLIST are removed."
  (let* ((prev rlist)			;"Previous" cons-celll
	 (curr (cdr rlist)))		;Current cons-cell
    (while curr
      (when text-no
        (cond
         ((let ((type (read-info->type (car curr)))) 
					; Don't change REVIEW et c.
            (memq type lyskom-review-types-list)))
         (t				; Do change all other entries.
          (let ((tl  (read-info->text-list (car curr))))
            (when (text-list->memq tl text-no)
              (text-list->delq tl text-no)
              (lp--maybe-update-unreads
               (conf-stat->conf-no (read-info->conf-stat (car curr)))))))))
      
      ;; Delete this element from RLIST if the text-list became or was empty.
      
      (if (text-list->empty (read-info->text-list (car curr)))
	  (setcdr prev (cdr curr))
	(setq prev curr))
      (setq curr (cdr curr))))
  rlist)


(defun read-list-enter-read-info (read-info rlist &optional before)
  "Destructively insert READ-INFO in RLIST, sorted by priority.
RLIST is a list of read-info.
Args: READ-INFO RLIST &optional BEFORE.
A new item with the same priority as an item that is alreay on the list
will nomally be inserted after the old one, but if BEFORE is non-nil it
will be inserted before it."
  (let ((pri (+ (if before 0 -1)
		(read-info->priority read-info)))
	(continue t)
	(conf-stat (read-info->conf-stat read-info))
	(type (read-info->type read-info)))
    (while continue
      (cond
       ((and (eq type 'CONF)
	     (eq (read-info->type (car (cdr rlist))) 'CONF)
	     (eq (conf-stat->conf-no conf-stat)
                 (conf-stat->conf-no (read-info->conf-stat
                                      (car (cdr rlist))))))
	(read-info-append-text-list
	 (car (cdr rlist))
	 (text-list->texts (read-info->text-list read-info)))
        (lp--maybe-update-unreads (conf-stat->conf-no conf-stat))
	(setq continue nil)) 

       ((null (cdr rlist)) 
	(setcdr rlist (list read-info))
	(setq continue nil))
       ((>= pri (read-info->priority (car (cdr rlist))))
	(setcdr rlist (cons read-info (cdr rlist)))
	(setq continue nil))
       (t 
	(setq rlist (cdr rlist)))))

    (when (eq type 'CONF)
      (lp--maybe-update-unreads (conf-stat->conf-no conf-stat)) )))


(defun read-list-delete-read-info (conf-no rlist)
  "Destructively removes all the entries for the conf CONF-NO in RLIST.
RLIST is a list of read-info."
  (while (cdr rlist)
    (if (eq (conf-stat->conf-no (read-info->conf-stat
				      (car (cdr rlist))))
	    conf-no)
	(setcdr rlist (cdr (cdr rlist)))
      (setq rlist (cdr rlist))))
  (lp--maybe-update-unreads conf-no))
 
(defun read-list-rotate (read-list)
  "Put the first element of READ-LIST last in the same list. The second
element will be the new first element."
  (if (> (length read-list) 2)
      (let ((first (cdr read-list))
	    (last  (cdr read-list)))
	(while (cdr last)
	  (setq last (cdr last)))
	(setcdr last first)
	(setcdr read-list (cdr first))
	(setcdr first nil)))
  read-list)    
	  

;;; ================================================================
;;;                      A simple queue

;;;
;;; This is a simple implementation of a queue.  The only thing you
;;; can do with it is stuff things at the back of it and remove
;;; things from the front of it.  (...and of course do a few tests)
;;;

;;; Author: Inge Wallin


(defun lyskom-queue-create ()
  "Create an empty queue."
  (cons 'QUEUE (cons nil nil)))


(defsubst lyskom-queue-enter (queue element)
  "Enter last into the queue QUEUE the ELEMENT."
  (let ((elementcell (cons element nil)))
    (if (null (car (cdr queue)))
	; QUEUE is empty
	(setcar (cdr queue)
		(setcdr (cdr queue) 
			elementcell))
      (setcdr (cdr (cdr queue))
	      elementcell)
      (setcdr (cdr queue)
	      elementcell))))


(defsubst lyskom-queue-p (queue)
  "Return t if QUEUE is a queue, otherwise return nil."
  (eq (car-safe queue) 'QUEUE))


(defsubst lyskom-queue-isempty (queue)
  "Return t if QUEUE is empty, otherwise return nil."
  (null (car (cdr queue))))


(defsubst lyskom-queue->first (queue)
  "Return the first element of QUEUE or nil if it is empty."
  (car-safe (car (cdr queue))))


(defsubst lyskom-queue->all-entries (queue)
  "Return all elements of QUEUE (nil if it is empty)."
  (car-safe (cdr queue)))


;;UNUSED: lyskom-queue->last
(defsubst lyskom-queue->last (queue)
  "Return the lastelement of QUEUE or nil if it is empty."
  (car-safe (cdr (cdr queue))))


(defun lyskom-queue-remove-matching (queue pred)
  "Remove all elements from QUEUE that satisfy PRED."
  (let ((prev nil)
        (ptr (car (cdr queue))))
    (while ptr
      (if (funcall pred (car ptr))
        (progn (if prev
                   (setcdr prev (cdr ptr))
                 (setcar (cdr queue) (cdr ptr)))
               (unless (cdr ptr)
                 (setcdr (cdr queue) prev)))
        (setq prev ptr))
      (setq ptr (cdr ptr)))))


(defsubst lyskom-queue-make-empty (queue)
  "Make the queue QUEUE empty."
  (setcdr queue (cons nil nil)))

;;UNUSED: lyskom-queue-set-data
(defsubst lyskom-queue-set-data (queue data)
  (lyskom-queue-make-empty queue)
  (setcdr queue (cons data (last data))))


(defsubst lyskom-queue-delete-first (queue)
  "Delete the first element of QUEUE and return it. If QUEUE is empty
return nil and do nothing."
  (if (lyskom-queue-isempty queue)
      nil
    (prog1
	(lyskom-queue->first queue)
      (setcar (cdr queue)
	      (cdr (car (cdr queue))))
      (if (null (car (cdr queue)))
	  (setcdr (cdr queue) nil)))))


;;; ================================================================
;;;                           A simple stack

;;; 
;;; This is an implementation of a simple stack.
;;;

;;; Author: Inge Wallin


(defun lyskom-stack-create ()
  "Create an empty stack"
  (cons 'STACK nil))


(defun lyskom-stack-p (stack)
  "Return t if STACK is a lyskom-stack, otherwise return nil."
  (eq (car-safe stack) 'STACK))


(defun lyskom-stack-push (stack element)
  "Push the second arg ELEMENT onto the first arg STACK"
  (setcdr stack (cons element (cdr stack))))


(defun lyskom-stack-pop (stack)
  "Remove the topmost element from STACK and return it. If the stack
is empty, return nil"
  (prog1
      (car-safe (cdr stack))
    (setcdr stack (cdr-safe (cdr stack)))))


(defun lyskom-stack->top (stack)
  "Return the topmost element of STACK or nil if it is empty."
  (car-safe (cdr stack)))


;;UNUSED: lyskom-stack->length
(defun lyskom-stack->length (stack)
  "Return the number of elements on STACK."
  (length (cdr stack)))

;;UNUSED: lyskom-stack->nth
(defun lyskom-stack->nth (stack n)
  "Return element no (second arg) N of the stack (first arg) STACK.
N counts from zero. If the length of STACK is less than N, nil is returned."
  (nth n (cdr stack)))


(defun lyskom-stack->all-entries (stack)
  "Return a list of all entries in STACK.
The element last pushed is first in the list."
  (cdr stack))


(defsubst lyskom-stack-isempty (stack)
  "Returns non-nil if the STACK is empty."
  (not (cdr stack)))



;;; ================================================================
;;;			format-state

(def-komtype format-state
  (format-string
   start
   argl
   length
   result
   delayed-propl
   delayed-overlays
   delayed-content
   depth)
  :nil-safe)

;;; ================================================================


;;; ======================================================================
;;;
;;; collector
;;;

;;; A collector is used when a handler needs to pass information
;;; back to a function using asynchronous calls. You could use
;;; dynamically scoped variables, but that causes problems when
;;; the user quits before all handlers have been called since the
;;; result variable will be out of scope for the remaining handlers.
;;; The best-case scenario will be a crash. The worst-case scenario
;;; is when the handler clobbers another variable with the same name
;;; that has come into scope.
;;;
;;; So, so collect a number of results, do the following:
;;;
;;;    (setq <result> (make-collector))
;;;    (<loop-function>
;;;        (initiate-<någonting> '<queue> <function> <argl> <result>))
;;;    (lyskom-wait-queue '<queue>)
;;;    (<use> (collector->value <result>))
;;;
;;; where function is something like this:
;;;
;;;    (defun <funktion> (data-från-servern collector)
;;;        (set-collector->value collector
;;;                              (cons (<behandla> data-från-servern)
;;;                                    (collector->value collector))))
;;;
;;; or shorter,
;;;
;;;    (defun <funktion> (data-från-servern collector)
;;;        (collector-push (<behandla> data-från-servern)))
;;;
;;; This sidestepping protects the handler from scope changes.
;;;


(defun make-collector ()
  "Create a data type for collecting asynchronous results safely"
  (cons 'COLLECTOR nil))

(defsubst collector->value (collector)
  "Get the current value of a collector"
  (cdr collector))

(defsubst set-collector->value (collector value)
  "Set the calue of a collector"
  (setcdr collector value))

(defun collector-push (value collector)
  "Push VALUE onto the front of COLLECTOR's value"
  (setcdr collector (cons value (cdr collector))))


;;; ================================================================
;;; Specification for text links (cached)
;;;
;;; We don't use def-komtype since the accessors should be able to
;;; work on user settings too.

(defun lyskom-create-lyskom-text-link (pattern replacement 
                                               highlight ignore-case)
  (cons 'LYSKOM-TEXT-LINK (vector pattern replacement highlight ignore-case)))

(defsubst lyskom-text-link->pattern (link)
  (if (symbolp (car link)) (elt (cdr link) 0) (elt link 0)))

(defsubst lyskom-text-link->replacement (link)
  (if (symbolp (car link)) (elt (cdr link) 1) (elt link 1)))

(defsubst lyskom-text-link->highlight (link)
  (if (symbolp (car link)) (elt (cdr link) 2) (elt link 2)))

(defsubst lyskom-text-link->ignore-case (link)
  (if (symbolp (car link)) (elt (cdr link) 3) (elt link 3)))





(eval-and-compile (provide 'lyskom-clienttypes))

;;; clienttypes.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: faces.el
;;;;
;;;; Code to handle faces in LysKOM
;;;;
;;;; ================================================================
;;;; Faces in LysKOM
;;;;
;;;; There are a bunch of face variables that define witch face to use
;;;; in various roles. By using variables as a secnod level of
;;;; indirection it is possible to have divveret faces in different
;;;; buffers.
;;;;
;;;; The concept of face schemes that was used in versions up to
;;;; and including 0.47.1 is completely out the door.
;;;; ================================================================

;;;; TODO:
;;;;
;;;; Setup faces from resources? Do we need that any more? Or will
;;;; defface take care of it for us?
;;;;
;;;; Remove old code.
;;;;
;;;; In the customization buffer we need a new widget type that
;;;; can open customization of a face.
;;;;
;;;; The customization buffer should provide a choice for the
;;;; user of nil, the "factory default" and a specific face.
;;;;
;;;; Other than that, it's more or less done.

;;; ================================================================
;;; Face variables
;;;
;;; Here's a list of all face variables. Seems to be useful on occasion.
;;;
;;; kom-active-face
;;; kom-url-face
;;; kom-me-face
;;; kom-highlight-face
;;; kom-text-face
;;; kom-subject-face
;;; kom-text-no-face
;;; kom-friends-face
;;; kom-morons-face
;;; kom-presence-face
;;; kom-first-line-face
;;; kom-warning-face
;;; kom-mark-face
;;; kom-dim-face
;;; kom-text-body-face
;;; kom-dashed-lines-face
;;; kom-async-text-body-face
;;; kom-async-dashed-lines-face

(def-kom-var kom-active-face 'kom-face--default--active-face
  "*Face used for most \"clickable\" areas.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--active-face.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-active-strikethrough-face 'kom-face--default--active-strikethrough-face
  "*Face used for some \"clickable\" areas.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--active-strikethrough-face.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-active-highlight-face 'kom-face--default--active-highlight-face
  "*Face used for some \"clickable\" areas.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--active-highlight-face.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-url-face 'kom-face--default--url-face
  "*Face used for URLs.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--url-face.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-me-face 'kom-face--default--me-face
  "*Face used to show your own name.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--me-face.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-highlight-face 'kom-face--default--highlight-face
  "*Face used for various highlights.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--highlight.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-subject-face 'kom-face--default--subject-face
  "*Face used for subject lines.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--subject-fac.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-text-no-face 'kom-face--default--text-no-face
  "*Face used for text numbers.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--text-no-fac.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-friends-face 'kom-face--default--friends-face
  "*Face used for people in kom-friends.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--friends-fac.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-morons-face 'kom-face--default--morons-face
  "*Face used for people in kom-morons.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--morons-face.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-presence-face 'kom-face--default--presence-face
  "*Face used for presence messages.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--presence-f.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-first-line-face 'kom-face--default--first-line-face
  "*Face used for the first line header of each text.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--first-li.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-warning-face 'kom-face--default--warning-face
  "*Face used to display important warnings.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--warning-fac.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-mark-face 'kom-face--default--mark-face
  "*Face used for temporary marks.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--mark-face.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-dim-face 'kom-face--default--dim-face
  "*Face used to display dimmed items, such as passive memberships.
The value should be either a symbol naming a face or nil.
The default value is kom-face--default--dim-face.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-text-body-face nil
  "*Face to use to modify text bodies.
The value should be either a symbol naming a face or nil.

If this is set to nil, a face with a background that differs ever
so slightly from the default background will be created.

The default value is nil.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-dashed-lines-face nil
  "*Face to use to modify the dashed lines before and after texts.
The value should be either a symbol naming a face or nil.

If this is set to nil, a face with a background that differs slightly
from the default background will be created.

The default value is nil.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-async-text-body-face nil
  "*Face to use to modify asynchronos message bodies.
The value should be either a symbol naming a face or nil.

If this is set to nil, a face with a background that differs ever
so slightly from the default background will be created.

The default value is nil.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var kom-async-dashed-lines-face nil
  "*Face to use to modify dashed lines around asynchronous messsages.
The value should be either a symbol naming a face or nil.

If this is set to nil, a face with a background that differs slightly
from the default background will be created.

The default value is nil.

This is a LysKOM face variable."
  server
  inherited)

(def-kom-var lyskom-default-text-body-face nil
  "Default text body face."
  inherited)

(def-kom-var lyskom-default-async-text-body-face nil
  "Default text body face."
  inherited)

(def-kom-var lyskom-default-dashed-lines-face nil
  "Default text body face."
  inherited)

(def-kom-var lyskom-default-async-dashed-lines-face nil
  "Default text body face."
  inherited)


;;; ================================================================
;;; Main entry points

(defun lyskom-setup-faces-for-buffer ()
  "Set up faces for the current buffer."
    (setq lyskom-default-text-body-face nil)
    (setq lyskom-default-dashed-lines-face nil)
    (setq lyskom-default-async-text-body-face nil)
    (setq lyskom-default-async-dashed-lines-face nil)
    (condition-case nil
	(let ((faces (lyskom-generate-faces-for-background
		      (or (face-background 'default)
			  (lyskom-frame-property (selected-frame) 'background-color)
			  "#ffffff"))))
	  (setq lyskom-default-text-body-face
		(cdr (assq 'text-body faces))
		lyskom-default-dashed-lines-face
		(cdr (assq 'dashed-lines faces))
		lyskom-default-async-text-body-face
		(cdr (assq 'async-text-body faces))
		lyskom-default-async-dashed-lines-face
		(cdr (assq 'async-dashed-lines faces))))
      (error nil)))


;;; ================================================================
;;; Background faces
;;;
;;; The faces for text backgrounds cannot be hard-coded. It's OK
;;; to hard-code some known alternatives, but in the general case
;;; we're better off calculating the colors.
;;;
;;; The user can specify faces for these properties, but can also
;;; specify "default", which means calculate the face.
;;;

(defvar lyskom-background-faces nil
  "Pool of background faces.
This is an alist of `(COLOR . FACES)' elements, where COLOR is a
canonical representation of a background color and FACES is an
alist of automatically generated faces to use in that background.
Each element of FACES is of the form `(KEY . FACE-NAME)', where
KEY is one of `text-body', `dashed-lines', `async-text-body' or
`async-dashed-lines', and FACE-NAME is the name of the face to
use for the feature specified by KEY.

When the faces for a buffer are set, the client will look in this 
pool for the appropriate faces. If none are found, new faces will
be generated and stored in the pool.

This list is not consulted at any other time.

See `lyskom-background-colors' for additional information.")


(defvar lyskom-background-colors
  '(("#ffffff" 
     (text-body . "#f8f8ff")
     (dashed-lines . "#e8e8ff")
     (async-text-body . "#f8fff8")
     (async-dashed-lines . "#e8ffe8"))
    ("#000000"
     (text-body . "#080808")
     (dashed-lines . "#101010")
     (async-text-body . "#000020")
     (async-dashed-lines . "#101030")))
  "Specification for special background faces.

This is an alist of `(COLOR . SPEC)' elements, where COLOR is a color
name and SPEC is an alist specifying background colors to use when the
main background is COLOR. Elements in SPEC are `(KEY . COLOR)' where
KEY is one of `text-body', `dashed-lines', `async-text-body' or
`async-dashed-lines' and COLOR is the name of the background color to
use for the feature indicated by KEY.

This list is consulted only when generating new faces for a particular
background color. Changes to it after starting the client may have no
effect.

See `lyskom-background-faces' for additional information."
)

(defun lyskom-canonicalize-color (color)
  "Create a canonical string name for color COLOR.
COLOR must be a string (a color name)"
  (apply 'format 
         "#%02x%02x%02x"
         (mapcar (lambda (x) (lsh x -8))
                 (lyskom-color-values color))))

(defun lyskom-generate-faces-for-background (background)
  "Generate highlight faces for background BACKGROUND.
BACKGROUND must be a color name. This function updates
`lyskom-background-colors' and returns a list that has the same
structure as the value of an element in that list. See the documentation
for `lyskom-background-colors' for more information."
  (let* ((color (lyskom-canonicalize-color background))
         (cached (cdr (assoc color lyskom-background-faces))))
    (unless cached
      (let* ((text-body-face-name
              (intern (format "lyskom-generated-text-body-face-%s" color)))
             (dashed-lines-face-name
              (intern (format "lyskom-generated-dashed-lines-face-%s" color)))
             (async-text-body-face-name
              (intern (format "lyskom-generated-async-text-body-face-%s" color)))
             (async-dashed-lines-face-name
              (intern (format "lyskom-generated-async-dashed-lines-face-%s" color)))
             (predefined (cdr (assoc color lyskom-background-colors)))
             (weak (lyskom-get-color-highlight (lyskom-color-values background) 0.025))
             (strong (lyskom-get-color-highlight (lyskom-color-values background) 0.05))
             (text-body-color (or (cdr (assq 'text-body predefined)) weak))
             (dashed-lines-color (or (cdr (assq 'dashed-lines predefined)) strong))
             (async-text-body-color (or (cdr (assq 'async-text-body predefined)) weak))
             (async-dashed-lines-color (or (cdr (assq 'async-dashed-lines predefined)) strong))
             )
        (make-face text-body-face-name)
        (set-face-background text-body-face-name text-body-color)
        (make-face dashed-lines-face-name)
        (set-face-background dashed-lines-face-name dashed-lines-color)
        (make-face async-text-body-face-name)
        (set-face-background async-text-body-face-name async-text-body-color)
        (make-face async-dashed-lines-face-name)
        (set-face-background async-dashed-lines-face-name async-dashed-lines-color)
        (setq cached
              `((text-body ,text-body-face-name)
                (dashed-lines ,dashed-lines-face-name)
                (async-text-body ,async-text-body-face-name)
                (async-dashed-lines ,async-dashed-lines-face-name)))
        (setq lyskom-background-faces
              (cons (cons color cached) lyskom-background-faces))))
    cached))




;;; ================================================================
;;; Predefined faces
;;;
;;; There are two sets of predefined faces: the defaults and the
;;; user-defined faces. Users should not muck with the defaults.

(defface kom-face--default--active-face
  '((((background light))
     (:foreground "blue4")
     )
    (((background dark))
     (:foreground "lightblue")
     )
    )
  "Face suitable for most \"clickable\" areas.
Do not alter this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--active-strikethrough-face
  '((((background light))
     (:foreground "blue4"
      :strike-through t)
     )
    (((background dark))
     (:foreground "lightblue"
      :strike-through t)
     )
    )
  "Face suitable for some \"clickable\" areas.
Do not alter this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--active-highlight-face
  '((((background light))
     (:foreground "blue4"
      :weight bold)
     )
    (((background dark))
     (:foreground "lightblue"
      :weight bold)
     )
    )
  "Face suitable for some \"clickable\" areas.
Do not alter this face unless you know what you're doing."
  :group 'lyskom-faces
  )


(defface kom-face--default--url-face
  '((((background light))
     (:foreground "blueviolet")
     )
    (((background dark))
     (:foreground "moccasin")
     )
    )
  "Face suitable for URLs.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )


(defface kom-face--default--me-face
  '((((background light))
     (:foreground "blue3"
     :background "lavender"
     :weight bold)
     )
    (((background dark))
     (:foreground "gold"
     :background "black"
     :weight bold)
     )
    )
  "Face suitable for showing your own name.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )


(defface kom-face--default--highlight-face
  '((((background light))
     (:background "darkseagreen2")
     )
    (((background dark))
     (:background "darkblue")
     )
    )
  "Face suitable for various highlights.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--subject-face
  '((t nil))
  "Face suitable for subject lines.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--text-no-face
  '((((background light))
     (:foreground "blue4")
     )
    (((background dark))
     (:foreground "lightblue")
     )
    )
  "Face suitable for text numbers.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--friends-face
  '((((background light))
     (:foreground "blue3"
     :background "lavender")
     )
    (((background dark))
     (:foreground "red")
     )
    )
  "Face suitable for people in kom-friends.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--morons-face
  '((((background light))
     (:foreground "blue3"
     :background "#ffff60")
     )
    (((background dark))
     (:foreground "yellow")
     )
    )
  "Face suitable for people in kom-morons.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--presence-face
  '((((background light))
     (:foreground "dim gray"
     :slant italic)
     )
    (((background dark))
     (:foreground "gray"
     :slant italic)
     )
    )
  "Face suitable for presence messages.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )


(defface kom-face--default--mark-face
  '((((background light))
     (:foreground "blue3"
     :background "lavender")
     )
    (((background dark))
     (:foreground "gold"
     :background "black")
     )
    )
  "Face suitable for temporary marks.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )


(defface kom-face--default--first-line-face
  '((t (:weight bold)))
  "Face suitable for the first line header of each text.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--warning-face
  '((t (:weight bold :foreground "red")))
  "Face suitable for displaying important warnings.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )

(defface kom-face--default--dim-face
  '((t (:foreground "gray")))
  "Face suitable for dimmed items, such as passive memberships.
Do not change this face unless you know what you're doing."
  :group 'lyskom-faces
  )


;;; ----------------------------------------------------------------
;;; Dummy faces (copies of the default faces for uses to muck with.
;;;

(defface kom-face--user-defined--active-face
  '((((background light))
     (:foreground "blue4")
     )
    (((background dark))
     (:foreground "lightblue")
     )
    )
  "Face suitable for most \"clickable\" areas."
  :group 'lyskom-faces
  )


(defface kom-face--user-defined--active-strikethrough-face
  '((((background light))
     (:foreground "blue4"
      :strike-through t)
     )
    (((background dark))
     (:foreground "lightblue"
      :strike-through t)
     )
    )
  "Face suitable for some \"clickable\" areas."
  :group 'lyskom-faces
  )

(defface kom-face--user-defined--active-highlight-face
  '((((background light))
     (:foreground "blue4"
      :weight bold)
     )
    (((background dark))
     (:foreground "lightblue"
      :weight bold)
     )
    )
  "Face suitable for some \"clickable\" areas."
  :group 'lyskom-faces
  )


(defface kom-face--user-defined--url-face
  '((((background light))
     (:foreground "blueviolet")
     )
    (((background dark))
     (:foreground "moccasin")
     )
    )
  "Face suitable for URLs."
  :group 'lyskom-faces
  )


(defface kom-face--user-defined--me-face
  '((((background light))
     (:foreground "blue3"
     :background "lavender"
     :weight bold)
     )
    (((background dark))
     (:foreground "gold"
     :background "black"
     :weight bold)
     )
    )
  "Face suitable for showing your own name."
  :group 'lyskom-faces
  )


(defface kom-face--user-defined--highlight-face
  '((((background light))
     (:background "darkseagreen2")
     )
    (((background dark))
     (:background "darkblue")
     )
    )
  "Face suitable for various highlights."
  :group 'lyskom-faces
  )

(defface kom-face--user-defined--subject-face
  '((t nil))
  "Face suitable for subject lines."
  :group 'lyskom-faces
  )

(defface kom-face--user-defined--text-no-face
  '((((background light))
     (:foreground "blue4")
     )
    (((background dark))
     (:foreground "lightblue")
     )
    )
  "Face suitable for text numbers."
  :group 'lyskom-faces
  )

(defface kom-face--user-defined--friends-face
  '((((background light))
     (:foreground "blue3"
     :background "lavender")
     )
    (((background dark))
     (:foreground "red")
     )
    )
  "Face suitable for people in kom-friends."
  :group 'lyskom-faces
  )

(defface kom-face--user-defined--morons-face
  '((((background light))
     (:foreground "blue3"
     :background "yellow")
     )
    (((background dark))
     (:foreground "yellow")
     )
    )
  "Face suitable for people in kom-morons."
  :group 'lyskom-faces
  )

(defface kom-face--user-defined--presence-face
  '((((background light))
     (:foreground "dim gray"
     :slant italic)
     )
    (((background dark))
     (:foreground "gray"
     :slant italic)
     )
    )
  "Face suitable for presence messages."
  :group 'lyskom-faces
  )


(defface kom-face--user-defined--mark-face
  '((((background light))
     (:foreground "blue3"
     :background "lavender")
     )
    (((background dark))
     (:foreground "gold"
     :background "black")
     )
    )
  "Face suitable for temporary marks."
  :group 'lyskom-faces
  )


(defface kom-face--user-defined--first-line-face
  '((t (:weight bold)))
  "Face suitable for the first line header of each text."
  :group 'lyskom-faces
  )

(defface kom-face--user-defined--warning-face
  '((t (:weight bold :foreground "red")))
  "Face suitable for displaying important warnings."
  :group 'lyskom-faces
  )

(defface kom-face--user-defined--dim-face
  '((t (:foreground "gray")))
  "Face suitable for dimmed items, such as passive memberships."
  :group 'lyskom-faces
  )





(defvar lyskom-predefined-faces
  '((kom-active-face . (kom-face--default--active-face
                        kom-face--user-defined--active-face))
    (kom-url-face . (kom-face--default--url-face
                     kom-face--user-defined--url-face))
    (kom-me-face . (kom-face--default--me-face
                    kom-face--user-defined--me-face))
    (kom-highlight-face . (kom-face--default--highlight-face
                           kom-face--user-defined--highlight-face))
    (kom-text-face . (kom-face--default--text-face
                      kom-face--user-defined--text-face))
    (kom-subject-face . (kom-face--default--subject-face
                         kom-face--user-defined--subject-face))
    (kom-text-no-face . (kom-face--default--text-no-face
                         kom-face--user-defined--text-no-face))
    (kom-friends-face . (kom-face--default--friends-face
                         kom-face--user-defined--friends-face))
    (kom-morons-face . (kom-face--default--morons-face
                        kom-face--user-defined--morons-face))
    (kom-presence-face . (kom-face--default--presence-face
                          kom-face--user-defined--presence-face))
    (kom-first-line-face . (kom-face--default--first-line-face
                            kom-face--user-defined--first-line-face))
    (kom-warning-face . (kom-face--default--warning-face
                         kom-face--user-defined--warning-face))
    (kom-mark-face . (kom-face--default--mark-face
                      kom-face--user-defined--mark-face))
    (kom-dim-face . (kom-face--default--dim-face
                     kom-face--user-defined--dim-face))
    (kom-text-body-face . (kom-face--default--text-body-face
                           kom-face--user-defined--text-body-face))
    (kom-dashed-lines-face . (kom-face--default--dashed-lines-face
                              kom-face--user-defined--dashed-lines-face))
    (kom-async-text-body-face . (kom-face--default--async-text-body-face
                                 kom-face--user-defined--async-text-body-face))
    (kom-async-dashed-lines-face . (kom-face--default--async-dashed-lines-face
                                    kom-face--user-defined--async-dashed-lines-face))
    (kom-active-highlight-face . (kom-face--default--active-highlight-face
                                  kom-face--user-defined--active-highlight-face))
    (kom-active-strikethrough-face . (kom-face--default--active-strikethrough-face
                                      kom-face--user-defined--active-strikethrough-face))

    )
  "Alist of predefined faces suitable for each face variable.
Used for customization and nothing else."
)
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: deferred-insert.el
;;;;
;;;; This file includes functions for deffering insertion of
;;;; information into the LysKOM buffer.
;;;;

;;;
;;; How to defer a peice of text:
;;; =============================
;;;
;;;  1. Insert some temporary text. Use the variable
;;;     `lyskom-defer-indicator' as a placeholder.
;;; 
;;;  2. Create a defer-info-structure by calling
;;;     `lyskom-create-defer-info' with the following parameters
;;;
;;;        SERVER-CALL - the call to get the data (initate-get-*)
;;;        CALL-ARG    - the argument for the server call. This is
;;;                      limited to a single argument, which is enough
;;;                      for get-conf-stat and friends.
;;;        HANDLER     - the function to be called to insert the
;;;                      "real" data.
;;;        POS         - a marker indicating where the insertion
;;;                      should be placed.
;;;        DEL-CHARS   - an integer indicating how many characters
;;;                      from POS on should be replaced by the "real"
;;;                      text.
;;;        FORMAT      - a format string for inserting the real
;;;                      data. This should normally only contain a
;;;                      single format atom using argument 1
;;;                      (i.e. "%#1P").
;;;        DATA        - any data that you might want to use in
;;;                      HANDLER.
;;;
;;;  3. Call `lyskom-defer-insertion' with the defer-info as argument.
;;;
;;;  4. Write a handler function that takes two arguments, the server
;;;     reply and the defer-info. This function should replace the
;;;     temporary text. A convenient way to do this is to use
;;;     `lyskom-replace-deferred'.
;;;
;;;  5. Sit back and watch it work.
;;;  
;;;  Notes:
;;;  
;;;  POS must be located before the temporary text. DEL-CHARS should
;;;  usually be set to (length lyskom-defer-indicator). Don't count on
;;;  it to be 5.
;;;
;;;  Steps 1 and 2 are often implemented the other way around, or at
;;;  least the POS parameter is determined before any text is
;;;  inserted.
;;;
;;;  You should only defer text if kom-deferred-printing is non-nil.
;;;



;;; Type: defer-info

(def-komtype defer-info 
  (server-call
   call-par
   handler
   pos
   del-chars
   format
   &optional
   data
   (last-viewed :automatic lyskom-last-viewed)))


(defun lyskom-defer-insertion (defer-info)
  "Defer insertion of something.
The insertion will be at (point)."
  (set-defer-info->last-viewed defer-info lyskom-last-viewed)
  ;; (goto-char (defer-info->pos defer-info))
  (if (and (defer-info->call-par defer-info)
           (listp (defer-info->call-par defer-info)))
      (apply (intern-soft (concat "initiate-"
				(symbol-name (defer-info->server-call
					       defer-info))))
	   'deferred
	   (defer-info->handler defer-info)
           (append (defer-info->call-par defer-info) (list defer-info)))
    (funcall (intern-soft (concat "initiate-"
                                  (symbol-name (defer-info->server-call
                                                 defer-info))))
             'deferred
             (defer-info->handler defer-info)
             (defer-info->call-par defer-info)
             defer-info)))

(defun lyskom-replace-deferred (defer-info &rest replacement-data)
  "Replace some defered text."
  (save-excursion
    (when (marker-buffer (defer-info->pos defer-info))
      (set-buffer (marker-buffer (defer-info->pos defer-info))))
    (goto-char (defer-info->pos defer-info))
    (apply 'lyskom-format-insert-at-point
	   (defer-info->format defer-info)
	   replacement-data)
    (let ((inhibit-read-only t))
      (delete-char (defer-info->del-chars defer-info)))
    (set-marker (defer-info->pos defer-info) nil))
  (if lyskom-executing-command
      nil
    (let ((window (get-buffer-window lyskom-buffer)))
      (if window
	  (if (lyskom-pos-visible-in-window-p (point-max) window)
	      nil
	    ;; This means that this insertion moved point out of the
	    ;; window. The scrolling becomes tricky. One big problem is
	    ;; that we can't use lyskom-last-viewed, because it has been
	    ;; updated to the new prompt. Until that is solved we make
	    ;; sure that we never scroll.
	    ;;
	    ;; The solution is to save lyskom-last-viewed in the defer-info
	    (save-selected-window
	      (select-window window)
	      (lyskom-scroll))
	    )))))


(defun lyskom-deferred-insert-conf (conf-stat defer-info)
  "Insert the name of a conference at a previously reserved place."
  (lyskom-replace-deferred
   defer-info
   (if (null conf-stat)
       (lyskom-format 
	(or (defer-info->data defer-info)
	    (if (= (aref (defer-info->format defer-info)
			 (1- (length (defer-info->format defer-info))))
		   ?P)
		(if (= (defer-info->call-par defer-info) 0)
		    'person-is-anonymous
		  'person-does-not-exist)
	      'conference-does-not-exist))
	(defer-info->call-par defer-info))
     conf-stat)
   (text-properties-at (defer-info->pos defer-info))))


;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: utilities.el
;;;;
;;;; This file contains general lisp utility functions and
;;;; lyskom-specific utility functions (such as date formatting and
;;;; minibuffer reading)
;;;;


(defvar coding-category-list)
(defun lyskom-check-configuration ()
  ;; Excuse my paranoia. This code is hardly tested at all, so I
  ;; really want it wrapped to prevent errors from breaking the
  ;; client. Plus it lets me check how to do stuff in Gnu Emacs 21
  ;; without worrying about compatibility with other versions.
  (when kom-check-configuration-on-startup
    (condition-case nil
        (progn
          ;; Check that we have MULE
          (when (and (>= emacs-major-version 20)
                     (boundp 'enable-multibyte-characters)
                     (not enable-multibyte-characters))
            (lyskom-format-insert 'no-mule-warning
                                  `(face ,kom-warning-face)
                                  ))

          ;; Check coding system
          (when enable-multibyte-characters
            (let* ((cs (symbol-value (car coding-category-list)))
                   (aliases (or (lyskom-coding-system-get cs 'mime-charset)
                                (lyskom-coding-system-get cs ':mime-charset)
                                (lyskom-coding-system-aliases cs)
                                (lyskom-coding-system-get cs 'alias-coding-systems))))
              (when (and (not (memq 'utf-8 aliases))
                         (not (memq lyskom-server-coding-system aliases)))
                (lyskom-format-insert 'coding-system-mismatch-warning
                                      (symbol-value (car coding-category-list))
                                      lyskom-server-coding-system
                                      `(face ,kom-warning-face)
                                      )))))
      (error nil)
      )))

;;;
;;; Lisp utility functions
;;;

(defsubst listify-vector (vector)
  "Turn VECTOR into a list"
  (append vector nil))

(defun lyskom-nfirst (n list)
  "Return a list of the N first elements of LIST."
  (let ((result nil))
    (while (and (> n 0) list)
      (setq result (cons (car list) result)
            list (cdr list)
            n (1- n)))
    (nreverse result)))

(defun lyskom-rotate-list (list el)
  "Destructively rotate LIST so EL becomes the first element.
If EL occurs more than one, the first occurrence is used."
  (let ((tail (memq el list)))
    (if (or (null tail) (eq el (car list)))
        list
      (setcdr (nthcdr (- (length list) (length tail) 1) list) nil)
      (setcdr (nthcdr (1- (length tail)) tail) list)
      tail)))

(defun lyskom-preceding-cons (list el)
  "Return the cons cell of LIST preceding the first cons cell whose car is EL.
Return nil if TAIL is the same as LIST or not a member of LIST."
  (unless (or (eq (car list) el)
              (not (memq el list)))
    (nthcdr (- (length list) (length (memq el list)) 1) list)))

(defun lyskom-insert-in-list (el list before)
  "Destructively insert EL in LIST before element BEFORE.
If BEFORE is not in the list, then insert EL at the end of the list."
  (cond ((eq before (car list))
         (cons el list))
        (t (setcdr (nthcdr (- (length list)
                              (length (memq before list)) 1) list)
                   (cons el (memq before list)))
           list)))

(defun lyskom-replace-in-list (list idx newelt)
  "Destructively replace LIST element at IDX with NEWELT."
  (if (zerop idx)
      (cons newelt (cdr list))
    (setcdr (nthcdr (1- idx) list) (cons newelt (nthcdr (1+ idx) list)))
    list))

(defun lyskom-move-in-list (el list pos)
  "Destructively move EL within LIST so it appears at position POS."
  (when (memq el list)
    (setq list (delq el list))
    (cond ((eq 0 pos) (setq list (cons el list)))
          (t (setcdr (nthcdr (1- pos) list)
                     (cons el (nthcdr pos list))))))
  list)


(defun lyskom-find-dag-roots (predecessor-func 
			      node-transform 
			      start-node 
			      only-first
			      &rest other-args)
  "Locate the roots of a graph from a particular node.

PREDECESSOR-FUNC is a function that returns the predecessors of a
node in a list. It takes at least one argument, which is the node
for which it is to find predecessors.

NODE-TRANSFORM is a function that transforms return values
\(elements of the list) from func to suitable arguments for the
predecessor function. It is a function that takes a single
argument.

START-NODE is the initial argument to give to the predecessor
function. It is also transformed using NODE-TRANSFORM.

OTHER-ARGS are other arguments to FUNC, appended at each call.

If ONLY-FIRST is non-nil, then only the first root found is
returned.

This function calls PREDECESSOR-FUNC until it returns nil. The
return value is a list of all arguments for which
PREDECESSOR-FUNC returned nil.

Cycles are by remembering the visited nodes. When a backlink is
detected, it is not traversed."
  (let ((result nil)
	(visited nil)
	(candidates (apply predecessor-func 
                           (funcall node-transform start-node) 
                           other-args))
	(cur nil)
	(tem nil))
    (unless candidates
      (setq result (cons start-node result)))
    (while candidates
      (setq cur (car candidates)
	    candidates (cdr candidates))
      (unless (member cur visited)
	(setq visited (cons cur visited)
	      tem (apply predecessor-func
			 (funcall node-transform cur) other-args))
	(if tem
	    (setq candidates (nconc candidates tem))
	  (setq result (cons cur result))
	  (when only-first (setq candidates nil)))))
    (nreverse result)))


;;;
;;; +++ FIXME: If cl.el can be guaranteed, this is pointless.
;;;

(defun lyskom-butlast (x &optional n)
  "Returns a copy of LIST with the last N elements removed."
  (if (and n (<= n 0)) x
    (lyskom-nbutlast (copy-sequence x) n)))

(defun lyskom-nbutlast (x &optional n)
  "Modifies LIST to remove the last N elements."
  (let ((m (length x)))
    (or n (setq n 1))
    (and (< n m)
	 (progn
	   (if (> n 0) (setcdr (nthcdr (- (1- m) n) x) nil))
	   x))))

(defun lyskom-filter-list (test list)
  (let ((result nil))
    (lyskom-traverse el list
      (when (funcall test el)
        (setq result (cons el result))))
    (nreverse result)))

;;;============================================================
;;;
;;; Utility functions.
;;;
;;; These should be shared in LysKOM
;;;

(defun lyskom-ignore (&rest args)
  "Ignore all arguments"
  )

(defun lyskom-mapcar2 (fn seq1 seq2)
  (let (result)
    (while (and seq1 seq2)
      (setq result (cons (funcall fn (car seq1) (car seq2)) result))
      (setq seq1 (cdr seq1)
            seq2 (cdr seq2)))
    (nreverse result)))

(defun lyskom-maxint ()
  (let ((n 1) 
        (l nil)
        (i 31))
    (while (and (> n 0) (> i 0))
      (setq l (cons n l))
      (setq n (* 2 n))
      (setq i (1- i)))
    (apply '+ l)))

;; Set lyskom-maxint correctly

(setq lyskom-max-int (lyskom-maxint))

(defun lyskom-indirect-assq (key assoc-list)
  "Return the value of KEY in ASSOC-LIST.

An indirect assoc list is a kind of assoc list that can contain
references to other lists. This function is intended to work with
numbers only since symbols and lists have special meanings as keys.

Matching is done as follows. If an element in ASSOC-LIST is a cons
and its car is a:

symbol bound to a list	Recursively test the list, setting the 
			default return value to the cdr of the
			element.

symbol with value eq KEY Return cdr of the element.

function		Call the function with KEY as its only arg. 
			Return the cdr of the element if the function
			returns non-nil.

list			Return the cdr of the element if KEY is
			memq the list.

t			If they key isn't found anywhere, return the
			cdr of this element.

other atom		Return the cdr of the element if KEY is eq
			the car of the element.

If an element in ASSOC-LIST is not a cons, but is eq KEY, return
the default return value (which defaults to t, but can be changed
when recursing due to bound symbols being used as keys."
  (if (listp assoc-list)
      (catch 'lyskom-indirect-assq-return
	(let ((memo-list (cons nil nil)))
	  (lyskom-indirect-assq-2 key assoc-list memo-list t)
	  (car memo-list)))
    (eq assoc-list t)))

(defun lyskom-indirect-assq-2 (key assoc-list &optional memo-list default-return)
  "Internal function to implement lyskom-indirect-assoc."
  (unless (memq assoc-list memo-list)
    (setcdr memo-list (cons assoc-list (cdr memo-list)))
    (lyskom-traverse el assoc-list
      (cond ((atom el) (when (eq el key) (throw 'lyskom-indirect-assq-return default-return)))
	    ((eq t (car el)) (setcar memo-list (cdr el)))
	    ((functionp (car el))
	     (when (condition-case nil (funcall (car el) key) (error nil))
	       (throw 'lyskom-indirect-assq-return (cdr el))))
	    ((and (symbolp (car el)) (boundp (car el)))
	     (cond ((listp (symbol-value (car el)))
		    (lyskom-indirect-assq-2 key (symbol-value (car el)) memo-list (cdr el)))
		   ((eq (symbol-value (car el)) key)
		    (throw 'lyskom-indirect-assq-return (cdr el)))))
	    ((listp (car el))
	     (lyskom-indirect-assq-2 key (car el) memo-list (cdr el)))
	    ((atom (car el)) (when (eq (car el) key) (throw 'lyskom-indirect-assq-return (cdr el))))))))


(defun lyskom-try-require (feature &optional message &rest args)
  "Load the feature FEATURE using require. 
If optional MESSAGE is non-nil, use it as a LysKOM format string 
taking one string argument to print an error message. Remaining
arguments are used as arguments for the format string.

Returns t if the feature is loaded or can be loaded, and nil otherwise."
  (or (featurep 'feature)
      (condition-case nil
          (progn (require feature)
                 t)
        (error 
         (when message
           (apply 'lyskom-format-insert-before-prompt message (symbol-name feature) args))
         nil))))


(defvar lyskom-apo-timeout 0
  "Current millisecond timeout value for accept-process-output")

(defvar lyskom-apo-timeout-index 0
  "Index in lyskom-apo-timeout-vector-max where last timeout is")

(defvar lyskom-apo-timeout-vector
  [0 1000 1000 2000 3000 5000 8000 13000 21000 
     34000 55000 89000 144000 233000 377000 610000]
  "Vector of timeout values (usecs) for accept-process-output")

;;; Emacs 22.1 introduced a non-backwards-compatible change to
;;; accept-process-output. The most reliable way to detect which
;;; version is in play, is to look at the documentation.

(defvar lyskom-recomputed-apo-timeout nil)
(when (and (not lyskom-recomputed-apo-timeout)
	   (string-match "milliseconds" (documentation 'accept-process-output)))
  (setq lyskom-apo-timeout-vector 
	(apply 'vector 
	       (mapcar (lambda (x) (/ x 1000)) lyskom-apo-timeout-vector))
	lyskom-recomputed-apo-timeout t))

(defconst lyskom-apo-timeout-vector-max (1- (length lyskom-apo-timeout-vector))
  "Maximum index in lyskom-apo-timeout-vector")

(defun lyskom-next-apo-timeout ()
  (if (< lyskom-apo-timeout-index lyskom-apo-timeout-vector-max)
      (setq lyskom-apo-timeout
            (aref lyskom-apo-timeout-vector
                  (setq lyskom-apo-timeout-index
                        (1+ lyskom-apo-timeout-index))))))

(defun lyskom-reset-apo-timeout ()
  (setq lyskom-apo-timeout-index -1)
  (setq lyskom-apo-timeout 0))

(defun lyskom-accept-process-output ()
  "Call accept-process-output with the correct timeout values."
  (lyskom-next-apo-timeout)
  (accept-process-output nil 0 lyskom-apo-timeout))

(defun lyskom-set-alist (alist item value)
  "Modifies (non-destructively) an alist ALIST to set item
ITEM to the value VALUE."
  (let ((pair (assq item alist)))
     (if pair
         (progn (setcdr pair value) alist)
       (cons (cons item value) alist))))


;;;
;;; WARNING!
;;;
;;; The following variable is *important* if you fuck it up in any
;;; way, the functions used to read conference names won't work. So if
;;; you change it, try to work one character at a time, and when
;;; you're done, run through the mappings of all 256 characters to
;;; make sure they look OK.
;;;
;;; Make sure your MULE Emacs doesnt fuck it up for you. It did for me.
;;;

(defvar lyskom-default-collate-table
  "\000\001\002\003\004\005\006\007\010 \012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]~\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237 !¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿AAAA[]ACEEEEIIIIÐNOOOO\\×OUUUYYÞßAAAA[]ACEEEEIIIIðNOOOO\\÷OUUUYYþÿ"
  "String mapping characters to their collate class.

Lowercase, uppercase and other equivalents are mapped to the same class.
The class number defines a proper sorting order.")


(defsubst lyskom-maybe-recode-string (s &optional coding force)
  "Change the encoding of S for when multibyte characters are not supported.
Optional second argument CODING is the coding system to use. If optional
third argument FORCE is non-nil, always encode multibyte strings, otherwise
only encode when multibyte strings are not supported."
  (if (and (lyskom-multibyte-string-p s)
           (or force (not enable-multibyte-characters)))
      (lyskom-encode-coding-string s (or coding
                                         (and lyskom-language
                                              (lyskom-language-coding lyskom-language))
                                         'raw-text))
    s))

(defun lyskom-recode-string-for-title (s coding)
  "Encode S with CODING for use in frame titles.
Attempts to encode the string only in Emacs versions that do not require
MULE coding for frame titles"
  (cond
   ((memq window-system '(win32 mswindows w32))
    (lyskom-maybe-recode-string s coding t))
   ((> emacs-major-version 20) s)
   (t (lyskom-maybe-recode-string s coding t))))

(defun lyskom-maybe-frob-completion-table (table &optional copy)
  "Recode the cars of alist TABLE to default coding system unless multibyte
characters are enabled. This function is destructive unless optional copy
is non-nil."
  (cond (enable-multibyte-characters table)
	(copy (mapcar (lambda (el)
			(cons (lyskom-maybe-recode-string (car el) nil t)
			      (cdr el)))
                      table))
	(t (lyskom-traverse el table
		(setcar el (lyskom-maybe-recode-string (car el) nil t)))
	   table)))


(defsubst lyskom-unicase-char (c)
  "Smash case and diacritical marks on c." 
  (if (< (lyskom-char-to-int c) (length lyskom-collate-table))
      (aref lyskom-collate-table (lyskom-char-to-int c))
    (setq c (lyskom-encode-coding-char c lyskom-server-coding-system))
    (if (and c (< (lyskom-char-to-int c) (length lyskom-collate-table)))
	(aref lyskom-collate-table (lyskom-char-to-int c))
      c)))

(defun lyskom-unicase (s)
  "Smash case and diacritical marks of all chars in s." 
  (lyskom-save-excursion
   (and lyskom-buffer (set-buffer lyskom-buffer))
   (let* ((s2 (string-to-vector 
               (lyskom-encode-coding-string s lyskom-server-coding-system)))
          (l (length s2)))
     (while (> l 0)
       (setq l (1- l))
       (aset s2 l (lyskom-unicase-char (aref s2 l))))
     (concat s2))))

(defun lyskom-looking-at (s)
  "Version of looking-at that will work in Gnu Emacs 20.3"
  (save-excursion
    (let ((start (point)))
      (and (re-search-forward s nil t)
	   (eq (match-beginning 0) start)))))

;; Don't slow things down with our own looking-at unless we have to

(eval-and-compile
 (condition-case nil
     (if (and (eq emacs-major-version 20)
	      (eq emacs-minor-version 3))
	 nil
       (fset 'lyskom-looking-at (symbol-function 'looking-at)))
   (error nil)))


(defun lyskom-compute-char-classes (map)
  "Find out which characters that are equivalent according to MAP.

MAP should be a collate table.

The return value is an assoc list, which characters as keys, and a
list of single-character strings as values.  Only characters that are
equivalent to at least one more character is returned in the assoc
list.

Example: if this function returns

	((97 \"A\" \"a\")
         (65 \"A\" \"a\")
	 (48 \"O\" \"o\" \"0\")
	 (111 \"O\" \"o\" \"0\")
	 (79 \"O\" \"o\" \"0\"))

it means that a and A are equivalent, and o, O and 0 are equivalent.
All other characters are unique."

  (lyskom-save-excursion
    (and lyskom-buffer (set-buffer lyskom-buffer))
    (let ((ix (length map))
	  (cls-to-strings nil)) ;assoc-list from equivalence class to
				;list of chars
      (while (> ix 0)
	(setq ix (1- ix))
	(let* ((cls (aref map ix))
	       (str (lyskom-decode-coding-string (concat (vector ix))
                                                 lyskom-server-coding-system))
	       (elem (assoc cls cls-to-strings)))
	  (if elem
	      (rplacd elem (cons str (cdr elem)))
	    (setq cls-to-strings (cons (list cls str) cls-to-strings)))))
      (let ((res nil))
	(while cls-to-strings
	  (let ((lst (cdr (car cls-to-strings))))
	    (if (> (length lst) 1)
		(while lst
		  (setq res (cons (cons (string-to-char (car lst))
					(cdr (car cls-to-strings)))
				  res))
		  (setq lst (cdr lst)))))
	  (setq cls-to-strings (cdr cls-to-strings)))
	res))))
    
;; Stolen from thingatpt.el
;; FIXME: We may not really need this function. Check the callers.

(defun lyskom-thing-at-point-looking-at (regexp)
  "Return non-nil if point is in or just after a match for REGEXP.
Set the match data from the earliest such match ending at or after
point."
  (save-excursion
    (let ((old-point (point)) match)
      (and (looking-at regexp)
	   (>= (match-end 0) old-point)
	   (setq match (point)))
      ;; Search back repeatedly from end of next match.
      ;; This may fail if next match ends before this match does.
      (re-search-forward regexp nil 'limit)
      (while (and (re-search-backward regexp nil t)
		  (or (> (match-beginning 0) old-point)
		      (and (looking-at regexp) ; Extend match-end past search start
			   (>= (match-end 0) old-point)
			   (setq match (point))))))
      (if (not match) nil
	(goto-char match)
	;; Back up a char at a time in case search skipped
	;; intermediate match straddling search start pos.
	(while (and (not (bobp))
		    (progn (backward-char 1) (looking-at regexp))
		    (>= (match-end 0) old-point)
		    (setq match (point))))
	(goto-char match)
	(looking-at regexp)))))


;; Stolen from Gnu Emacs

(defun lyskom-truncate-string-to-width (str end-column &optional trailer)
  "Truncate string STR to end at column END-COLUMN.

If optional TRAILER is non-nil, it is a string to append if STR is
truncated. The total width will still be at most END-COLUMN."
  (when (and trailer (>= (lyskom-string-width trailer) end-column))
    (setq trailer nil))

  (cond ((< (lyskom-string-width str) end-column) str)
        (t (when trailer 
             (setq end-column (- end-column (lyskom-string-width trailer))))
           (let ((idx 0)
                 (column 0)
                 (len (length str))
                 ch last-column last-idx)

             (condition-case nil
                 (while (< column end-column)
                   (setq last-column column
                         last-idx idx
                         ch (aref str idx)
                         column (+ column (lyskom-char-width ch))
                         idx (1+ idx)))
               (args-out-of-range (setq idx len)))
             (if (> column end-column)
                 (setq column last-column idx last-idx))
             (setq str (substring str 0 idx))
             (when trailer
               (setq str (concat str trailer)))
             str))))

(eval-and-compile
  (lyskom-xemacs-or-gnu
   (fset 'lyskom-string= (symbol-function 'string=))
   (if (< emacs-major-version 20)
       (fset 'lyskom-string= (symbol-function 'string=))
     (defun lyskom-string= (s1 s2)
       (string= (and s1 (if (lyskom-multibyte-string-p s1)
		    s1
		  (lyskom-decode-coding-string s1 (lyskom-language-coding
					    lyskom-language))))
		(and s2 (if (lyskom-multibyte-string-p s2)
		    s2
		  (lyskom-decode-coding-string s2 (lyskom-language-coding
					    lyskom-language)))))))))

(defun lyskom-string-assoc (key list)
  "Return non-nil if KEY is the same string as the car of an element of LIST.
The value is actually the element of LIST whose car equals KEY."
  (let ((s (and key (downcase key)))
        (result nil))
    (while list
      (when (lyskom-string= s (downcase (car (car list))))
        (setq result (car list))
        (setq list nil))
      (setq list (cdr list)))
    result))

(defun lyskom-string-member (key list)
  "Return non-nil if KEY is the same string as the car of an element of LIST.
The value is actually the element of LIST whose car equals KEY."
  (let ((s (and key (downcase key)))
        (result nil))
    (while list
      (when (lyskom-string= s (downcase (car list)))
        (setq result (car list))
        (setq list nil))
      (setq list (cdr list)))
    result))

(defun lyskom-string-rassoc (key list)
  "Return non-nil if KEY is the same string as the cdr of an element of LIST.
The value is actually the element of LIST whose car equals KEY."
  (let ((s (and key (downcase key)))
        (result nil))
    (while list
      (when (lyskom-string= s (downcase (cdr (car list))))
        (setq result (car list))
        (setq list nil))
      (setq list (cdr list)))
    result))

(defun lyskom-set-default (sym val)
  "Set the value of SYM in the LysKOM buffer to VAL."
  (save-current-buffer
    (set-buffer (or (and (boundp 'lyskom-buffer)
                         (bufferp lyskom-buffer)
                         (buffer-live-p lyskom-buffer)
                         lyskom-buffer)
                    (current-buffer)))
    (set sym val)))

(defun lyskom-default-value (sym)
  "Get the value of SYM in the LysKOM buffer"
  (save-current-buffer
    (set-buffer (or (and (boundp 'lyskom-buffer)
                         (bufferp lyskom-buffer)
                         (buffer-live-p lyskom-buffer)
                         lyskom-buffer)
                    (current-buffer)))
    (symbol-value sym)))

(defun lyskom-default-value-safe (sym)
  "Get the value of SYM in the LysKOM buffer"
  (save-current-buffer
    (set-buffer (or (and (boundp 'lyskom-buffer)
                         (bufferp lyskom-buffer)
                         (buffer-live-p lyskom-buffer)
                         lyskom-buffer)
                    (current-buffer)))
    (and (boundp sym) (symbol-value sym))))


;;; ============================================================
;;; Prefix arguments

(defun lyskom-get-ancestors-of-text (text-no level)
  "Returns a list of all ancestors of TEXT-NO that are LEVEL
comment/foot-note levels up the comment tree. LEVEL is a
non-negative integer and 0 means the given text-no."
  (if (< level 1)
      (list text-no)
    (let* ((text-stat (blocking-do 'get-text-stat text-no))
	   (ancestors (and text-stat
			   (lyskom-text-stat-commented-texts text-stat)))
	   (level (1- level))
	   (result '()))
	(while ancestors
	  (setq result
		(lyskom-union
		 (lyskom-get-ancestors-of-text (car ancestors) level)
		 result
                 ))
	  (setq ancestors (cdr ancestors)))
	result)))

(defun lyskom-maybe-get-commented-text ()
  (let* ((text-no (lyskom-text-at-point))
         (text-stat (and text-no (blocking-do 'get-text-stat text-no))))
    (when text-no
      (if (lyskom-misc-infos-from-list 
           'COMM-IN (text-stat->misc-info-list text-stat))
          text-no
        (lyskom-get-text-at-point-ancestor 1)))))

(defun lyskom-maybe-get-footnoted-text ()
  (let* ((text-no (lyskom-text-at-point))
         (text-stat (and text-no (blocking-do 'get-text-stat text-no))))
    (when text-no
      (if (lyskom-misc-infos-from-list 
           'FOOTN-IN (text-stat->misc-info-list text-stat))
          text-no
        (lyskom-get-text-at-point-ancestor 1)))))

(defun lyskom-get-last-read-text ()
  (lyskom-default-value 'lyskom-current-text))

(defun lyskom-get-previous-text ()
  (lyskom-default-value 'lyskom-previous-text))

(defun lyskom-get-text-at-point ()
  (lyskom-text-at-point))

(defun lyskom-get-text-at-point-ancestor (arg)
  (let* ((text (lyskom-text-at-point))
	 (cnos (and text (lyskom-get-ancestors-of-text text arg)))
	 (txts (length cnos)))
    (cond
     ((not text)
      (lyskom-error (lyskom-get-string 'no-text-at-point)))
     ((eq txts 0)
      (lyskom-error (lyskom-get-string 'no-comment-to)))
     ((eq txts 1)
      (car cnos))
     (t
      (lyskom-read-number (lyskom-get-string 'what-ancestor) 
                          (car cnos)
                          nil
                          nil
                          cnos)))))

(defun lyskom-get-last-text-written-by-me ()
  (lyskom-default-value 'lyskom-last-written))

(defun lyskom-get-last-written-or-read-by-me ()
  (lyskom-default-value 'lyskom-last-seen-written))

(defun lyskom-get-text-above-point (arg)
  (save-excursion
    (let ((former-point (point)))
      (kom-backward-text (+ 1 arg))
      (if (looking-at "\\([0-9]+\\)\\s-")
	  (lyskom-string-to-number (match-string 1))
	(progn ;; we probably ended up above the first message in the buffer
	  (kom-forward-text)
	  (if (and (< (point) former-point)
		   (looking-at "\\([0-9]+\\)\\s-"))
	      (lyskom-string-to-number (match-string 1))
	    (lyskom-error (lyskom-get-string 'bad-text-no-prefix) arg)))))))

(defun lyskom-get-text-below-point (arg)
  (if (and (numberp arg) (> arg 20))
      arg
    (save-excursion
      (let ((former-point (point)))
        (kom-forward-text arg)
        (if (looking-at "\\([0-9]+\\)\\s-")
            (lyskom-string-to-number (match-string 1))
          (progn ;; we probably ended up below the final message in the buffer
            (kom-backward-text)
            (if (and (> (point) former-point)
                     (looking-at "\\([0-9]+\\)\\s-"))
                (lyskom-string-to-number (match-string 1))
              (lyskom-error (lyskom-get-string 'bad-text-no-prefix) arg))))))))

(defun lyskom-read-text-no-prompt-p (command)
  "Return non-nil if the COMMAND should prompt for a text number."
  (let ((check (assq command kom-text-no-prompts)))
    (if check (cdr check) (memq command lyskom-text-no-prompts-defaults))))

;;; ------------------------------------------------------------
;;; Constraints

(defun lyskom-text-written-by-me-p (text-no)
  (let ((text-stat (blocking-do 'get-text-stat text-no)))
    (and text-stat
         (lyskom-is-supervisor (text-stat->author text-stat)
                               lyskom-pers-no))))



;;; ------------------------------------------------------------
;;; Functions used in predicates and strategies

(defun lyskom-eq-dash (e) (eq e '-))

(defun lyskom-tnpa-prompt (text-nos)
  (lyskom-tnpa-add-property text-nos 'prompt))

(defun lyskom-tnpa-valid (text-nos)
  (lyskom-tnpa-add-property text-nos 'valid))

(defun lyskom-tnpa-add-property (text-nos what)
  (cond ((numberp text-nos) (vector text-nos (list what)))
        ((vectorp text-nos)
         (aset text-nos 1 (cons what (aref text-nos 1)))
         text-nos)
        ((and text-nos (listp text-nos))
         (mapcar (lambda (el)
                   (cond ((vectorp el) (aset el 1 (cons what (aref el 1))))
                         (t (vector el (list what)))))
                 text-nos))))

(defsubst lyskom-tnpa-text-no (el)
  (if (vectorp el) (aref el 0) el))

(defsubst lyskom-tnpa-text-property (el prop)
  (and (vectorp el) (memq prop (aref el 1))))



;;; ------------------------------------------------------------
;;; The main function

(defun lyskom-tnpa-apply-strategy (strategies filter-spec constraint-spec)
  (let ((filter-locl (car (cdr (memq :filter strategies))))
        (constraint-locl (car (cdr (memq :constraint strategies)))))
    (lyskom-traverse strategy strategies
      (if (memq strategy '(:filter :constraint :save))
          (lyskom-traverse-break)
        (let ((tmp (cond ((functionp strategy) (funcall strategy))
                         ((and (symbolp strategy) (boundp strategy)) (symbol-value strategy))
                         (t nil))))
          (unless (listp tmp) (setq tmp (list tmp)))
          (when filter-locl (setq tmp (mapcar filter-locl tmp)))
          (when filter-spec (setq tmp (mapcar filter-spec tmp)))
          (when (or constraint-spec constraint-locl)
            (setq tmp 
                  (delq nil (mapcar (lambda (el)
                                      (when (or (lyskom-tnpa-text-property el 'valid)
                                                (and (or (null constraint-locl)
                                                         (funcall constraint-locl (lyskom-tnpa-text-no el)))
                                                     (or (null constraint-spec)
                                                         (funcall constraint-spec (lyskom-tnpa-text-no el)))))
                                        el))
                                    tmp))))
          (when tmp (lyskom-traverse-break (car tmp))))))))



(defun lyskom-read-text-no-prefix-arg (prompt &optional command)
  (when (listp prompt) (setq prompt (car prompt)))
  (let* ((command (or command lyskom-current-command this-command))
         (command-spec (cdr (assq command kom-pick-text-no-strategy-alist)))
         (refer-spec (car (cdr (memq :refer command-spec))))
         (default-spec (cdr (assq t kom-pick-text-no-strategy-alist)))
         (filter-spec (car (cdr (or (memq :filter command-spec)
                                    (memq :filter default-spec)))))
         (constraint-spec (car (cdr (or (memq :constraint command-spec)
                                        (memq :constraint default-spec)))))
         (text nil))

    (if refer-spec
        (lyskom-read-text-no-prefix-arg prompt refer-spec)

      ;; Traverse all strategy lists, in the appropriate order.
      ;; Identical predicates are only called once. We traverse
      ;; command-spec and default-spec until we find an applicable
      ;; element (one that matches the prefix argument). When we find
      ;; that element, we call its strategies until we find one that
      ;; generates a result that passes the filter and the constraint.
      ;;
      ;; If we never find an element, we use the t predicates in the
      ;; specification. These are fallbacks and only applied if no
      ;; strategy provides a text number.
      ;;
      ;; The exit from this block should give us a text (perhaps with
      ;; properties) that we can either use or prompt for.
      ;;
      ;; Using catch and throw significantly simplifies this code.

      (setq text
            (catch 'lyskom-tnpa-text
              (let ((handled nil))
                (lyskom-traverse spec-list (list command-spec default-spec)
                  (lyskom-traverse el spec-list
                    (if (not (listp el))
                        (lyskom-traverse-break) ; We hit :filter or something like that
                      (cond
                       ((memq (car el) handled)) ; Already handled once
                       ((eq (car el) t)) ; Defaults are handled after this loop
                       ((or (eq (car el) current-prefix-arg)
                            (condition-case nil (funcall (car el) current-prefix-arg) (error nil)))

                        ;; The predicate matches the perfix argument, so now we can
                        ;; evaluate the strategies.

                        (let ((tmp (lyskom-tnpa-apply-strategy (cdr el) filter-spec constraint-spec)))
                          (when tmp (throw 'lyskom-tnpa-text tmp)))))
                      (setq handled (cons (car el) handled))))))

              ;; The strategies failed to yield anything useful Apply
              ;; the default strategies, both from the command and from
              ;; the default strategy list..

              (throw 'lyskom-tnpa-text
                     (or (lyskom-tnpa-apply-strategy (cdr (assq t command-spec)) filter-spec constraint-spec)
                         (lyskom-tnpa-apply-strategy (cdr (assq t default-spec)) filter-spec constraint-spec)))
              ))

      ;; We now have a text that fits the constraint. Really, the only
      ;; thing to do at this point is prompt for the text, if that is
      ;; requested.

      (if (or (not (lyskom-tnpa-text-no text))
              (lyskom-tnpa-text-property text 'prompt)
              (lyskom-read-text-no-prompt-p lyskom-current-command))
          (lyskom-read-number prompt (or (lyskom-tnpa-text-no text)
                                         (lyskom-default-value 'lyskom-current-text)))
        (lyskom-tnpa-text-no text)))))

;;; ============================================================
;;; Set functions

(defun lyskom-subset-p (a b)
  "Return non-nil if A is a subset of B"
  (let ((result t))
    (while a
      (if (memq (car a) b)
          (setq a (cdr a))
        (setq result nil a nil)))
    result))

(defun lyskom-intersection (a b)
  "Returns as a list the intersection of list A and list B.
The order of the list a is kept."
  (if (or a b)
      (let ((list nil))
        (while a
          (if (memq (car a) b)
              (setq list (cons (car a) list)))
          (setq a (cdr a)))
        (nreverse list))))

(defun lyskom-union (a b)
  "Returns a list containing the union of list A and list B.
Specifically, concatenate all elements of B that are not in A
to the end of A. This operation is destructive and modifies the
list pointed to by A."
  (let ((result nil))
    (lyskom-traverse x b
      (unless (memq x a) (setq result (cons x result))))
    (nconc a (nreverse result))))

(defun lyskom-delete-duplicates (list &optional key)
  "Removes all but one instance of each element in LIST.
If optional argument KEY is non-nil, apply KEY to each element before
comparison. Comparison is done with eq."
  (let ((clist (mapcar (lambda (el)
                         (if key
                             (cons (funcall key el) el)
                           (cons el el)))
                       list))
        (result nil))
    (lyskom-traverse el clist
      (unless (assq (car el) result)
        (setq result (cons el result))))
    (nreverse (mapcar 'cdr result))))

(defun lyskom-plusp (int)
  "Returns t for integers greater than 0, nil otherwise."
  (and (integerp int) (> int 0)))

(defun lyskom-minusp (int)
  "Returns t for integers smaller than 0, nil otherwise."
  (and (integerp int) (< int 0)))



;;; ======================================================================
;;; LysKOM Hooks
;;;

(defun lyskom-run-hook-with-args (hook &rest args)
  "Run HOOK with the specified arguments ARGS in the LysKOM buffer.
See run-hook-with-args for detailed information."
  (save-current-buffer (set-buffer (or (and (boundp 'lyskom-buffer)
                                              lyskom-buffer)
                                         (current-buffer)))
                  (apply 'run-hook-with-args hook args)))



(defun lyskom-add-hook (hook function &optional append)
  "Add to the value of HOOK the function FUNCTION in the LysKOM buffer.
If optional APPEND is non-nil, add at the end of HOOK."
  (save-current-buffer (set-buffer (or (and (boundp 'lyskom-buffer)
                                       lyskom-buffer)
                                  (current-buffer)))
                  (add-hook hook function append t)))

(defun lyskom-remove-hook (hook function)
  "From the value of HOOK remove the function FUNCTION in the LysKOM buffer."
  (save-current-buffer (set-buffer (or (and (boundp 'lyskom-buffer)
                                            lyskom-buffer)
                                       (current-buffer)))
                       (remove-hook hook function t)))




;;; ======================================================================
;;; Printing
;;;
;;; XEmacs princ does not insert text properties. This function is based
;;; on the C code for princ. 
;;;

(defun lyskom-princ (object &optional stream)
  "Output the printed representation of OBJECT, any Lisp OBJECT.
No quoting characters are used; no delimiters are printed around
the contents of strings. Text properties are retained.

Output stream is STREAM, or value of standard-output, and must be a
buffer or a marker. Function or minibuffer streams are not supported
for strings."
  (if (not (stringp object))
      (princ object stream)
    (let ((old-point nil)
          (start-point nil)
          (old-buffer (current-buffer)))
      (unwind-protect
          (progn
            (cond ((bufferp stream) (set-buffer stream))
                  ((markerp stream) 
                   (setq old-point (point))
                   (set-buffer (marker-buffer stream))
                   (goto-char stream)
                   (setq start-point (point)))
                  ((null stream)
                   (cond ((bufferp standard-output) (set-buffer standard-output))
                         ((markerp standard-output) 
                          (setq old-point (point))
                          (set-buffer (marker-buffer standard-output))
                          (goto-char standard-output)
                          (setq start-point (point))))))

            (insert object))
        (cond ((markerp stream) 
               (set-marker stream (point))
               (if (>= old-point start-point)
                   (goto-char (+ old-point (- (point) start-point)))
                 (goto-char old-point))))
        (set-buffer old-buffer)))))


;;; ======================================================================
;;; Faces
;;;

(make-face 'lyskom-weak-highlight-face)
(make-face 'lyskom-strong-highlight-face)

(defun lyskom-copy-face (old new)
  (lyskom-xemacs-or-gnu (copy-face old new nil nil nil 'remove-all)
                        (copy-face old new)))



;;; ============================================================
;;; Date and time utilities

(defun lyskom-current-client-time ()
  "Return time representing current client time."
  (let ((now (decode-time)))
    (lyskom-create-time (elt now 0)     ;sec
                        (elt now 1)     ;min
                        (elt now 2)     ;hour
                        (elt now 3)     ;mday
                        (elt now 4)     ;mon
                        (elt now 5)     ;year
                        (elt now 6)     ;wday
                        0               ;yday
                        (if (elt now 7) ;isdst
                            1
                          0)
                        )))

(defun lyskom-current-server-time ()
  "Return time representing current server time."
  (blocking-do 'get-time))

(defun lyskom-format-time (format &optional time)
  "Return TIME as a string formatted as FORMAT.

FORMAT may be a string or a symbol.  If it is a symbol, it is
interpreted as follows:

    'date-and-time  Include date and time. [1][2]
    'date           Include just date. [1]
    'time           Include just time. [2]

    [1] If kom-print-seconds-in-time-strings is nil, only hours and
    minutes will be included in the time; if it is non-nil, seconds
    will also be included.
    [2] If kom-print-relative-dates is non-nil and the date is today's
    or yesterday's, the string \"today\" or \"yesterday\" (respectively)
    is used instead of the standard date format.

If FORMAT is a symbol but not one of the symbols listed above, the
format string will be looked up with lyskom-get-string.  The
timeformat-* strings are tailored to be used as formats for this
function.

The arguments to the format string are (in order): year, month number
\(starting with one for January), day-of-month number, hour, minute,
second, full name of the day of the week, abbreviated name of the day
of the week.

TIME defaults to the current client time."
  (let* ((time (or time (lyskom-current-client-time)))
         (fmt (cond
               ((stringp format) format)
               ((memq format '(date-and-time date time))
                (lyskom-format (cond ((eq format 'date-and-time)
                                      'format-time-date-and-time)
                                     ((eq format 'date)
                                      'format-time-just-date)
                                     ((eq format 'time)
                                      'format-time-just-time))
                               (lyskom-get-string
                                (or (and kom-print-relative-dates
                                         (lyskom-calculate-day-diff time))
                                    'timeformat-yyyy-mm-dd))
                               (lyskom-get-string
                                (if kom-print-seconds-in-time-strings
                                    'timeformat-hh-mm-ss
                                  'timeformat-hh-mm))))
               ((symbolp format) (lyskom-get-string format))
               (t (error "Invalid argument")))))
    (lyskom-format fmt
                   (time->year time)
                   (time->mon  time)
                   (time->mday time)
                   (time->hour time)
                   (time->min  time)
                   (time->sec  time)
                   (elt (lyskom-get-string 'weekdays)
                        (time->wday time))
                   (elt (lyskom-get-string 'weekdays-short)
                        (time->wday time))
                   (lyskom-format-timezone (time->zone time)))))

(defun lyskom-format-timezone (zone)
  "Return a string representation of the timezone ZONE.

Returns nil if ZONE is nil."
  (cond ((null zone) "")
        ((eq zone t) (lyskom-format-timezone (current-time-zone)))
        ((stringp zone) zone)
        ((listp zone) (car (cdr zone)))
        ((integerp zone)
         (format "%s%02d%02d"
                 (if (< zone 0) "-" "+")
                 (/ (abs zone) 3600)
                 (/ (% (abs zone) 3600) 60)))
        (t nil)))


;;; ============================================================
;;; Keymap utilities

(defun lyskom-lookup-key (keymap event &optional accept-default)
  (if (not (arrayp event))
      (setq event (vector event)))
  (if (null keymap)
      (and accept-default 
           (lookup-key global-map event))
    (or (lookup-key keymap event)
        (lyskom-lookup-key (keymap-parent keymap) event accept-default))))

(defun lyskom-keymap-body (keymap)
  (setq keymap (cdr keymap))
  (cond ((arrayp (car keymap)) (car keymap))
        (t keymap)))

(defun lyskom-keymap-realbinding (binding)
  (while (stringp (car-safe binding))
    (setq binding (cdr binding)))
  binding)

(defun lyskom-overlay-keymap (basemap overlay keymap &optional prefix)
  (let ((keys (make-vector (1+ (length prefix)) nil))
        (index (length prefix))
        (body nil)
        (r 0))

    (while (< r (length prefix))
      (aset keys r (aref prefix r))
      (setq r (1+ r)))

    (cond ((not (keymapp keymap)))
          ((not (keymapp overlay)))
          ((not (keymapp basemap)))

          ((setq body (lyskom-keymap-body overlay))
           (mapcar
            (function
             (lambda (element)
               (cond ((arrayp element)
                      (let ((len (length element)))
                        (setq r 0)
                        (while (< r len)
                          (aset keys index r)
                          (lyskom-overlay-keys keys (aref element r)
                                               basemap overlay keymap)
                          (setq r (1+ r)))))

                     ((consp element)
                      (when (not (eq t (car element)))
                        (aset keys index (car element))
                        (lyskom-overlay-keys keys
                                             (lyskom-keymap-realbinding
                                              (cdr element))
                                             basemap overlay keymap)))

                     (t nil))))
            body)))))


(defun lyskom-overlay-keys (keys binding basemap overlay keymap)
  (let ((base-binding (lyskom-lookup-key basemap keys nil)))

   ;; If the binding is a keymap or prefix and
   ;; the binding in the base is a keymap or prefix 
   ;; then recurse

   (cond ((and (keymapp binding)
               (keymapp base-binding))
          (lyskom-overlay-keymap basemap binding keymap keys))

   ;; If the binding is a keymap or prefix and
   ;; we are bound in the base
   ;; then don't recurse

         ((and (keymapp binding)
               base-binding) nil)

   ;; If we are not bound in the base
   ;; copy the binding

         ((and binding
               (null base-binding)) (define-key keymap keys binding)))))

(defun lyskom-traverse-keymap (fn keymap)
  "Like lyskom-map-keymap, but traverses parent links too."
  (let ((parent (keymap-parent keymap)))
    (lyskom-map-keymap fn keymap)
    (when (keymapp parent) (lyskom-traverse-keymap fn parent))))


;;;
;;; Stuff
;;;

(defun lyskom-return-membership-type (mt)
  "Return a text description of membership type mt"
  (let ((tmp
         (mapconcat 
          'identity
          (delete nil
                  (list (if (membership-type->invitation mt) (lyskom-get-string 'invitation-mt-type) nil)
                        (if (membership-type->passive mt) (lyskom-get-string 'passive-mt-type) nil)
                        (if (membership-type->secret mt) (lyskom-get-string 'secret-mt-type) nil)
                        (if (eq (membership-type->message-flag mt)
                                (membership-type->passive mt))
                            (lyskom-get-string (if (membership-type->message-flag mt)
                                                   'message-flag-on-mt-type
                                                 'message-flag-off-mt-type)))))
          ", ")))
    (if (string= tmp "") 
        tmp
      (concat "[" tmp "]"))))

(defun lyskom-find-unread (conf-no)
  "Return the number of unread texts in CONF-NO.
If this function is unable to calculate the number of unread texts it will
return nil."
  (lyskom-with-lyskom-buffer
   (let ((rlist (read-list->all-entries lyskom-to-do-list))
         (found nil))
     (while (and (not found) rlist)
       (when (eq conf-no (conf-stat->conf-no
                          (read-info->conf-stat (car rlist))))
         (setq found (text-list->length (read-info->text-list (car rlist)))))
       (setq rlist (cdr rlist)))
     found)))

(defun lyskom-prev-area (num prop &optional goto-point-min)
  (while (> num 0)
    (let ((where (previous-single-property-change (point) prop)))
      (cond (where
             (if (not (get-text-property where prop))
                 (setq where (previous-single-property-change 
                              where prop)))
             (if where
                 (goto-char where)
               (goto-char (point-min))
               (setq num 1)))
            (goto-point-min (goto-char (point-min))
                            (setq num 1))))
    (setq num (1- num))))

(defun lyskom-next-area (num prop &optional goto-point-max)
  "Move the cursor to the next prompt in the LysKOM buffer"
  (interactive "p")
  (while (> num 0)
    (let ((where (next-single-property-change (point) prop)))
      (cond (where
             (if (not (get-text-property where prop))
                 (setq where (next-single-property-change where prop)))
             (if where
                 (goto-char where)
               (goto-char (point-max))
               (setq num 1)))
            (goto-point-max (goto-char (point-max))
                            (setq num 1))))
      (setq num (1- num))))

;;; ============================================================
;;; Database stuff

(defun lyskom-get-send-comments-to (conf-no &optional want-type)
  "Get the send-comments-to value for conference CONF-NO.
Returns nil if there is no send-comments-to set.

Ïf WANT-TYPE is set, return as a conf (RECPT . TYPE), wher RECPT
is the conference to use and TYPE is the type of recipient (numeric)."
  (let* ((conf-stat (if (numberp conf-no)
                        (blocking-do 'get-conf-stat conf-no)
                      conf-no))
         (send-comments-to
          (and conf-stat 
               (car (lyskom-get-aux-item (conf-stat->aux-items conf-stat) 33))))
         (result (when send-comments-to
                   (cond ((string-match "^\\([0-9]+\\)\\s-+\\([0-9]+\\)" (aux-item->data send-comments-to))
                          (cons (string-to-number
                                 (match-string 1 (aux-item->data send-comments-to)))
                                (string-to-number
                                 (match-string 2 (aux-item->data send-comments-to)))))
                         ((string-match "^\\([0-9]+\\)"
                                        (aux-item->data send-comments-to))
                          (cons (string-to-number
                                 (match-string 1 (aux-item->data send-comments-to)))
                                0))
                         (t nil)))))
    (if want-type result (car result))))
    
(defun lyskom-get-all-conferences (&optional feep)
  "Return a list of all conferences.
If FEEP is non-nil, show progress messages"
  (and feep (lyskom-message (lyskom-get-string 'getting-all-confs)))
  (prog1 (mapcar 'conf-z-info->conf-no
                 (conf-z-info-list->conf-z-infos
                  (blocking-do 'lookup-z-name "" nil t)))
    (and feep (lyskom-message (lyskom-get-string 'getting-all-confs-done)))))

(defun lyskom-is-supervisor (conf-no viewer-no)
  "Return non-nil if the supervisor of CONF-NO is VIEWER-NO."
  (or (eq viewer-no conf-no)
      (lyskom-is-strictly-supervisor conf-no viewer-no)))

(defun lyskom-is-strictly-supervisor (conf-no viewer-no)
  "Return non-nil if VIEWER-NO is strictly a supervisor of CONF-NO

Cannot be called from a callback."
  (let ((collector (make-collector))
        (conf-stat nil))
    (initiate-get-conf-stat 'background 'collector-push conf-no collector)
    (lyskom-wait-queue 'background)
    (setq conf-stat (car (collector->value collector)))

    (cond ((null viewer-no) nil)
          ((eq viewer-no 0) nil)
          ((null conf-stat) nil)
          ((eq viewer-no (conf-stat->supervisor conf-stat)) t)
          ((lyskom-is-member (conf-stat->supervisor conf-stat) viewer-no) t)
          (t nil))))

(defun lyskom-i-am-supervisor (conf-stat &optional may-block)
  "Returns non-nil if lyskom-pers-no is the supervisor of CONF-STAT.

If MAY-BLOCK is non-nil, this function never makes server calls, so if
the information required to answer accurately is not cached, this
function will return an incorrect result (nil instead of t)."
  (let ((marshal nil))
    (when conf-stat
      (let ((conf (conf-stat->supervisor conf-stat))
            (result nil))
        (while (and (not (memq conf marshal)) conf (not result))
          (setq marshal (cons conf marshal))
          (if (lyskom-try-get-membership conf t)
              (setq result t)
            (if may-block
                (setq conf (conf-stat->supervisor 
                            (blocking-do 'get-conf-stat conf)))
              (setq conf (conf-stat->supervisor (cache-get-conf-stat conf))))))
        result))))


(defun lyskom-is-member (conf-no pers-no &optional queue)
  "Return the membership in CONF-NO of PERS-NO
Optional argument QUEUE is the queue to send the queries on.

Cannot be called from a callback."
  (or (and (eq pers-no lyskom-pers-no)
           (lyskom-try-get-membership conf-no t))
      (let ((collector (make-collector)))
        (initiate-query-read-texts (or queue 'background)
                                   'collector-push
                                   pers-no
                                   conf-no
                                   nil 0
                                   collector)
        (lyskom-wait-queue (or queue 'background))
        (car (collector->value collector)))))

(defun lyskom-text-recipients (text-stat &optional want-types)
  "Return the list of recipients for TEXT-STAT.
If WANT-TYPES is non-nil then the result is an assoc list where the 
car of each element is the recipient number and the cdr is the type."
  (let ((result nil))
    (lyskom-traverse misc (text-stat->misc-info-list text-stat)
      (when (memq (misc-info->type misc) lyskom-recpt-types-list)
        (if want-types
            (setq result (cons (cons (misc-info->recipient-no misc)
                                     (misc-info->type misc))
                               result))
          (setq result (cons (misc-info->recipient-no misc)
                             result)))))
    (nreverse result)))

(defun lyskom-text-comments (text-stat &optional skip-footnotes)
  "Return the list of comments to TEXT-STAT.
If optional SKIP-FOOTNOTES is non-nil, do not return footnotes."
  (let ((result nil))
    (lyskom-traverse misc (text-stat->misc-info-list text-stat)
      (cond ((and (not skip-footnotes)
                  (eq (misc-info->type misc) 'FOOTN-IN))
             (setq result (cons (misc-info->footn-in misc) result)))
            ((eq (misc-info->type misc) 'COMM-IN)
             (setq result (cons (misc-info->comm-in misc) result)))))
    (nreverse result)))


(defun lyskom-find-conf-by-date (target-date)
  "Search all conferences for a conference created on or about TARGET-DATE.
Returns the conf-stat of the conference."
  (blocking-do-multiple ((lowest (find-next-conf-no 0))
                         (highest (first-unused-conf-no)))

    (let* ((index (+ lowest (/ (- highest lowest) 2)))
           (last-index (1- index))
           (result nil))
      (while (/= last-index index)

        ;; Get the first conf-stat after index

        (let ((conf-stat nil)
              (conf-no index))
          (while (and (null conf-stat)
                      (<= conf-no highest))
            (setq conf-stat (blocking-do 'get-conf-stat conf-no))
            (unless conf-stat
              (setq conf-no (blocking-do 'find-next-conf-no conf-no))))

          ;; If we were unable to find a conf-stat, then set highest
          ;; equal to index -- there are no conf-stats in that area,
          ;; so we might as well start looking below index

          (cond ((null conf-stat) (setq highest index))
                ((lyskom-time-greater (conf-stat->creation-time conf-stat)
                                      target-date)
                 (setq highest (1+ conf-no)))
                (t (setq lowest conf-no)))
          (setq last-index index result conf-stat)
          (setq index (+ lowest (/ (- highest lowest) 2)))))
      result)))


(defun lyskom-find-text-by-date (conf-stat target-date)
  "Search texts in CONF-STAT for a text added on or about TARGET-DATE.
Returns a cons of (LOCAL . GLOBAL)"
  (let* ((lowest (conf-stat->first-local-no conf-stat))
         (highest (+ lowest (conf-stat->no-of-texts conf-stat)))
         (conf-no (conf-stat->conf-no conf-stat))
         (result nil)
         (index (+ lowest (/ (- highest lowest) 2)))
         (last-index (1- index)))
    (while (/= last-index index)
      (let* ((map (blocking-do 'local-to-global 
                               conf-no
                               index
                               1)))
        (cond ((null map) (setq lowest highest))
              ((null (text-mapping->global-numbers map))
               (setq highest index))
              (t 
               (let* ((text-no (car (text-mapping->global-numbers map)))
                      (text-stat (blocking-do 'get-text-stat text-no))
                      (local-no (text-mapping->global-to-local map text-no))
                      (date (and text-stat
                                 (lyskom-traverse misc
                                     (text-stat->misc-info-list text-stat)
                                   (when (and (memq (misc-info->type misc) lyskom-recpt-types-list)
					      (eq (misc-info->recipient-no misc) conf-no))
                                     (lyskom-traverse-break 
                                      (if (misc-info->sent-at misc)
                                          (misc-info->sent-at misc)
                                        (text-stat->creation-time text-stat))))))))
                 (when text-stat
                   (setq index local-no)
                   (if (lyskom-time-greater
			date
                        target-date)
                       (setq highest index)
                     (setq lowest index))
                   (setq result text-stat))))))
      (setq last-index index)
      (setq index (+ lowest (/ (- highest lowest) 2))))
    (cons last-index (text-stat->text-no result))))

(put 'lyskom-parse-date-invalid 'error-conditions
     '(error lyskom-error))
(put 'lyskom-parse-date-invalid 'error-message
     "Error parsing date")

(defvar lyskom-year-window-start 80
  "Windowing threshold for YY year specifications.
Years below this are considered in the 21st century. Years above this
in the 20th century")

(defun lyskom-all-prefixes (s)
  "Return part of a regular expression matching all prefixes of S.
The value returned does not include the parens before at either ends of the expression."
  (let ((result nil)
        (i 1))
    (while (<= i (length s))
      (setq result (cons (substring s 0 i) result) i (1+ i)))
    (mapconcat 'regexp-quote result "\\|")))

(defun lyskom-read-date (prompt &optional empty)
  "Read a date from the minibuffer. 
Returns a list (YEAR MONTH DATE) corresponding to the user's input."
  (let ((result nil)
        (date nil))
    (while (null result)
      (setq date (lyskom-verified-read-from-minibuffer 
                  prompt 
                  date
                  (lambda (data)
                    (condition-case nil
                        (cond ((and empty (string-equal data "")) nil)
                              (t (lyskom-parse-date data) nil))
                      (lyskom-error (lyskom-get-string 'invalid-date-entry))))))
      (condition-case nil
          (cond ((and empty (string-equal date "")) (setq result t))
                (t (setq result (lyskom-parse-date date))))
        (lyskom-error nil)))
    (and (listp result) result)))

(defun lyskom-parse-date (arg)
  "Parse ARG (a string) as a date.
Returns a list (YEAR MONTH DAY) corresponding to the date in ARG."
  (let* ((month-regexp (concat "\\("
                               (mapconcat (lambda (el)
                                            (regexp-quote (car el)))
                                          lyskom-month-names
                                          "\\|")
                               "\\)"))
         (y-regexp (concat "\\("
                             (mapconcat (lambda (el)
                                          (lyskom-all-prefixes (lyskom-get-string el)))
                                        '(years year)
                                        "\\|")
                             "\\)"))
         (m-regexp (concat "\\("
                             (mapconcat (lambda (el)
                                          (lyskom-all-prefixes (lyskom-get-string el)))
                                        '(months month)
                                        "\\|")
                             "\\)"))
         (d-regexp (concat "\\("
                             (mapconcat (lambda (el)
                                          (lyskom-all-prefixes (lyskom-get-string el)))
                                        '(days day)
                                        "\\|")
                             "\\)"))
         (test-date (format-time-string "%x" '(20 0)))
         (now (decode-time))
         (current-day (elt now 3))
         (current-month (elt now 4))
         (current-year (elt now 5))
         (case-fold-search t)
         year month day di mi yi)

    ;; Look at test-date to see where dates in ambiguous cases should go
    (cond ((string-match "01.*16.*70" test-date) (setq di 2 mi 1 yi 3))
          ((string-match "01.*70.*16" test-date) (setq di 3 mi 1 yi 2))
          ((string-match "16.*01.*70" test-date) (setq di 1 mi 2 yi 3))
          ((string-match "16.*70.*01" test-date) (setq di 1 mi 3 yi 2))
          ((string-match "70.*01.*16" test-date) (setq di 3 mi 2 yi 1))
          ((string-match "70.*16.*01" test-date) (setq di 2 mi 3 yi 1)))

    ;; Match various variants
    (cond ((string-match "^\\s-*\\([0-9][0-9][0-9][0-9]?\\)\\s-*[ -./]\\s-*\\([0-9][0-9]?\\)\\s-*[ -./]\\s-*\\([0-9][0-9]?\\)\\s-*$" arg)
           ;; YYYY-MM-DD
           (setq year (lyskom-string-to-number (match-string 1 arg))
                 month (lyskom-string-to-number (match-string (if (> mi di) 3 2) arg))
                 day (lyskom-string-to-number (match-string (if (> mi di) 2 3) arg)))
           (when (> month 12) (setq month day day month))
           )

          ((string-match "^\\s-*\\([0-9][0-9]?\\)\\s-*[ -./]\\s-*\\([0-9][0-9]?\\)\\s-*[ -./]\\s-*\\([0-9][0-9][0-9][0-9]?\\)\\s-*$" arg)
           ;; MM-DD-YYYY
           (setq year (lyskom-string-to-number (match-string 3 arg))
                 month (lyskom-string-to-number (match-string (if (> mi di) 2 1) arg))
                 day (lyskom-string-to-number (match-string (if (> mi di) 1 2) arg)))
           (when (> month 12) (setq month day day month))
           )

          ((string-match "^\\s-*\\([0-9][0-9]\\)\\s-*[ -./]\\s-*\\([0-9][0-9]?\\)\\s-*[ -./]\\s-*\\([0-9][0-9]?\\)\\s-*$" arg)
           ;; Ambiguous:
           ;; YY/MM/DD, YY/DD/MM, MM/DD/YY, DD/MM/YY
           (setq year (lyskom-string-to-number (match-string yi arg))
                 month (lyskom-string-to-number (match-string mi arg))
                 day (lyskom-string-to-number (match-string di arg)))
           (when (> month 12) (setq month day day month))
           )

          ((string-match "^\\s-*\\([0-9][0-9]\\)\\s-*/\\s-*\\([0-9][0-9]\\)\\s-*$" arg)
           ;; Ambiguous:
           ;; MM/DD       Euro
           ;; DD/MM       US
           (let ((a (lyskom-string-to-number (match-string 1 arg)))
                 (b (lyskom-string-to-number (match-string 2 arg))))
             (cond ((> a 12) (setq month b day a))
                   ((> b 12) (setq month a day b))
                   ((> di mi) (setq month b day a))
                   (t (setq month a day b))))
           )

          ((string-match (format "^\\s-*\\([0-9][0-9]?\\)\\s-*%s\\s-*\\([0-9][0-9][0-9][0-9]\\)\\s-*$" month-regexp) arg)
           ;; DD Month YYYY
           (setq day (lyskom-string-to-number (match-string 1 arg))
                 month (cdr (lyskom-string-assoc (match-string 2 arg) lyskom-month-names))
                 year (lyskom-string-to-number (match-string 3 arg)))
           )

          ((string-match (format "^\\s-*%s \\([0-9][0-9]?\\),\\s-*\\([0-9][0-9][0-9]?[0-9]?\\)\\s-*$" month-regexp) arg)
           ;; Month DD, YYYY
           (setq day (lyskom-string-to-number (match-string 2 arg))
                 month (cdr (lyskom-string-assoc (match-string 1 arg) lyskom-month-names))
                 year (lyskom-string-to-number (match-string 3 arg)))
           )

          ((string-match (format "^\\s-*\\([0-9][0-9]?\\) %s,\\s-*\\([0-9][0-9][0-9]?[0-9]?\\)\\s-*$" month-regexp) arg)
           ;; DD Month, YYYY
           (setq day (lyskom-string-to-number (match-string 1 arg))
                 month (cdr (lyskom-string-assoc (match-string 2 arg) lyskom-month-names))
                 year (lyskom-string-to-number (match-string 3 arg)))
           )

          ((string-match (format "^\\s-*%s,?\\s-*\\([0-9][0-9][0-9][0-9]\\)\\s-*$" month-regexp) arg)
           ;; Ambiguous:
           ;; Month YYYY
           (setq day 1
                 month (cdr (lyskom-string-assoc (match-string 1 arg) lyskom-month-names))
                 year (lyskom-string-to-number (match-string 2 arg)))
           )

          ((string-match (format "^\\s-*%s \\([0-9][0-9]?\\)\\s-*$" month-regexp) arg)
           ;; Ambiguous:
           ;; Month DD, Month YY
           (setq month (cdr (lyskom-string-assoc (match-string 1 arg) lyskom-month-names))
                 day (lyskom-string-to-number (match-string 2 arg))
                 year current-year)
           (when (> day 31) (setq day 1 year day))
           )

          ((string-match (format "^\\s-*\\([0-9][0-9]?\\) %s\\s-*$" month-regexp) arg)
           ;; DD Month
           (setq day (lyskom-string-to-number (match-string 1 arg))
                 month (cdr (lyskom-string-assoc (match-string 2 arg) lyskom-month-names))
                 year current-year)
           )

          ((string-match (format "^\\s-*-?\\([0-9]+\\)\\s-*%s\\s-*$" y-regexp) arg)
           ;; -NN years
           (setq year (- current-year (lyskom-string-to-number (match-string 1 arg)))
                 day current-day
                 month current-month)
           )

          ((string-match (format "^\\s-*-?\\([0-9]+\\)\\s-*%s\\s-*$" m-regexp) arg)
           ;; -NN months
           (setq year current-year month current-month day current-day)
           (let ((count (lyskom-string-to-number (match-string 1 arg))))
             (while (> count 0)
               (if (>= count month)
                   (setq count (- count month)
                         year (1- year)
                         month 12)
                 (setq month (- month count) count 0))))
           (setq day (lyskom-adjust-day-for-date year month day))
           )

          ((string-match (format "^\\s-*-?\\([0-9]+\\)\\s-*%s\\s-*$" d-regexp) arg)
           ;; -NN days
           ;; Theres probably an off-by-one error in this code on year transitions
           ;; but I really don't care.
           (setq year current-year month current-month day current-day)
           (let ((count (lyskom-string-to-number (match-string 1 arg))))
             (while (> count 0)
               (if (>= count day)
                   (progn (setq count (- count day) month (1- month))
                          (when (< month 1) (setq month 12 year (1- year)))
                          (setq day (lyskom-adjust-day-for-date year month 31)))
                 (setq day (- day count) count 0))))
           (setq day (lyskom-adjust-day-for-date year month day))
           )
          ((string-match "^\\s-*\\([0-9][0-9][0-9][0-9]?\\)\\s-*$" arg)
           ;; YYYY goes last because the pattern is the most general
           (setq year (lyskom-string-to-number (match-string 0 arg)) month 1 day 1)
           )
          (t (signal 'lyskom-parse-date-invalid (list (format "Unrecognized date: %s" arg))))
          )

     ;; Do the window thing for two-digit dates
     (cond ((< year lyskom-year-window-start) (setq year (+ 2000 year)))
           ((< year 100) (setq year (+ 1900 year))))

     ;; Check date validity. Check month before checking days-in-month or stuff breaks
     (when (or (< month 1) (< day 1) (> month 12)
               (> day (lyskom-days-in-month year month)))
       (signal 'lyskom-parse-date-invalid (list (format "Invalid date: %s" arg))))


     (list year month day)
     ))

(defvar lyskom-month-limits '[31        ; Jan
                              28        ; Feb (non-leap)
                              31        ; Mar
                              30        ; Apr
                              31        ; May
                              30        ; Jun
                              31        ; Jul
                              31        ; Aug
                              30        ; Sep
                              31        ; Oct
                              30        ; Nov
                              31        ; Dec
                              ]
  "Number of days in various months. The value for february is not used.")

(defun lyskom-is-leap-year (year)
  "Return non-nil if YEAR is a leap year."
  (or (and (zerop (% year 4))
           (not (zerop (% year 100))))
      (zerop (% year 400))))

(defun lyskom-days-in-year (year)
  "Return number of days in YEAR."
  (if (lyskom-is-leap-year year) 366 365))

(defun lyskom-days-in-month (year month)
  "Return the number of days mONTH of YEAR.
Args: YEAR MONTH"
  (cond ((eq month 2) (if (lyskom-is-leap-year year) 29 28))
        (t (elt lyskom-month-limits (1- month)))))


(defun lyskom-adjust-day-for-date (year month day)
  "Return an appropriate day of month for a combination of YEAR, MONTH and DAY.
If DAY is too high for YEAR and MONTH, return the maximum permissible DAY for
that combination. Otherwise return DAY."
  (let ((mdays (lyskom-days-in-month year month)))
    (if (> day mdays) mdays day)))


;;; ================================================================
;;; Check noconversion

(defun lyskom-viewing-noconversion ()
  "Return non-nil if we are reviewing in noconversion mode."
  (eq 'kom-review-noconversion lyskom-current-command))


;;; ================================================================
;;; Read membership types

(defun lyskom-read-membership-type ()
  "Interactively read a membership type"
  (let ((invitation (lyskom-j-or-n-p 'mship-type-invitation-q))
        (passive (lyskom-j-or-n-p 'mship-type-passive-q))
        (secret (lyskom-j-or-n-p 'mship-type-secret-q)))
    (lyskom-create-membership-type invitation
                                   passive
                                   secret
                                   nil
                                   nil
                                   nil
                                   nil
                                   nil)))


;;; ================================================================
;;; String truncation
;;;

(defun lyskom-truncate-to-lines (string threshold show-lines &optional width)
  "If STRING is more than THRESHOLD lines on screen, truncate it to  SHOW-LINES.
Optional argument WIDTH is thw window width to use instead of window-width.

This function takes the setting of truncate-lines into account, so
the resulting string may not have SHOW-LINES newline characters.

Result is eq to STRING when no truncation is required.

The result is approximate when truncate-lines is non-nil since different
Emacsen use a different number of characters for the continuation marks
at the end of broken lines. We assume one character continuation marks."
  (let ((line-length (if truncate-lines lyskom-max-int (- (or width (window-width)) 1)))
        (count 0)
        (end nil)
        (pos 0))
    (while (and (< pos (length string)) (< count threshold))
      (setq count (1+ count))
      (let ((next (string-match "\\(\n\\|\\'\\)" string pos)))
        (if (> (- next pos) line-length)
            (setq pos (+ pos line-length))
          (setq pos (match-end 0))))
        (when (= count show-lines)
          (setq end pos)))

    (if (>= count threshold)
        (substring string 0 end)
      string)))


;;; ================================================================
;;; Color model manipulations
;;;
;;; (Of COURSE you need this in a KOM client!)
;;;

(defun lyskom-get-color-highlight (color distance)
  "Create a highlight color for COLOR that is DISTANCE away.
COLOR is a list of R G and B components from 0 to 65535.
DISTANCE is a non-negative integer no larger than 1.0, that in some
way specifies how far away from the original color the new color
should be."
  (when color
    (let* ((hls (lyskom-rgb-to-hls (mapcar (lambda (x) (/ x 65535.0)) color)))
           (l (elt hls 1)))
      (if (> l 0.6)
          (setq l (- l distance))
        (setq l (+ l distance)))
      (cond ((> l 1.0) (setq l 1.0))
            ((< l 0.0) (setq l 0.0)))
      (aset hls 1 l)

      (apply 'format "#%02x%02x%02x"
             (mapcar (lambda (c) (round (* 255 c)))
                     (lyskom-hls-to-rgb hls))))))


(defun lyskom-rgb-to-hls (rgb)
  "Convert a point in RGB color space to a point in HLS color space.

Input value is a vector [R G B], where R, G and B represent red, green
and blue components, respectively. Each value is a non-negative
floating-point value no larger than 1.0.

Output is a vector [H L S], where H, L and S represend hue, lightness
and saturation, respectively. H is in the range 0..360, L and S are
non-negative floating-point numbers no higher than 1.0. If the input
color is a shade of gray (all components are equal), then H in the
output is nil.

Algorithm adapted from Foley, \"Computer Graphics\"."
  (let* ((r (elt rgb 0))
         (g (elt rgb 1))
         (b (elt rgb 2))
         (rgbmin (min r g b))
         (rgbmax (max r g b))
         (h nil)
         (l (/ (+ rgbmax rgbmin) 2.0))
         (s nil)
         (delta (- rgbmax rgbmin)))
    (if (zerop delta)
        (setq s 0.0)
      (if (<= l 0.5)
          (setq s (/ delta (+ rgbmax rgbmin)))
        (setq s (/ delta (- 2.0 rgbmax rgbmin))))
      (cond ((= r rgbmax)
             (setq h (/ (- g b) delta)))
            ((= g rgbmax)
             (setq h (+ 2.0 (/ (- b r) delta))))
            ((= b rgbmax)
             (setq h (+ 4.0 (/ (- r g) delta)))))
      (setq h (* h 60.0))
      (if (< h 0)
          (setq h (+ 360.0 h))))
    (vector h l s)))

(defun lyskom-hls-to-rgb-value (n1 n2 h)
  "Helper function for lyskom-hls-to-rgb"
  (cond ((> h 360) (setq h (- h 360.0)))
        ((< h 0) (setq h (+ h 360.0))))

  (cond ((< h 60)  (+ n1 (* (- n2 n1) (/ h 60.0))))
        ((< h 180) n2)
        ((< h 240) (+ n1 (* (- n2 n1) (/ (- 240.0 h) 60.0))))
        (t n1)))

(defun lyskom-hls-to-rgb (hls)
  "Convert a point in HLS color space to a point in RGB color space.

Input HLS is a vector [H L S], where H, L and S represend hue,
lightness and saturation, respectively. H is in the range 0..360, L
and S are non-negative floating-point numbers no higher than 1.0. If
the input color is a shade of gray (all components are equal), then S
in the input is ignored and may be anything.

Output value is a vector [R G B], where R, G and B represent red,
green and blue components, respectively. Each value is in the range
1..1.0.

This algorithm is adapted from Foley, \"Computer Graphics\" (and
has the bug in that algorithm fixed)."
  (let* ((h (elt hls 0))
         (l (elt hls 1))
         (s (elt hls 2))
         (m2 (if (<= l 0.5)
                 (* l (+ 1.0 s))
               (+ l (* s (- 1 l)))))
         (m1 (- (* 2 l) m2)))
    (if (zerop s)
        (vector l l l)
      (vector (lyskom-hls-to-rgb-value m1 m2 (+ h 120))
              (lyskom-hls-to-rgb-value m1 m2 h)
              (lyskom-hls-to-rgb-value m1 m2 (- h 120))))))


;;; ================================================================
;;; Automatically test that RGB->X and X->RGB color model conversions
;;; really are the inverse of each other.
;;; 
;;; (defun lyskom-test-color-model ()
;;;   (let ((r 0.0)
;;;         (g 0.0)
;;;         (b 0.0)
;;;         (step 0.05))
;;;     (while (<= r 1.0)
;;;       (setq g 0.0)
;;;       (while (<= g 1.0)
;;;         (setq b 0.0)
;;;         (while (<= b 1.0)
;;;           (let ((tmp (lyskom-hls-to-rgb
;;;                       (lyskom-rgb-to-hls (vector r g b)))))
;;;             (unless (and (< (- r (elt tmp 0)) 0.000000001)
;;;                          (< (- g (elt tmp 1)) 0.000000001)
;;;                          (< (- b (elt tmp 2)) 0.000000001))
;;;               (message "Mismatch %1.2f,%1.2f,%1.2f gave %S/%S" r g b (lyskom-rgb-to-hls (vector r g b)) tmp)))
;;;           (setq b (+ b step)))
;;;         (setq g (+ g step)))
;;;       (setq r (+ r step)))))

;;;(defun lyskom-test-auto-colors ()
;;;  (make-face 'test-1)
;;;  (make-face 'test-2)
;;;  (make-face 'test-default)
;;;  (while t
;;;    (let ((foreground (read-from-minibuffer "Foreground: "))
;;;          (background (read-from-minibuffer "Background: ")))
;;;      (pop-to-buffer (get-buffer-create "*kom*-test"))
;;;      (erase-buffer)
;;;      (set-face-foreground 'test-default foreground)
;;;      (set-face-background 'test-default background)
;;;      (set-face-background 'test-1 (lyskom-get-color-highlight (x-color-values background) 0.05))
;;;      (set-face-background 'test-2 (lyskom-get-color-highlight (x-color-values background) 0.025))
;;;      (let ((lyskom-buffer (current-buffer)))
;;;        (lyskom-format-insert "\
;;;%#3@Läsa nästa fotnot...
;;;8408827 idag 00:40 /1 rad/ Lunkwill/CH ( Auf das Universum! )
;;;Fotnot till text 8408825 av Lunkwill/CH ( Auf das Universum! )
;;;Mottagare: Nätverk, Internet, LysNET, Sunet... <30873>
;;;Mottagare: SUBnet (Stångåstadens och LiU:s) studentbostadsnät <12152>
;;;Mottagare: Lunkwill/CH ( Auf das Universum! ) <2092>
;;;Ärende: Vårmötet
;;;%[%#1$------------------------------------------------------------
;;;%]%[%#2$Och tack så mycket för förklaringarna! =)
;;;%]%[%#1$(8408827) /Lunkwill/CH ( Auf das Universum! )/------
;;;%]Gå till nästa möte...
;;;SUN erfarenhetsutbyte - 1 oläst
;;;Läsa nästa text...
;;;8408823 idag 00:39 /4 rader/ Erik Persson, Lysato(r)
;;;Kommentar till text 8407161 av Dejan (något desperat)
;;;Mottagare: SUN erfarenhetsutbyte <23622>
;;;Ärende: Skapa partitionstabell
;;;%[%#1$------------------------------------------------------------
;;;%]%[%#2$Om du nu vill spela Fibre Channel med IDE-RAID så går du över ån efter
;;;vatten. Det finns redan IDE-RAID med FC-interface.  Dock har jag inte
;;;sett någon som stödjer något annat än FC-AL vilket är lite
;;;begränsande.
;;;%]%[%#1$(8408823) ------------------------------------------
;;;%]Gå till nästa möte...
;;;
;;;
;;;
;;;"
;;;                              '(face test-1)
;;;                              '(face test-2)
;;;                              '(face test-default))))))
;;;

(defun lyskom-safe-color-name (c)
  "Return a color name as a string or nil." 
  (cond ((stringp c) c)
	((lyskom-color-specifier-p c) (lyskom-color-name c))))


(defun lyskom-is-url (text)
  "Return non-nil if TEXT can be interpreted as an URL.
Any whitespace and newlines in TEXT will be ignored."
  (save-match-data
    (let ((text (replace-in-string text "\\s-+" "")))
      (or (string-match "^\\(file://\\|ftp://\\|gopher://\\|http://\\|https://\\|news:\\|wais://\\|mailto:\\|telnet:\\|rtsp:\\)[^\t \012\014\"<>|\\]*[^][\t \012\014\"<>|.,!(){}?'`:;]$" text)
          (string-match "^\\(www\\|ftp\\|home\\)\\.[^\t \012\014\"<>|\\]*[^][\t \012\014\"<>|.,!(){}?'`:;]$"  text)))))

(defun lyskom-nag-about-presentation (&optional harder)
  (unless kom-dont-complain-about-missing-presentation
    (blocking-do-multiple
        ((pers-stat (get-pers-stat (or lyskom-pers-no 0)))
         (conf-stat (get-conf-stat (or lyskom-pers-no 0))))
      (when (and pers-stat conf-stat
                 (or harder
                     (eq (random 
                          (max 3 (- 20
                                    (pers-stat->no-of-created-texts pers-stat)
                                    (* 5 (pers-stat->created-confs pers-stat))
                                    (* 2 (pers-stat->created-persons pers-stat)))))
                         1))
                 (null (blocking-do 'get-text-stat 
                                    (conf-stat->presentation conf-stat))))
        (lyskom-beep 2)
        (lyskom-format-insert 'why-you-got-no-presentation
                              `(face ,kom-warning-face)
                              (pers-stat->no-of-created-texts pers-stat)
                              (lyskom-session-nickname)
                              72)
        (sit-for (if harder 0 1))))))

(defun lyskom-get-server-stats ()
  (let ((descr (blocking-do 'get-stats-description)))
    (when descr
      (let ((result (lyskom-create-server-stats))
            (collector (make-collector)))
      (set-server-stats->when result
       (listify-vector (stats-description->when descr)))
      (set-server-stats->what result
       (listify-vector (stats-description->what descr)))
      (lyskom-traverse name (stats-description->what descr)
        (initiate-get-stats 'main
                            (lambda (res name c)
                              (when res
                                (collector-push (cons name (listify-vector res)) c)))
                            name
                            name
                            collector))
      (lyskom-wait-queue 'main)

      (set-server-stats->values result
                                (nreverse (collector->value collector)))
      result))))

(defun lyskom-format-units (val units base-unit)
  "Format VAL using units. UNITS is an alist (COUNT . NAME), where
NAME is the name of a unit and COUNT is the number of base units for
that name. For example, if the base unit is seconds, then minutes
could be defined with \(60 . \"min\"). BASE-UNIT is the name of the
base unit \(which implicitly has a count of 1)."
  (mapconcat
   (lambda (el)
     (format "%d%s"
             (car el)
             (cond ((stringp (cdr el)) (cdr el))
                   ((symbolp (cdr el)) (lyskom-get-string (cdr el))))))
   (nreverse
    (let ((result nil))
      (lyskom-traverse unit units
        (when (>= val (car unit))
          (let ((a (/ val (car unit))))
            (when (> a 0) (setq result (cons (cons a (cdr unit)) result)))
            (setq val (% val (car unit))))))
      (if (> val 0)
          (cons (cons val base-unit) result)
        result)))
   ""))

(defun lyskom-format-time-units (val &optional what)
  "Format VAL as values per time unit in such a way that the
result is readable (i.e. whole numbers, if possible). WHAT is
the unit measured."
  (let ((last-unit nil))
    (if (eq val 0) 
        (lyskom-format "0%#1?b%[ %#1s%]%[%]/%#2s" 
                       what
                       (lyskom-get-string 'unit-hour))
      (lyskom-traverse unit '((unit-second . 1)
                              (unit-minute . 60)
                              (unit-hour . 60)
                              (unit-day . 24)
                              (unit-month . 30)
                              (unit-year . 12))
        (setq val (* val (cdr unit)))
        (setq last-unit unit)
        (when (> val 1)
          (lyskom-traverse-break)))
      (lyskom-format "%0.0.4#1f%#2?b%[ %#2s%]%[%]/%#3s" val
                     what (lyskom-get-string (car last-unit))))))




(defun lyskom-extended-status-information (tag)
  "Return non-nil is extended status information indicated by TAG
should be shown."
  (cond ((eq kom-extended-status-information t) t)
        ((assq tag kom-extended-status-information) 
         (cdr (assq tag kom-extended-status-information)))
        ((cdr (assq t kom-extended-status-information)))))

(defun lyskom-extended-status-override (tag)
  "Return a new value for kom-extended-status-information that
reflects an override of the value by the value of TAG."
  (cond ((eq kom-extended-status-information t) t)
        ((assq tag kom-extended-status-information)
         (cdr (assq tag kom-extended-status-information)))
        (t kom-extended-status-information)))




;;; ============================================================
;;; Magic that allows us to clear the initial value in the
;;; minibuffer when the user types a character.
;;;
;;; We can't alter the buffer contents in before-change-functions
;;; since this can crash Gnu Emacs 20.7 (it computes positions
;;; internally before calling before-change-functions, and if
;;; those positions are invalid afterwards all sorts of things
;;; can happen).
;;;
;;; Theory of operation:
;;;
;;; In the pre-command-hook, record the position of point and
;;; the contents of the minibuffer.
;;;
;;; In before-change-functions, check if there is initial
;;; input in the buffer yet -- if not we're not yet ready
;;; to erase anything.
;;;
;;; In the post-command-hook, if the buffer contents have changed
;;; since there was inital input in the buffer, erase the initial
;;; input. If the position of point has moved, disable all the
;;; magic (both can happen at the same time).
;;;
;;; We set up advice on read-from-minibuffer and completing-read
;;; that mangles the initial input appropriately.
;;;

(defvar lyskom-minibuffer-point nil)
(defvar lyskom-minibuffer-string nil)
(defvar lyskom-minibuffer-do-change nil)

(defun lyskom-magic-minibuffer-pre-command (&rest args)
  "Save current status of the minibuffer for later magic."
  (setq lyskom-minibuffer-point (point)
        lyskom-minibuffer-string (buffer-string)))

(defun lyskom-magic-minibuffer-before-change (&rest args)
  "Check if the initial input has been placed in the minibuffer.

This function is called at least once before the initial input
has been placed in the minibuffer by Emacs. Most magic needs to
be disabled until the input has been deposited."
  (setq lyskom-minibuffer-do-change
        (lyskom-next-property-bounds (point-min)
                                     (point-max) 'lyskom-initial-mbc)))

(defun lyskom-magic-minibuffer-post-command (&rest args)
  "Function for use as post-command-hook in magic minibuffer.

If the contents of the minibuffer have changed since initial input
has been placed in the minibuffer, erase the initial input. This
happens when the user enters something in the minibuffer.

If point has moved, disable magic. This happens either after we
delete the initial contents (which is OK) or after the user moves
point without altering the buffer contents."
  (when (and (boundp 'lyskom-minibuffer-do-change)
             lyskom-minibuffer-do-change
             (not (equal (buffer-string) lyskom-minibuffer-string)))
    (let ((ranges nil)
          (tmp nil)
          (start (point-min)))
      (while (setq tmp (lyskom-next-property-bounds
                        start (point-max) 'lyskom-initial-mbc))
        (setq ranges (cons tmp ranges)
              start (cdr tmp)))
      (lyskom-traverse range ranges
        (delete-region (car range) (cdr range)))
      (when ranges
        (lyskom-magic-minibuffer-cancel))))

  (unless (or (not (boundp 'lyskom-minibuffer-point))
              (null lyskom-minibuffer-point)
              (eq lyskom-minibuffer-point (point)))
    (lyskom-magic-minibuffer-cancel)))

(defun lyskom-magic-minibuffer-cancel ()
  "Remove hooks used to make the minibuffer magic."
  (remove-hook 'pre-command-hook 'lyskom-magic-minibuffer-pre-command)
  (remove-hook 'post-command-hook 'lyskom-magic-minibuffer-post-command)
  (remove-hook 'before-change-functions 'lyskom-magic-minibuffer-before-change))

(defun lyskom-magic-minibuffer-mangle-initial (initial)
  "Add text properties to INITIAL (a string or cons) so it is
suitable for use as initial input in a magic minibuffer."
  (cond ((null initial) nil)
        ((stringp initial)
         (let ((tmp (copy-sequence initial)))
           (add-text-properties 0 (length tmp) '(lyskom-initial-mbc t rear-nonsticky t end-open t start-open t front-sticky nil) tmp)
           tmp))
        ((consp initial)
         (let ((tmp (copy-sequence (car initial))))
           (add-text-properties 0 (length tmp) '(lyskom-initial-mbc t rear-nonsticky t end-open t start-open t front-sticky nil) tmp)
           (cons tmp (cdr initial))))

        (t initial)))



;;; ------------------------------------------------------------
;;; Gnu Emacs and XEmacs have different names for parameters.
;;; This is stupid and makes the code below a lot uglier.

(defadvice read-from-minibuffer (around lyskom-magic-minibuffer-read-from-minibuffer nil disable)
  (let* ((initial-input (and (boundp 'initial-input) (lyskom-magic-minibuffer-mangle-initial (symbol-value 'initial-input))))
	 (initial-contents (and (boundp 'initial-contents) (lyskom-magic-minibuffer-mangle-initial (symbol-value 'initial-contents))))
	 (initial (and (boundp 'initial) (lyskom-magic-minibuffer-mangle-initial (symbol-value 'initial))))
	 (init (and (boundp 'init) (lyskom-magic-minibuffer-mangle-initial (symbol-value 'init))))
	 (result ad-do-it))
    (lyskom-ignore initial-input)
    (lyskom-ignore initial-contents)
    (lyskom-ignore initial)
    (lyskom-ignore init)
    (when (stringp result)
      (remove-text-properties 0 (length result) '(end-open nil rear-nonsticky nil lyskom-initial-mbc nil) result)
      (unless (or (text-properties-at 0 result)
		  (next-property-change 0 result))
	(set-text-properties 0 (length result) nil result)))
    result))

(defadvice completing-read (around lyskom-magic-minibuffer-completing-read nil disable)
  (let* ((initial-input (and (boundp 'initial-input) (lyskom-magic-minibuffer-mangle-initial (symbol-value 'initial-input))))
	 (initial-contents (and (boundp 'initial-contents) (lyskom-magic-minibuffer-mangle-initial (symbol-value 'initial-contents))))
	 (initial (and (boundp 'initial) (lyskom-magic-minibuffer-mangle-initial (symbol-value 'initial))))
	 (init (and (boundp 'init) (lyskom-magic-minibuffer-mangle-initial (symbol-value 'init))))
	 (result ad-do-it))
    (lyskom-ignore initial-input)
    (lyskom-ignore initial-contents)
    (lyskom-ignore initial)
    (lyskom-ignore init)
    (when (stringp result)
      (remove-text-properties 0 (length result) '(end-open nil rear-nonsticky nil lyskom-initial-mbc nil) result)
      (unless (or (text-properties-at 0 result)
		  (next-property-change 0 result))
	(set-text-properties 0 (length result) nil result)))
    result))

(defun lyskom-magic-minibuffer-add-advice ()
  (ad-enable-advice 'read-from-minibuffer 'around 'lyskom-magic-minibuffer-read-from-minibuffer)
  (ad-enable-advice 'completing-read 'around 'lyskom-magic-minibuffer-completing-read)
  (ad-activate 'read-from-minibuffer)
  (ad-activate 'completing-read)
  )

(defun lyskom-magic-minibuffer-cancel-advice ()
  (ad-disable-advice 'read-from-minibuffer 'around 'lyskom-magic-minibuffer-read-from-minibuffer)
  (ad-disable-advice 'completing-read 'around 'lyskom-magic-minibuffer-completing-read)
  )

(defmacro lyskom-with-magic-minibuffer (&rest forms)
  `(let ((lyskom-minibuffer-point nil)
         (lyskom-minibuffer-string nil)
         (lyskom-minibuffer-do-change nil)
         (pre-command-hook pre-command-hook)
         (post-command-hook post-command-hook)
         (before-change-functions before-change-functions))
     (add-hook 'pre-command-hook 'lyskom-magic-minibuffer-pre-command)
     (add-hook 'post-command-hook 'lyskom-magic-minibuffer-post-command)
     (add-hook 'before-change-functions 'lyskom-magic-minibuffer-before-change)
     (unwind-protect
         (progn (lyskom-magic-minibuffer-add-advice)
                ,@forms)
       (lyskom-magic-minibuffer-cancel-advice))))




(defun lyskom-completing-read (prompt table &optional predicate
                                      require-match init hist def)
  (let ((this-command this-command))
    (lyskom-ignore def)
    (lyskom-with-lyskom-minibuffer
     (lyskom-with-magic-minibuffer
      (let ((res (completing-read prompt table predicate require-match init hist)))
        (if (or (null res) (equal res "")) def res))))))

(defun lyskom-read-from-minibuffer (prompt 
                                    &optional initial-contents
                                    keymap read hist def)
  (let ((this-command this-command))
    (lyskom-ignore def)
    (lyskom-with-lyskom-minibuffer
     (lyskom-with-magic-minibuffer
      (or (read-from-minibuffer prompt initial-contents keymap read hist) def)))))

(defun lyskom-set-connection-time-format (val)
  "Change protocol time format to UTC if VAL is non-nil."
  ;; Check for broken encode-time
  (setq val (and val 
                 (condition-case nil
                     (not (equal (encode-time 0 0 0 1 1 1970 0)
                                 (encode-time 0 0 0 1 1 1970 -4)))
                   (error nil))))
  (when (or (blocking-do 'set-connection-time-format val)
            (null val))
    (lyskom-traverse-buffer-hierarchy
     (lambda (buf)
       (save-current-buffer
         (set-buffer buf)
         (make-local-variable 'lyskom-server-uses-utc)
         (setq lyskom-server-uses-utc val)))
     lyskom-buffer)))

(put 'lyskom-integer-conversion-error 'error-conditions '(lyskom-integer-conversion-error lyskom-error error))
(put 'lyskom-integer-conversion-error 'error-message "Invalid integer conversion")

(defun lyskom-string-to-int (string &optional signal-error)
  "Convert STRING to an integer by parsing it as a decimal number.
Returns NIL if STRING is not a number.
This does not parse floating point numbers.
It ignores leading spaces and tabs."
  (or (let ((tmp (lyskom-string-to-number string)))
        (cond ((eq tmp 0) 
               (and (string-match "^\\s-*0+\\s-*" string) 0))
              ((integerp tmp) tmp)
              (t nil)))
      (and signal-error
           (signal 'lyskom-integer-conversion-error string))))

(defun lyskom-privilege-string (privs &optional format sep)
  (setq format (or format "%#1s")
        sep (or sep ", "))
  (mapconcat (lambda (x) (lyskom-format format (lyskom-get-string x)))
             (or (delq nil (list
                            (and (privs->wheel privs) 'privs-wheel)
                            (and (privs->admin privs) 'privs-admin)
                            (and (privs->statistic privs) 'privs-statistic)
                            (and (privs->create_pers privs) 'privs-create-pers)
                            (and (privs->create_conf privs) 'privs-create-conf)
                            (and (privs->change_name privs) 'privs-change-name)
                            (and (privs->flg7 privs) 'privs-flg7)
                            (and (privs->flg8 privs) 'privs-flg8)
                            (and (privs->flg9 privs) 'privs-flg9)
                            (and (privs->flg10 privs) 'privs-flg10)
                            (and (privs->flg11 privs) 'privs-flg11)
                            (and (privs->flg12 privs) 'privs-flg12)
                            (and (privs->flg13 privs) 'privs-flg13)
                            (and (privs->flg14 privs) 'privs-flg14)
                            (and (privs->flg15 privs) 'privs-flg15)
                            (and (privs->flg16 privs) 'privs-flg16)
                            ))
                 '(lyskom-no-privileges))
             sep))

(defvar lyskom-gensym-index 0
  "Index to use in lyskom-gensym")

(defun lyskom-gensym ()
  "Generate a unique symbol"
  (let ((name "t"))
    (while (intern-soft name)
      (setq lyskom-gensym-index (1+ lyskom-gensym-index))
      (setq name (format "lyskom-gensym: %d" lyskom-gensym-index)))
    (intern name)))

(put 'lyskom-with-magic-minibuffer 'edebug-form-spec '(body))



;;; ================================================================
;;; List text summaries

(defvar lyskom-list-text-summary-params)
(defvar lyskom-list-text-summary-params-persistent)
(defvar lyskom-list-text-summary-format)

(defsubst lyskom-list-text-summary-get (sym)
  (or (plist-get lyskom-list-text-summary-params sym)
      (plist-get lyskom-list-text-summary-params-persistent sym)))

(defsubst lyskom-list-text-summary-put (sym val &optional persistent)
  (if persistent
      (setq lyskom-list-text-summary-params-persistent
            (plist-put lyskom-list-text-summary-params-persistent sym val))
    (setq lyskom-list-text-summary-params
          (plist-put lyskom-list-text-summary-params sym val))))

(defun lyskom-max-mark-width ()
  "Return the maximum width of the user's symbolic mark strings."
  (apply 'max 3 (mapcar (function (lambda (x) (length (car x))))
                        kom-symbolic-marks-alist)))

(defun lyskom-max-text-no-width ()
  "Ask the server about the maximum text number width."
  (length (int-to-string (blocking-do 'find-previous-text-no
                                      lyskom-max-int))))

(defun lyskom-symbolic-mark-type-string (mark-no &optional strict)
  "Return a symbolic name string for the mark-type of MARK.
If optional argument STRICT is non-nil, return nil if there is no symbolic
 name for the mark type."
  (or (car-safe (rassoc mark-no kom-symbolic-marks-alist))
      (and (not strict) (int-to-string mark-no))))





(defvar lyskom-text-summary-fields
  '((mark-type :width lyskom-max-mark-width
               :prompt mark-type
               :align left
               :format "s"
               :output (lambda (text-stat)
                         (let ((mark (or (lyskom-list-text-summary-get :mark)
                                         (cache-text-is-marked 
                                          (text-stat->text-no text-stat)))))
                           (if mark
                               (lyskom-symbolic-mark-type-string (mark->mark-type mark))
                             "")))
               )

    (mark-no :width 3
             :prompt mark-no
             :align left
             :format "s"
             :output (lambda (text-stat)
                       (let ((mark (cache-text-is-marked 
                                    (text-stat->text-no text-stat))))
                         (if mark (int-to-string (mark->mark-type mark)) "")))
               )

    (comments  :width 2
               :prompt Comments
               :align right
               :format "s"
               :output (lambda (text-stat)
                         (if (zerop (length (lyskom-text-comments text-stat)))
                             ""
                           (int-to-string
                            (length (lyskom-text-comments text-stat)))))
               )

    (mark-count :width 2
                :prompt Num-marks
                :align right
                :format "s"
                :output (lambda (text-stat)
                          (if (zerop (text-stat->no-of-marks text-stat))
                              ""
                            (int-to-string (text-stat->no-of-marks text-stat))))
                )

    (text-no   :width lyskom-max-text-no-width
               :prompt Texts
               :align left
               :format "n"
               :output (lambda (text-stat) (text-stat->text-no text-stat)))

    (written   :width (lambda ()
                        (max (length (lyskom-format-time 'time))
                             (length (lyskom-format-time
                                      'timeformat-yyyy-mm-dd))))
               :prompt Written
               :align left
               :format "s"
               :output (lambda (text-stat)
                         (let ((now (lyskom-current-server-time))
                               (time (or (lyskom-mx-date-to-time
                                          (car (lyskom-get-aux-item 
                                                (text-stat->aux-items text-stat) 21)))
                                         (text-stat->creation-time text-stat))))
                           (if (and (= (time->year now) (time->year time))
                                    (= (time->mon now) (time->mon time))
                                    (= (time->mday now) (time->mday time)))
                               (lyskom-format-time 'time time)
                             (lyskom-format-time 'timeformat-yyyy-mm-dd 
                                                 time))))
               )

    (lines     :width 5
               :prompt Lines
               :align right
               :align left
               :format "d"
               :output (lambda (text-stat)
                         (text-stat->no-of-lines text-stat))
               )

    (author    :width nil
               :weight 50.0
               :prompt Author
               :align left
               :format "P"
               :output (lambda (text-stat)
                         (let ((mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 17)))
                               (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 16))))
                           (if (or mx-from mx-author)
                               (lyskom-format-mx-author mx-from mx-author)
                             (text-stat->author text-stat))))
               )

    (subject   :width nil
               :weight 100.0
               :prompt Subject
               :align left
               :format "s"
               :output (lambda (text-stat)
                         (lyskom-list-text-summary-subject text-stat))
               )
    )
  "Specification of fields that can be included in text summary lists.

Each element is a cons (KEY . PLIST), where KEY is the name of the 
field type (arbitrary symbol). and PLIST is a property list, which 
may include the following properties:

    :width      Witdh of the field. Function, number or nil.
    :weight     Weight for dynamically sized field. Floating point.
    :prompt     Heading for the column. Symbol.
    :align      Alignment of the column. Either left or right.
    :format     Format code for the column. For lyskom-format.
    :output     Function that outputs the column.

If width is nil, the field is dynamically sized. All dynamically sized
fields share the available width, according to their respective weights.
Columns will always be at least as wide as the column heading.

Note that weight must be a floating-point number, or the arithmetic
used to comput dynamic columns will not work. The weight is only used
for dynamic columns."
  )


(defun lyskom-list-text-summary-subject (text-stat)
  "Handle subject field for a text summary listing."
  (let* ((mode (lyskom-list-text-summary-get :subject-mode))
         (indent (lyskom-list-text-summary-get :subject-indent))
         (text (lyskom-list-text-summary-get :text))
         (txt (text->decoded-text-mass text text-stat))
         (eos (and txt (string-match (regexp-quote "\n") txt)))
         (subject (if eos (substring txt 0 eos) "")))

    (cond ((eq mode 'unique)
           (if (lyskom-string-member 
                subject (lyskom-list-text-summary-get :subjects))
               (throw 'lyskom-list-text-summary-abort t)
             (lyskom-list-text-summary-put
              :subjects (cons subject (lyskom-list-text-summary-get :subjects))
              t)))

          ((eq mode 'thread)
           (when (and (not (eq indent 0))
                      (string-equal (lyskom-list-text-summary-get :subject-last)
                                    subject))
             (setq subject ""))))

    (unless (string-equal "" subject)
      (lyskom-list-text-summary-put :subject-last subject t))

    (lyskom-format "%#1@%#2r"
                   (lyskom-default-button 'text (text-stat->text-no text-stat))
                   (concat (cond ((not indent) "")
                                 ((>= indent 10)
                                  (format ">>>[%d]>>>" indent))
                                 (t (make-string indent ?\>)))
                           subject))

    ))


;; Sub-optimal (i.e. too much code) implementation for the sake of
;; clarity. It is possible to implement this with far less computation
;; and fewer variables, but the code tends to be harder to understand.

(defun lyskom-summary-line-format-string (spec)
  "Return header and format string for text summary lines.
SPEC is the specification for the line. It is a list of symbols
that must exist as keys in lyskom-text-summary-fields and strings,
which must be valid for lyskom-format (i.e. with percent signs
doubled.

Returns a cons of the header string and a format string for the
"
  (let ((total-field-width 0)
        (field-width-list nil)
        (dynamic-width nil)
        (dynamic-weight 0))

    ;; Compute dynamic widths

    (lyskom-traverse el spec
      (if (stringp el)
          (setq total-field-width (+ total-field-width 
                                     (lyskom-string-width el)))
        (let* ((field-spec (cdr (assq el lyskom-text-summary-fields)))
               (prompt (plist-get field-spec :prompt))
               (dw (plist-get field-spec :weight))
               (w1 (plist-get field-spec :width))
               (w2 (cond ((functionp w1) (funcall w1))
                         ((numberp w1) w1)
                         ((null w1) nil))))
          (if (null w2)
              (setq dynamic-weight (+ dynamic-weight dw))
            (setq w2 (max w2 (if prompt (lyskom-string-width (lyskom-get-string prompt)) 0)))
            (setq total-field-width (+ total-field-width w2)))
          (setq field-width-list (cons w2 field-width-list)))))

    (setq dynamic-width (- (1- (window-width)) total-field-width)
          field-width-list (nreverse field-width-list))

    ;; Now dynamic-width contains the total width allocated to dynamically
    ;; sized fields. We will calculate the individual field widths like 
    ;; this:
    ;;
    ;; field-width := floor(dynamic-width / num-dynamic-fields)
    ;; dynamic-width := dynamic-width - field-width
    ;; num-dynamic-fields := num-dynamic-fields - 1
    ;;
    ;; This spreads the available space as equally as possible and 
    ;; eliminates any possibility of round-off errors since everything
    ;; can be implemented using integer arithmetic.

    (let ((field-no 0)
          (format-string "")
          (header-string ""))
      (lyskom-traverse el spec
        (if (stringp el)
            (setq format-string (concat format-string el)
                  header-string (concat header-string (lyskom-format el)))
          (let* ((field-spec (cdr (assq el lyskom-text-summary-fields)))
                 (prompt (plist-get field-spec :prompt))
                 (width (car field-width-list)))

            (unless width
              (let* ((dw (plist-get field-spec :weight))
                     (frac (floor (* dynamic-width (/ dw dynamic-weight)))))
                (setq width (max (if prompt
                                     (lyskom-string-width
                                      (lyskom-get-string prompt))
                                   0)
                                 frac))
                (setq dynamic-width (- dynamic-width width))
                (setq dynamic-weight (- dynamic-weight dw))))

            (setq field-no (1+ field-no)
                  format-string
                  (concat format-string
                          (format "%%%s%s%d#%d%s"
                                  (if (null (car field-width-list)) "=" "")
                                  (if (eq (plist-get field-spec :align) 'left)
                                      "-" "")
                                  width 
                                  field-no
                                  (plist-get field-spec :format)))
                  header-string 
                  (concat header-string
                          (lyskom-format
                           (format "%%%s%d#1s"
                                   (if (eq (plist-get field-spec :align) 'left)
                                       "-" "")
                                   width)
                           (if prompt (lyskom-get-string prompt) "")))
                  field-width-list (cdr field-width-list)
                  ))))
      (cons header-string format-string))))



(defun lyskom-list-text-summary-print (spec &rest args)
  (setq lyskom-list-text-summary-params args)

  (if (or (null (lyskom-list-text-summary-get :text-stat))
          (null (lyskom-list-text-summary-get :text)))
      (lyskom-format-insert 'could-not-read 
                            (lyskom-list-text-summary-get :text-no))

    (catch 'lyskom-list-text-summary-abort
      (apply 'lyskom-format-insert
             lyskom-list-text-summary-format
             (delq 'CONSTANT-STRING
                   (mapcar (lambda (el)
                             (if (stringp el) 'CONSTANT-STRING
                               (let* ((field-spec (cdr (assq el lyskom-text-summary-fields)))
                                      (output (plist-get field-spec :output)))
                                 (funcall output (lyskom-list-text-summary-get :text-stat)))))
                           spec)))
      (lyskom-insert "\n"))
    ))


(defun lyskom-list-text-summary (text-list spec &rest args)
  "List a summary of texts in TEXT-LIST according to SPEC.
TEXT-LIST is a list of text numbers or marks. SPEC is a list
of symbols and constant strings. The symbols must be keys in
lyskom-text-summary-fields.

Args is a list of keywords, some of which may have arguments

    :unique           Only print the first text with a specific subject
    :comment-order    Print texts in comment order, with indented subjects
    :filter FN        Filter function (see below)
    :filter-args ARGS Filter arguments (see below).

The filter function is applied to each element of text-list. It is passed
the text-list element as its first argument, and elements of :filter-args
as its remaining arguments. The text will be listed only if the filter
function returns non-nil."

  (setq spec (or spec 
                 '(text-no " " written " " lines " " 
                           comments " " author " " subject)))

  (let* ((text-no nil)
         (indent 0)
         (comment-order nil)
         (filter-function nil)
         (filter-args nil)
         (lyskom-list-text-summary-tmp (lyskom-summary-line-format-string spec))
         (lyskom-list-text-summary-params nil)
         (lyskom-list-text-summary-params-persistent nil)
         (lyskom-list-text-summary-format (cdr lyskom-list-text-summary-tmp)))

    (lyskom-list-text-summary-put :subject-mode nil t)

    (while args
      (cond ((eq (car args) :unique)
             (lyskom-list-text-summary-put :subject-mode 'unique t))
            ((eq (car args) :comment-order)  
             (setq comment-order t)
             (lyskom-list-text-summary-put :subject-mode 'thread t))
            ((eq (car args) :filter)
             (setq filter-function (car (cdr args))
                   args (cdr args)))
            ((eq (car args) :filter-args)
             (setq filter-args (car (cdr args))
                   args (cdr args))))
      (setq args (cdr args)))

    (lyskom-insert (car lyskom-list-text-summary-tmp))
    (lyskom-insert "\n")

    (lyskom-list-text-summary-put :subject-indent 0)

    (while text-list
      (setq text-no (car text-list) text-list (cdr text-list))

      (when (or (null filter-function)
                (apply filter-function text-no filter-args))

        (cond 
         ((eq text-no 'in) (setq indent (1+ indent)))
         ((eq text-no 'out) (setq indent (1- indent)))
         (t (let (mark)
              (cond ((lyskom-mark-p text-no) 
                     (setq mark text-no text-no (mark->text-no text-no)))
                    ((numberp text-no)))

              (blocking-do-multiple ((text-stat (get-text-stat text-no))
                                     (text (get-text text-no)))
              (lyskom-list-text-summary-print spec
                                              :text-no text-no
                                              :text-stat text-stat
                                              :text text
                                              :mark mark
                                              :subject-indent indent)
              (sit-for 0)

              (when comment-order
                (let ((comments (lyskom-text-comments text-stat)))
                  (setq text-list (cons 'out text-list))
                  (lyskom-traverse comment-no (nreverse comments)
                    (when (memq comment-no text-list)
                      (setq text-list (cons comment-no 
                                            (delq comment-no text-list)))))
                  (setq text-list (cons 'in text-list))))))
            ))))))




;;; ================================================================
;;; Computing recipients for comments


(defun lyskom-compute-recipients-commented-authors (commented-texts recipients)
  "Compute the commented-authors recipients for a text that comments
COMMENTED-TEXTS and has recipients RECIPIENTS."
  (when (lyskom-default-value 'kom-check-commented-author-membership)
    (lyskom-message "%s" (lyskom-get-string 'checking-rcpt))

    (let ((raw-author-list (make-collector))
	  (author-list nil)
	  (result nil))
    ;;
    ;; Collect conf-stats of all authors to check. Since there
    ;; could be several, do it in a non-blocking way.
    ;;

    (lyskom-traverse text-no commented-texts
      (initiate-get-text-stat 
       'background
       (lambda (text-stat collector)
	 (when text-stat
	   (initiate-get-conf-stat 'background
				   'collector-push
				   (text-stat->author text-stat)
				   collector)))
       text-no
       raw-author-list))
    (lyskom-wait-queue 'background)
    (setq raw-author-list (lyskom-delete-duplicates
			   (collector->value raw-author-list)
			   'conf-stat->conf-no))

    ;;
    ;; Filter the list. Remove all authors that are direct recipients
    ;; or whose send-comments-to is a direct recipient or who are
    ;; listed in kom-dont-check-commented-authors
    ;;

    (lyskom-traverse author raw-author-list
      (let ((send-comments-to (lyskom-get-send-comments-to author t)))
	(cond

	 ;; Author is in kom-dont-check-commented-authors

	 ((memq (conf-stat->conf-no author) 
		kom-dont-check-commented-authors))

	 ;; Author is direct recipient to text

	 ((memq (conf-stat->conf-no author) recipients))

	 ;; Author has a zero send-comments-to

	 ((and send-comments-to (zerop (car send-comments-to))))

	 ;; We don't have permission to send stuff to the author's
	 ;; send-comments-to or to the author if there is no
	 ;; send-comments-to

	 ((or (and send-comments-to
		   (not (lyskom-is-permitted-author
			 (blocking-do 'get-conf-stat (car send-comments-to)))))
	      (and (not send-comments-to)
		   (not (lyskom-is-permitted-author author)))))

	 ;; We can't filter the author straight off, so we add it
	 ;; to the list that we want to check completely.

	 (t (setq author-list (cons author author-list))))))

    ;;
    ;; Now author-list contains a list of authors that we need to
    ;; check memberships of. Traverse all authors and get their
    ;; memberships in all recipients.

    (lyskom-traverse author author-list
      (let ((memberships (make-collector)))
	(lyskom-traverse recipient recipients
	  (initiate-query-read-texts 'background
				     (lambda (x y)
				       (collector-push x y))
				     (conf-stat->conf-no author)
				     recipient
				     nil
				     0
				     memberships))
	(lyskom-wait-queue 'background)
	(setq memberships (collector->value memberships))

	;;
	;; Now memberships contains the memberships of author
	;; in all recipients of the text. Traverse all memberships
	;; in the collector and see if there is one that is not
	;; an active membership. If there is not, then we need to
	;; ask the user whether they want to add the author.
	;;

	(unless (lyskom-traverse membership memberships
		  (when (and membership
			     (not (membership-type->passive
				   (membership->type membership))))
		    (lyskom-traverse-break t)))
	  (setq result (cons author result)))))
    result)))


;;; ================================================================
;;; MULE utilities

;;; (defun lyskom-safe-encode-string (string coding-system)
;;;   "Encode STRING using CODING-SYSTEM.
;;; Return the encoded string if the encoding could be safely performed, 
;;; or nil if it seems there are replacement characters in the encoded
;;; string"
;;;   (let ((res (encode-coding-string string coding-system)))
;;;     (and (lyskom-encoding-is-safe string res) res)))
;;; 
;;; (defun lyskom-encoding-is-safe-xemacs (from to)
;;;   (let ((len1 (length from))
;;; 	(len2 (length to))
;;; 	(i 0)
;;; 	(res t))
;;;     (and (= len1 len2)
;;; 	 (progn (while (< i len1)
;;; 		  (when (and (eq (aref to i) ?~)
;;; 			     (not (eq (aref from i) ?~)))
;;; 		    (setq i len1 res nil))
;;; 		  (setq i (1+ i)))
;;; 		res))))
;;; 
;;; (defun lyskom-encoding-is-safe-gnu (from to)
;;;   (let ((len1 (length from))
;;; 	(len2 (length to))
;;; 	(i 0)
;;; 	(res t))
;;;     (and (= len1 len2)
;;; 	 (progn (while (< i len1)
;;; 		  (when (and (eq (aref to i) ??)
;;; 			     (not (eq (aref from i) ??)))
;;; 		    (setq i len1 res nil))
;;; 		  (setq i (1+ i)))
;;; 		res))))


;;; ================================================================
;;; Other utilities
;;;
;;; An llist is a list with a limited size, used for a fast FIFO 
;;; cache. One use is to keep track of the N most recently read
;;; texts.
;;; 

;;; (defun lyskom-llist-create (size) 
;;;   (let ((list (make-list size nil)))
;;;     (cons list list)))
;;; 
;;; (defsubst lyskom-llist->head (llist) (car llist))
;;; (defsubst set-lyskom-llist->head (llist c) (setcar llist c))
;;; (defsubst lyskom-llist->tail (llist) (cdr llist))
;;; (defsubst set-lyskom-llist->tail (llist c) (setcdr llist c))
;;; 
;;; 
;;; (defun lyskom-llist-add (llist el)
;;;   (cond ((null (cdr (lyskom-llist->tail llist)))
;;; 	 (setcar (lyskom-llist->tail llist) el)
;;; 	 (setcdr (lyskom-llist->tail llist) (lyskom-llist->head llist))
;;; 	 (set-lyskom-llist->tail llist (lyskom-llist->head llist))
;;; 	 (set-lyskom-llist->head llist (cdr (lyskom-llist->head llist)))
;;; 	 (setcdr (lyskom-llist->tail llist) nil))
;;; 	(t (setcar (lyskom-llist->tail llist) el)
;;; 	   (set-lyskom-llist->tail llist (cdr (lyskom-llist->tail llist)))))
;;;   (lyskom-llist->head llist))
;;; 
;;; (defun lyskom-llist-remove (el llist)
;;;   (let ((cur (lyskom-llist->head llist)))
;;;     (while cur
;;;       (when (eq el (car cur))
;;; 	(setcar cur nil))
;;;       (setq cur (cdr cur)))))
;;; 
;;; (defun lyskom-llist-memq (el llist)
;;;   (memq el (lyskom-llist->list head)))
;;;     result)))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: completing-read.el
;;;; Author: David Byers
;;;;
;;;; This file implements functions for reading a conference name
;;;; or a person name with completion and other help.
;;;;

(defvar lyskom-name-hist nil)


;;; ============================================================
;;;
;;; Name lookup caches
;;;

(defvar lyskom-completing-who-info-cache nil
  "Temporary cache of who-info data")

(defvar lyskom-completing-lookup-name-cache nil
  "Temporary cache of server queries")

(defvar lyskom-completing-use-dynamic-info nil)

(defun lyskom-completing-clear-cache ()
  (setq lyskom-completing-who-info-cache nil)
  (setq lyskom-completing-lookup-name-cache nil))

(defun lyskom-completing-who-is-on ()
  "Get information about who is on, first checking the cache. Returns what 
\(blocking-do 'who-is-on\) would, but as a list, not a vector"
  (if lyskom-completing-who-info-cache
      lyskom-completing-who-info-cache
    (setq lyskom-completing-who-info-cache
          (listify-vector
	   (if (lyskom-have-feature dynamic-session-info)
	       (blocking-do 'who-is-on-dynamic t t 0)
	     (blocking-do 'who-is-on))))))

(defun lyskom-completing-cache-completion (string data)
  (let* ((downs (lyskom-unicase string))
         (tmp (assoc downs lyskom-completing-lookup-name-cache)))
    (if (null tmp)
        (setq lyskom-completing-lookup-name-cache
              (cons (cons downs data) lyskom-completing-lookup-name-cache)))
    string))

(defun lyskom-completing-lookup-z-name (string want-conf want-pers)
  "Look up STRING as a name. Same as \(blocking-do 'lookup-z-name ...\)
but first checks a cache."
  (if (and (eq 0 want-conf)
           (eq 0 want-pers))
      nil
    (let* ((downs (lyskom-unicase string))
           (tmp (assoc downs lyskom-completing-lookup-name-cache)))
      (if tmp
          (cdr tmp)
        (progn
          (setq tmp (blocking-do 'lookup-z-name string want-pers want-conf))
          (setq lyskom-completing-lookup-name-cache
                (cons (cons downs tmp)
                      lyskom-completing-lookup-name-cache))
          tmp)))))

;;; ============================================================
;;;
;;; Keymaps
;;;


(defvar lyskom-minibuffer-local-completion-map
  (let ((map (copy-keymap minibuffer-local-completion-map)))
    (define-key map " " nil)
    map)
  "Keymap used for reading LysKOM names.")

(defvar lyskom-minibuffer-local-must-match-map
  (let ((map (copy-keymap minibuffer-local-must-match-map)))
    (lyskom-xemacs-or-gnu 
     (lyskom-set-keymap-parent map lyskom-minibuffer-local-completion-map)
     (define-key map " " nil))
    map)
  "Keymap used for reading LysKOM names.")



(defsubst lyskom-completing-match-string-regexp (string)
  (concat "^"
          (replace-in-string (regexp-quote (lyskom-unicase (lyskom-completing-strip-name string)))
                             "\\s-+" "\\\\S-*\\\\s-+")
          "\\s-*"))

(defsubst lyskom-completing-match-string (string name)
  "Return non-nil if STRING matches NAME using LysKOM completion rules."
  (string-match (lyskom-completing-match-string-regexp string)
                (lyskom-completing-strip-name (lyskom-unicase name))))


(defun lyskom-read-conf-no (prompt type &optional empty initial mustmatch)
  "Read a conference name from the minibuffer with completion and
return its number or zero if nothing was matched.

See lyskom-read-conf for a description of the parameters."
  (let ((conf-z-info (lyskom-read-conf prompt type empty initial mustmatch)))
    (cond ((null conf-z-info) 0)
          ((stringp conf-z-info) 0)
	  ((lyskom-conf-stat-p conf-z-info) (conf-stat->conf-no conf-z-info))
	  ((lyskom-uconf-stat-p conf-z-info) (uconf-stat->conf-no conf-z-info))
          (t (conf-z-info->conf-no conf-z-info)))))

(defun lyskom-read-conf-stat (prompt type &optional empty initial mustmatch)
  "Read a conference name from the minibuffer with completion and
return its conf-stat or nil if nothing was matched.

See lyskom-read-conf for a description of the parameters."
  (let ((conf-z-info (lyskom-read-conf prompt type empty initial mustmatch)))
    (cond ((null conf-z-info) nil)
          ((stringp conf-z-info) nil)
	  ((lyskom-conf-stat-p conf-z-info) conf-z-info)
          ((lyskom-uconf-stat-p conf-z-info) 
           (blocking-do 'get-conf-stat (uconf-stat->conf-no conf-z-info)))
          (t (blocking-do 'get-conf-stat 
                          (conf-z-info->conf-no conf-z-info))))))

(defun lyskom-read-uconf-stat (prompt type &optional empty initial mustmatch)
  "Read a conference name from the minibuffer with completion and
return its conf-stat or nil if nothing was matched.

See lyskom-read-conf for a description of the parameters."
  (let ((conf-z-info (lyskom-read-conf prompt type empty initial mustmatch)))
    (cond ((null conf-z-info) nil)
          ((stringp conf-z-info) nil)
	  ((lyskom-uconf-stat-p conf-z-info) conf-z-info)
	  ((lyskom-conf-stat-p conf-z-info)
           (blocking-do 'get-uconf-stat 
                        (conf-stat->conf-no conf-z-info)))
          (t (blocking-do 'get-uconf-stat 
                          (conf-z-info->conf-no conf-z-info))))))

(defun lyskom-read-conf-name (prompt type &optional empty initial mustmatch)
  "Read a conference name from the minibuffer with completion and
return its name.

See lyskom-read-conf for a description of the parameters."
  (let ((conf-z-info (lyskom-read-conf prompt type empty initial mustmatch)))
    (cond ((null conf-z-info) "")
          ((stringp conf-z-info) conf-z-info)
	  ((lyskom-conf-stat-p conf-z-info) (conf-stat->name conf-z-info))
	  ((lyskom-uconf-stat-p conf-z-info) (uconf-stat->name conf-z-info))
	  (t (conf-z-info->name conf-z-info)))))


;;; ================================================================
;;; Code to guess defaults for initial input
;;;

(defun lyskom-default-conference-at-point (&rest args)
  (let* ((pos (or lyskom-command-point (point)))
         (type (and pos (get-text-property pos 'lyskom-button-type))))
    (and (memq type '(conf pers))
         (list (get-text-property pos 'lyskom-button-arg)))))

(defun lyskom-default-conference-current (&rest args)
  (list lyskom-current-conf))

(defun lyskom-default-conference-self (&rest args)
  (list lyskom-pers-no))

(defun lyskom-default-conference-last-author (&rest args)
  (and (lyskom-get-last-read-text)
       (list (text-stat->author
              (blocking-do 'get-text-stat (lyskom-get-last-read-text))))))

(defun lyskom-default-conference-author-of-text-at-point (&rest args)
  (let ((text (lyskom-text-at-point)))
    (and text 
	 (list (text-stat->author (blocking-do 'get-text-stat text))))))

(defun lyskom-default-conference-restriction (predicate &rest args)
  (and (assq 'restrict predicate)
       (cdr (assq 'restrict predicate))))

(defun lyskom-default-conference-empty (&rest args)
  nil)


(defun lyskom-default-conference-saved (sym &rest args)
  (save-excursion
    (when lyskom-buffer (set-buffer lyskom-buffer))
    (and (cdr (assq (car sym) lyskom-read-conf-saved-inputs))
         (list (conf-z-info->conf-no 
                (cdr (assq (car sym) lyskom-read-conf-saved-inputs)))))))

(defun lyskom-default-conference-not-self (uc &rest args)
  (not (eq (uconf-stat->conf-no uc) lyskom-pers-no)))

(defun lyskom-default-conference-not-current (uc &rest args)
  (not (eq (uconf-stat->conf-no uc) lyskom-current-conf)))



(defun lyskom-get-initial-conf-strategy (prompt)
  (when (listp prompt) (setq prompt (car prompt)))
  (let* ((spec-1 (cdr (assq (or lyskom-current-command this-command)
                            lyskom-default-conference-strategy)))
         (default-spec 
           (cdr (assq t (assq t lyskom-default-conference-strategy))))
         (prompt-spec (cdr (assq prompt spec-1)))
         (cmd-spec (cdr (assq t spec-1))))
    (lyskom-debug-forms
     (unless spec-1 
       (save-excursion
         (when lyskom-buffer (set-buffer lyskom-buffer))
         (lyskom-format-insert-before-prompt 
          "%[%#2@%#1s%]\n"
          (format "Warning: no strategy for %S/%S"
                  (or lyskom-current-command this-command)
                  prompt)
          `(face ,kom-warning-face)))))
    (list (or (assq 'default prompt-spec)
              (assq 'default cmd-spec)
              (assq 'default default-spec))
          (or (assq 'filter prompt-spec)
              (assq 'filter cmd-spec)
              (assq 'filter default-spec))
          (or (assq 'save prompt-spec)
              (assq 'save cmd-spec)
              (assq 'save default-spec)))))

(defun lyskom-read-conf-guess-initial (prompt predicate)
  "Return a guess for the initial value for lyskom-read-conf."
  (let* ((strategy (lyskom-get-initial-conf-strategy prompt))
         (default (cdr (assq 'default strategy)))
         (filter (cdr (assq 'filter strategy))))

    (uconf-stat->name
     (car
      (lyskom-filter-list 
       (lambda (uconf-stat)
         (and uconf-stat
              (not (memq nil
                         (mapcar (lambda (fn)
                                   (funcall fn uconf-stat))
                                 filter)))
              (lyskom-read-conf-internal-verify-type
               (uconf-stat->conf-no uconf-stat)
               (uconf-stat->conf-type uconf-stat)
               predicate nil nil)))
       (mapcar (lambda (conf-no)
                 (blocking-do 'get-uconf-stat conf-no)) 
               (apply 'append 
                      (delq nil (mapcar (lambda (fn)
                                          (if (listp fn)
                                              (funcall (car fn) (cdr fn) predicate)
                                            (funcall fn predicate)))
                                        default)))))))))



(defun lyskom-read-conf-save-input (prompt input)
  "Save INPUT as input for the current completing read command."
  (save-excursion
    (when lyskom-buffer (set-buffer lyskom-buffer))
    (lyskom-traverse sym (cdr (assq 'save (lyskom-get-initial-conf-strategy prompt)))
      (if (assq sym lyskom-read-conf-saved-inputs)
          (setcdr (assq sym lyskom-read-conf-saved-inputs) input)
        (setq lyskom-read-conf-saved-inputs 
              (cons (cons sym input) lyskom-read-conf-saved-inputs))
        ))))

(defun lyskom-read-conf (prompt type &optional empty initial mustmatch)
  "Completing read a conference or person from the minibuffer. 

PROMPT is the prompt. It can be a string, symbol or list. If it is a symbol,
       lyskom-get-string will be used to get the string. If it is a list,
       it is used as the argument list to lyskom-format to create the
       prompt. You should use symbols and lists only.
TYPE   is the type of conferences to return. It is a list of one or
       more of the following:

       all     Return any conference.
       conf    Return conferences (not letterboxes).
       pers    Return persons (letterboxes).
       login   Return persons who are also logged-in.
       membership Return only conferences and letterboxes lyskom-pers-no
               is a member of.
       none    Return names that do not match anything in the database.
       (restrict c1 c2 ...) Restrict matching to conference numbers c1, 
               c2 etc. The implementation is inefficient for long lists.

Optional arguments

EMPTY     allow nothing to be entered.

INITIAL   initial contents of the minibuffer. If an integer, use the
          name of that conference. This is normally computed automatically,
          so pass nil whenever possible.

MUSTMATCH if non-nil, the user must enter a valid name.

The return value may be one of
A conf-z-info: The conf-z-info associated with the name entered,
nil:         Nothing was entered, or
A string:    A name that matched nothing in the database."

  ;; We bind lyskom-current-command here so that changes to
  ;; this-command will not confuse our guesses for the initial value
  ;; (particularly at the end of this function, where we sometimes
  ;; save the value the user entered).

  (let ((lyskom-current-command (or lyskom-current-command this-command)))
    (lyskom-completing-clear-cache)
    (setq initial
          (cond ((integerp initial)
                 (uconf-stat->name (blocking-do 'get-uconf-stat initial)))
                ((stringp initial) initial)
                ((lyskom-conf-stat-p initial)
                 (conf-stat->name initial))
                ((lyskom-uconf-stat-p initial)
                 (uconf-stat->name initial))
                ((lyskom-conf-z-info-p initial)
                 (conf-z-info->name initial))
                ((consp initial) initial)
                ((lyskom-read-conf-guess-initial prompt type))
                (t nil)))

    (let* ((completion-ignore-case t)
           (minibuffer-local-completion-map 
            lyskom-minibuffer-local-completion-map)
           (minibuffer-local-must-match-map 
            lyskom-minibuffer-local-must-match-map)
           (read-string nil)
           (old-prompt prompt)
           (result nil)
           (keep-going t))

      (setq prompt (cond ((stringp prompt) prompt)
                         ((symbolp prompt) (lyskom-get-string prompt))
                         ((listp prompt) (apply 'lyskom-format prompt))
                         (t (lyskom-get-string 'conf-prompt))))

      (while keep-going
        (setq read-string (lyskom-completing-read prompt
                                                  'lyskom-read-conf-internal
                                                  type
                                                  mustmatch
                                                  (if (listp initial)
                                                      initial
                                                    (cons initial 0))
                                                  'lyskom-name-hist))
        (setq result
              (cond ((null read-string) nil)
                    ((string= "" read-string) nil)
                    (t (lyskom-lookup-conf-by-name read-string type))))
        (setq keep-going (and (not empty)
                              (null result))))
      (lyskom-read-conf-save-input old-prompt result)
      result)))


(defun lyskom-read-conf-get-logins ()
  "Used internally by lyskom-read-conf-internal to get a list of
persons who are logged on."
  (mapcar (if (lyskom-have-feature dynamic-session-info)
              (function (lambda (el) (dynamic-session-info->person el)))
            (function (lambda (el) (who-info->pers-no el))))
          (lyskom-completing-who-is-on)))


(defun lyskom-read-conf-expand-specials (string
                                         predicate
                                         login-list
                                         x-list
                                         &optional return-cs)
  "Used internally by lyskom-read-conf-internal to expand person and
conference number specifications to something useful."
  (cond ((string-match (lyskom-get-string 'person-or-conf-no-regexp) string)
         (let* ((no (lyskom-string-to-number (match-string 1 string)))
                (cs (blocking-do 'get-uconf-stat no)))
           (if (and cs
                    (lyskom-read-conf-internal-verify-type
                     (uconf-stat->conf-no cs)
                     (uconf-stat->conf-type cs)
                     predicate 
                     login-list
                     x-list))
               (if return-cs
                   cs
                 (list string)))))
        ((string-match (lyskom-get-string 'session-no-regexp) string)
         (let* ((no (lyskom-string-to-number (match-string 1 string)))
                (si (blocking-do 'get-session-info no))
                (cs (and si
                         (blocking-do 'get-uconf-stat
                                      (session-info->pers-no si)))))
           (if (and cs
                    (lyskom-read-conf-internal-verify-type
                     (uconf-stat->conf-no cs)
                     (uconf-stat->conf-type cs)
                     predicate 
                     login-list
                     x-list))
               (if return-cs
                   cs
                 (list string)))))))

(defun lyskom-read-conf-lookup-specials (string predicate login-list x-list)
  "Used internally by lyskom-read-conf-internal to look up conf-stats
from person and conference number specifications."
  (let ((cs (lyskom-read-conf-expand-specials string
                                              predicate
                                              login-list
                                              x-list
                                              t)))
    (lyskom-create-conf-z-info (uconf-stat->name cs)
                               (uconf-stat->conf-type cs)
                               (uconf-stat->conf-no cs))))

(defun lyskom-lookup-conf-by-name (string predicate)
  "Return the conf-z-info associated with STRING that also satisfies
PREDICATE or nil if no name matches. See lyskom-read-conf-internal for
a documentation of PREDICATE."
  (if (string= string "")
      nil
    (lyskom-read-conf-internal string predicate 'lyskom-lookup)))


(defun lyskom-read-conf-internal (string predicate all)
  "Complete the name STRING according to PREDICATE and ALL.

STRING is a string to complete.
PREDICATE is a list of name types to return. See lyskom-read-conf for
details.
ALL is set by try-completion and all-completions. See the Emacs lisp
manual for a description. Special value 'lyskom-lookup makes the
function work as a name-to-conf-stat translator."

  ;;
  ;;  Catch some degenerate cases that can cause...problems. This
  ;;  won't solve all the...problems, but should speed things up a
  ;;  little bit.
  ;;

  (cond 
   ((and (null all)
         (string-match "^\\s-*$" string)) "")
   ((and (eq all 'lyskom-lookup)
         (string-match "^\\s-*$" string)) nil)
   ((and (eq all 'lambda)
         (string-match "^\\s-*$" string)) nil)
   (t

    (let* ((login-list (and (memq 'login predicate)
                            (lyskom-read-conf-get-logins)))
           (x-list (lyskom-completing-lookup-z-name string 
                                                    (if (or (memq 'all predicate)
                                                            (memq 'membership predicate)
                                                            (memq 'conf predicate)
                                                            (memq 'none predicate)) 1 0)
                                                    (if (or (memq 'all predicate)
                                                            (memq 'membership predicate)
                                                            (memq 'pers predicate)
                                                            (memq 'none predicate)
                                                            (memq 'login predicate)) 1 0)))
           (r-list (when (assq 'restrict predicate)
                     (let ((result (make-collector)))
                       (lyskom-traverse conf-no (cdr (assq 'restrict predicate))
                         (initiate-get-uconf-stat 'main 'collector-push 
                                                  conf-no result))
                       (lyskom-wait-queue 'main)
                       (delq nil
                             (mapcar (lambda (conf-stat)
                                       (when (lyskom-completing-match-string string (uconf-stat->name conf-stat))
                                         (lyskom-create-conf-z-info
                                          (uconf-stat->name conf-stat)
                                          (uconf-stat->conf-type conf-stat)
                                          (uconf-stat->conf-no conf-stat))))
                               (collector->value result))))))
           (candidate-list 
            (append r-list
                   (if x-list
                       (conf-z-info-list->conf-z-infos x-list))))
           (result-list nil))

      ;;
      ;;  login-list now contains a list of logins, IF the predicate
      ;;  includes 'login
      ;;
      ;;  candidate-list contains a list of conf-z-infos
      ;;
      ;;  Now set result-list to the conf-z-infos that fulfill the
      ;;  predicate, fetching the conf-stats asynchronously.
      ;;

      (lyskom-traverse el candidate-list
        (if (lyskom-read-conf-internal-verify-type (conf-z-info->conf-no el)
                                                   (conf-z-info->conf-type el)
                                                   predicate
                                                   login-list
                                                   candidate-list)
            (setq result-list (cons el result-list))))
      

      ;;
      ;;  Now the matching conf-z-infos are in result-list
      ;;

      (cond 
       ((eq all 'lyskom-lookup)
        (let ((names (mapcar 'conf-z-info->name 
                             result-list))
              (specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
                                                          candidate-list)))

          (cond ((and kom-complete-numbers-before-names specials)
                 (lyskom-read-conf-lookup-specials string
                                                   predicate
                                                   login-list
                                                   candidate-list))
                ((= (length result-list) 1)
                 (car result-list))

                ((and (> (length result-list) 1)
                      (lyskom-completing-member string names))
                 (elt result-list
                      (- (length result-list)
                         (length (lyskom-completing-member string names)))))

                (specials (lyskom-read-conf-lookup-specials string
                                                            predicate
                                                            login-list
                                                            candidate-list))
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
                ((lyskom-read-conf-internal-verify-type nil
                                                        nil
                                                        predicate
                                                        login-list
                                                        candidate-list)
                 string))))
     
       ;;
       ;;  Check for exact match. We have an exact match in the server
       ;;  when there was a single match OR when there was no match, and
       ;;  no match is valid according to predicate
       ;;

       ((eq all 'lambda)
        (let ((specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
                                                          candidate-list)))
          (cond ((= (length result-list) 1) t)
                ((and (> (length result-list) 1)
                      (let ((names (mapcar 'conf-z-info->name
                                           result-list)))
                        (and (lyskom-completing-member string names)
                             t))))
                (result-list nil)
                ((= (length specials) 1) t)
                (specials nil)
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)

                (t (lyskom-read-conf-internal-verify-type nil
                                                          nil
                                                          predicate
                                                          login-list
                                                          candidate-list)))))


       ;;
       ;;  Called from all-completions. Return a list of all possible
       ;;  completions, in this case all names in the result list plus,
       ;;  if the input string is a person or conf number specification,
       ;;  the input string, PROVIDED, the requested conference matches
       ;;  the predicate. If there were no matches, return the input
       ;;  string if no matches satisfies the predicate.
       ;;
          
       (all
        (let ((names (mapcar 'conf-z-info->name result-list))
              (specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
                                                          candidate-list)))
          (cond (specials (append specials names))
                (names names)
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
                ((lyskom-read-conf-internal-verify-type nil
                                                        nil
                                                        predicate
                                                        login-list
                                                        candidate-list)
                 (list string))
                (t nil))))

       ;;
       ;;  Called from try-completion, and there were no matches. Try to
       ;;  expand the input string as a person or conf number
       ;;  specification or return something sensible if the predicate
       ;;  is satisfied by no matches.
       ;;

       ((null result-list)
        (let ((specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
                                                          candidate-list)))
          (cond (specials (car specials))
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
                ((lyskom-read-conf-internal-verify-type nil
                                                        nil
                                                        predicate
                                                        login-list
                                                        candidate-list)
                 t)
                (t nil))))

       ;;
       ;;  Called from try-completion, and there were matches in the
       ;;  server. Return t if the string is an exact match to any
       ;;  string returned from the server. Otherwise, expand the string
       ;;  as far as possible and return that
       ;;

       (t
        (let ((name-list (mapcar 'conf-z-info->name result-list))
              (specials (lyskom-read-conf-expand-specials string
                                                          predicate
                                                          login-list
                                                          candidate-list)))
          (if specials (setq name-list (nconc specials name-list)))

          (cond ((= (length name-list) 1) (if (string= (car name-list) string) t (car name-list)))
                ((string-match (lyskom-get-string 'person-or-conf-no-regexp)
                               string) nil)
                ((string-match (lyskom-get-string 'session-no-regexp)
                               string) nil)
                (t (or (lyskom-completing-cache-completion
                        (lyskom-complete-string name-list)
                        (if r-list
                            (lyskom-create-conf-z-info-list
                             (apply 'vector candidate-list))
                          x-list))
                       (and (lyskom-read-conf-internal-verify-type 
                             nil
                             nil
                             predicate
                             login-list
                             candidate-list)
                            (list string))))))))))))
        

(defun lyskom-completing-member (string list)
  (let ((string (lyskom-unicase (lyskom-completing-strip-name string)))
        (result nil))
    (while (and list (not result))
      (if (lyskom-string= string (lyskom-unicase 
                           (lyskom-completing-strip-name (car list))))
          (setq result list)
        (setq list (cdr list))))
    result))


(defun lyskom-completing-strip-name (string)
  "Strip parens and crap from a name."
  (while (string-match "([^()]*)" string)
    (setq string (replace-match " " t t string)))
  (while (string-match "\\s-\\s-+" string)
    (setq string (replace-match " " t t string)))
  (while (string-match "([^()]*$" string)
    (setq string (substring string 0 (match-beginning 0))))
  (if (string-match "^\\s-*\\(.*\\S-\\)\\s-*$" string)
      (match-string 1 string)
    string))


(defun lyskom-read-conf-internal-verify-type (conf-no
                                              conf-type
                                              predicate
                                              logins
                                              x-list)
  (or (memq conf-no (cdr (assq 'restrict predicate)))
      (and (memq 'all predicate)
           conf-no)
      (and (memq 'conf predicate)
           conf-type
           (not (conf-type->letterbox conf-type)))
      (and (memq 'pers predicate) 
           conf-type
           (conf-type->letterbox conf-type))
      (and (memq 'login predicate)
           conf-no
           (memq conf-no logins))
      (and (memq 'membership predicate)
           conf-no
           (lyskom-get-membership conf-no t))
      (and (memq 'none predicate) 
           (and (null conf-no)
                (null x-list)))))

;;; FOR DEBUGGING (DON'T DELETE)     /byers
;;;
;;; (defun lyskom-complete-show-data-list (state data)
;;;   (save-excursion
;;;     (pop-to-buffer (get-buffer-create "*kom*-complete"))
;;;     (erase-buffer)
;;;     (lyskom-set-buffer-multibyte nil)
;;;    (while data
;;;       (insert
;;;        (format "%s\n" (substring (aref (car data) 2)
;;;                                  (aref (car data) 0)
;;;                                  (aref (car data) 1))))
;;;       (setq data (cdr data)))
;;;     (insert (format "%S %S: %S" (symbol-value current-state)
;;;                     (elt state 0)
;;;                     (elt state 1)))
;;;     (sit-for 5)))


(defun lyskom-complete-string (string-list)
  "Find the longest common prefix of all strings in STRING-LIST according to
the LysKOM rules of string matching."
  (let ((main-state 'start-of-string)
        (tmp-state nil)
        (current-state 'main-state)
        (main-accumulator nil)
        (tmp-accumulator nil)
        (current-accumulator 'main-accumulator)
        (done nil)
        (paren-depth 0)
        (have-here nil)
        (last-event-worth-noting nil)
        (data-list (lyskom-complete-string-munge-input string-list))
        (next-char-state (vector nil nil)))

    (while (not done)
      (lyskom-complete-string-next-char next-char-state data-list)
;      (lyskom-complete-show-data-list next-char-state data-list)
      (cond

       ;;
       ;; Case one, a match of two non-special characters.
       ;; Accumulate one character and advance the lists
       ;;

       ((eq (aref next-char-state 0) 'match)
        (if (eq (aref next-char-state 1) ?\ )
            (progn
              (cond ((memq (symbol-value current-state)
			     '(start-of-word start-of-string))
                     nil)
                    ((eq last-event-worth-noting 'mismatch)
                     (lyskom-complete-string-accumulate current-accumulator
                                                        'SPC))
                    (t
                     (lyskom-complete-string-accumulate current-accumulator
                                                        ?\ )))
              (set current-state 'start-of-word)
              (lyskom-complete-string-advance data-list))
          (progn
            (set current-state 'in-a-word)
            (lyskom-complete-string-accumulate current-accumulator
                                               (aref next-char-state 1))
            (lyskom-complete-string-advance data-list)))
        (setq last-event-worth-noting 'match))
       
       ;;
       ;; Case two, a match of two open-paren expressions Increase
       ;; paren depth and accumulate a character. First set
       ;; current-accumulator to the temporary if paren-depth is zero
       ;; to start with.
       ;;

       ((eq (aref next-char-state 0) 'open-paren-match)
        (setq last-event-worth-noting 'match)
        (if (zerop paren-depth)
            (progn
              (setq current-accumulator 'tmp-accumulator)
              (setq current-state 'tmp-state)
              (setq tmp-state main-state)
              (setq tmp-accumulator nil)))
        (setq paren-depth (1+ paren-depth))
        (lyskom-complete-string-accumulate current-accumulator
                                    (aref next-char-state 1))
        (lyskom-complete-string-advance data-list))

       ;;
       ;; Case three, a match of two close-paren expressions
       ;; Accumulate a character. If paren-depth is postitive,
       ;; decrease it. If it ends up zero, add the temporary
       ;; accumulator to the main accumulator and set the current
       ;; accumulator to the main accumulator.
       ;;

       ((eq (aref next-char-state 0) 'close-paren-match)
        (setq last-event-worth-noting 'match)
        (lyskom-complete-string-accumulate current-accumulator
                                    (aref next-char-state 1))
        (if (> paren-depth 0)
            (progn
              (setq paren-depth (1- paren-depth))
              (if (zerop paren-depth)
                  (progn
                    (setq main-accumulator
                          (nconc tmp-accumulator main-accumulator))
                    (setq main-state tmp-state)
                    (setq current-state 'main-state)
                    (setq current-accumulator 'main-accumulator)))))
        (lyskom-complete-string-advance data-list))

       ;;
       ;; Case two, a mismatch of any kind in a paren expression
       ;;

       ((and (> paren-depth 0)
             (memq (aref next-char-state 0)
		     '(mismatch space-mismatch open-paren-mismatch)))
        (setq last-event-worth-noting 'mismatch)
        (setq tmp-accumulator nil)
        (setq tmp-state nil)
        (setq current-state 'main-state)
        (setq current-accumulator 'main-accumulator)
        (lyskom-complete-string-close-parens data-list paren-depth)
        (setq paren-depth 0))

       ;;
       ;; Case two and a half or so, a space mismatch. This is ignored
       ;; if we're still at the start of the string
       ;;
       
       ((and (eq (aref next-char-state 0) 'space-mismatch)
             (memq (symbol-value current-state)
		     '(start-of-string start-of-word)))
        (setq last-event-worth-noting nil)
        (lyskom-complete-string-skip-whitespace data-list))

       ;;
       ;; Case three, a mismatch of regular characters outside a paren
       ;; Advance to the end of the current word
       ;;

       ((and (memq (aref next-char-state 0) '(mismatch space-mismatch))
             (zerop paren-depth))
        (setq last-event-worth-noting 'mismatch)
        (if (memq (symbol-value current-state)
		    '(start-of-word start-of-string))
            (setq done t)
          (progn
            (if (not have-here)
                (progn
                  (lyskom-complete-string-accumulate current-accumulator 
                                                     'HERE)
                  (setq have-here t)))
            (lyskom-complete-string-advance-to-end-of-word data-list)
            (set current-state 'in-a-word))))

       ;;
       ;; Case four, a mistmatch where one character is an open-paren
       ;;

       ((eq (aref next-char-state 0) 'open-paren-mismatch)
        (setq last-event-worth-noting 'mismatch)
        (lyskom-complete-string-skip-parens data-list))


       ;;
       ;; Case five, eof
       ;;

       ((eq (aref next-char-state 0) 'eof)
        (setq done t))

       ;;
       ;; Case six, can't happen
       ;;

       (t (error "This can't happen: %S" next-char-state))))

    ;;
    ;; Build the result by reversing the result list and making a
    ;; string out of it.
    ;;

    (if (eq (car main-accumulator) 'SPC)
        (setq main-accumulator (cdr main-accumulator)))

    (setq main-accumulator (nreverse main-accumulator))

    (if (memq 'HERE main-accumulator)
        (let ((backup (length (memq 'HERE main-accumulator))))
          (if lyskom-experimental-features
              (setq unread-command-events
                    (append (cons ? (make-list (1- backup) 2))
                            unread-command-events)))
          (setq main-accumulator (delq 'HERE main-accumulator))))
    
    (concat (mapcar (lambda (el) (if (eq el 'SPC) ?\  el))
		    main-accumulator))))


(defun lyskom-complete-string-accumulate (accumulator char)
  (set accumulator (cons char (symbol-value accumulator))))

(defun lyskom-complete-string-munge-input (string-list)
  (mapcar (function
           (lambda (x)
             (vector 0 (length x) x)))
          string-list))

;;;
;;; Advance one regular character or multiple whitespaces
;;;

(defun lyskom-complete-string-advance (data-list)
  (lyskom-traverse 
   el data-list
   (string-match "\\([ \t]+\\|[^ \t]\\|$\\)"
                 (aref el 2)
                 (aref el 0))
   (aset el 0 (match-end 0))))

(defun lyskom-complete-string-skip-whitespace (data-list)
  (lyskom-traverse
   el data-list
   (string-match "[ \t]*" (aref el 2) (aref el 0))
   (aset el 0 (match-end 0))))

;;;
;;; Advance to the end of the current word
;;;

(defun lyskom-complete-string-advance-to-end-of-word (data-list)
  (lyskom-traverse
   el data-list
   (aset el 0 (string-match "\\([ \t]\\|$\\)" 
                            (aref el 2)
                            (aref el 0)))))

;;;
;;; Unwind a number of parens
;;;

(defun lyskom-complete-string-skip-parens (data-list)
  (lyskom-traverse
   el data-list
   (if (eq ?\( (aref (aref el 2) (aref el 0)))
       (progn
         (aset el 0 (1+ (aref el 0)))
         (lyskom-complete-string-close-parens-2 el 1)))))

(defun lyskom-complete-string-close-parens (data-list depth)
  (lyskom-traverse
   el data-list
   (lyskom-complete-string-close-parens-2 el depth)))

(defun lyskom-complete-string-close-parens-2 (el depth)
  (let ((string (aref el 2))
        (pos (aref el 0)))
    (while (> depth 0)
      (cond ((>= pos (length string)) 
             (setq depth 0))
            ((eq (aref string pos) ?\))
             (setq depth (1- depth)))
            ((eq (aref string pos) ?\))
             (setq depth (1+ depth))))
      (setq pos (1+ pos)))
    (aset el 0 pos)))


;;;
;;; Check what's happenin' next
;;;

(defun lyskom-complete-string-next-char (state data-list)
  (let ((eofp nil)
        (open-paren-p nil)
        (close-paren-p nil)
        (matchp t)
        (spacep nil)
        (char nil)
        (xchar nil))

    (lyskom-save-excursion
     (set-buffer lyskom-buffer)
     (mapc
      (function 
       (lambda (x)
         (cond ((>= (aref x 0) (aref x 1))
                (setq eofp t)
                (setq matchp nil))
               ((eq (aref (aref x 2) (aref x 0)) ?\()
                (setq open-paren-p t))
               ((eq (aref (aref x 2) (aref x 0)) ?\))
                (setq close-paren-p t))
               ((eq (aref (aref x 2) (aref x 0)) ?\ )
                (setq spacep t)))

         (setq matchp (and matchp
                           (if (null char)
                               (progn
                                 (setq xchar (aref (aref x 2)
                                                   (aref x 0)))
                                 (setq char (lyskom-unicase-char xchar)))
                             (eq char (lyskom-unicase-char
                                       (aref (aref x 2)
                                             (aref x 0)))))))))
      data-list))

    (aset state 1 xchar)
    (cond (eofp (aset state 0 'eof))
          ((and matchp open-paren-p)
           (aset state 0 'open-paren-match))
          ((and matchp close-paren-p)
           (aset state 0 'close-paren-match))
          (matchp
           (aset state 0 'match))
          (spacep
           (aset state 0 'space-mismatch))
          (open-paren-p
           (aset state 0 'open-paren-mismatch))
          (t
           (aset state 0 'mismatch))))
  state)



;;; ============================================================
;;; Session reading
;;;

(defun lyskom-read-session-no (prompt &optional empty initial only-one)
  (let ((possible-matches
         (lyskom-session-from-conf
          (lyskom-read-conf-no prompt
                               (if kom-permissive-completion
                                   '(pers)
                                 '(login))
                               empty
                               initial
                               t))))
    (if (and (> (length possible-matches) 1)
             only-one)
        (lyskom-read-session-resolve-ambiguity possible-matches)
      possible-matches)))


(defun lyskom-session-from-conf (conf-no)
  (let ((who-list (lyskom-completing-who-is-on))
        (sessions nil))
    (if (lyskom-have-feature dynamic-session-info)
	(while who-list
	  (if (eq (dynamic-session-info->person (car who-list)) conf-no)
	      (setq sessions (cons (dynamic-session-info->session
				    (car who-list))
				   sessions)))
	  (setq who-list (cdr who-list)))
      (while who-list
	(if (eq (who-info->pers-no (car who-list))
		conf-no)
	    (setq sessions (cons (who-info->connection (car who-list))
				 sessions)))
	(setq who-list (cdr who-list))))
    (cond ((and (null sessions) kom-permissive-completion) (list (- conf-no)))
          (t sessions))))


(defun lyskom-read-session-resolve-ambiguity (sessions)
  (lyskom-insert "\n")
  (let* ((s-width (1+ (apply 'max (mapcar (function
					   (lambda (x)
					     (lyskom-string-width (int-to-string x))))
					  sessions))))
	 (format-string-s (lyskom-info-line-format-string s-width "s" "s"))
	 (format-string-p (lyskom-info-line-format-string s-width "P" "M")))
    (lyskom-format-insert format-string-s
			  ""
			  (lyskom-get-string 'lyskom-name)
			  (lyskom-get-string 'is-in-conf))
    (lyskom-format-insert format-string-s
			  ""
			  (lyskom-get-string 'from-machine)
			  (lyskom-get-string 'is-doing))
    (lyskom-insert
     (concat (make-string (- (lyskom-window-width) 2) ?-)
	     "\n"))
    (let ((result nil)
	  (who-info
	   (mapcar (function
		    (lambda (el)
		      (let* ((info (blocking-do 'get-session-info el))
			     (confconfstat
			      (blocking-do 'get-uconf-stat
					   (session-info->working-conf info))))
			(lyskom-format-insert
			 format-string-p
			 (format "%d%s"
				 (session-info->connection info)
				 (if (eq (session-info->connection info)
					 lyskom-session-no)
				     "*" " "))
			 (session-info->pers-no info)
			 (or confconfstat
                             (lyskom-get-string 'not-present-anywhere)))
			(lyskom-format-insert
			 format-string-p
			 ""
			 (lyskom-return-username info)
			 (concat "("
				 (session-info->doing info)
				 ")"))
			(cons (number-to-string
			       (session-info->connection info))
			      info))))
		   (sort sessions '<))))
      (lyskom-insert (concat (make-string (- (lyskom-window-width) 2) ?-)
			     "\n"))
      (lyskom-insert (lyskom-format 'total-users-sans-date (length who-info)))
      (lyskom-scroll)
      (while (string= ""
                      (setq result (lyskom-completing-read
				    (lyskom-get-string 'resolve-session)
				    (lyskom-maybe-frob-completion-table 
				     who-info)
				    nil
				    t
				    (car (car who-info))
				    nil))))
      (list (session-info->connection (cdr (assoc result who-info)))))))


;;; ================================================================
;;; Reading text data
;;;

(defun lyskom-completing-read-text-no (prompt default text-no misc-types)
  "Read a text number based on some other text number.
PROMPT is the prompt. DEFAULT is the default value. It can be a number,
nil (in which case a default is computed) or a function that is called
with TEXT-NO and the list of possible completions as argument (and
should return a single number). TEXT-NO is the text number to base
reading on. MISC-TYPES are the types of misc-infos in the base text
to use as completion"
  (unless (listp misc-types) (setq misc-types (list misc-types)))
  (let* ((text-stat (blocking-do 'get-text-stat text-no))
	 (completions 
	  (delq nil
		(mapcar (lambda (misc)
			  (and (memq (misc-info->type misc) misc-types)
			       (cond ((eq (misc-info->type misc) 'COMM-IN)
				      (misc-info->comm-in misc))
				     ((eq (misc-info->type misc) 'FOOTN-IN)
				      (misc-info->footn-in misc))
				     ((eq (misc-info->type misc) 'COMM-TO)
				      (misc-info->comm-to misc))
				     ((eq (misc-info->type misc) 'FOOTN-TO)
				      (misc-info->footn-to misc)))))
			(text-stat->misc-info-list text-stat)))))
    (lyskom-read-number prompt
			(cond ((functionp default) (funcall default text-no completions))
			      (default)
			      ((memq lyskom-current-text completions) lyskom-current-text)
			      (t (car completions)))
			nil
			nil
			completions
			)))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: keyboard-menu.el
;;;;
;;;; Implements completing-read almost-compatible lyskom-read-from-menu
;;;; used to implement keyboard navigation of LysKOM menus ('cos tmm
;;;; was *so* ugly).
;;;;


(defvar lyskom-keyboard-menu-menu)
(defvar lyskom-keyboard-menu-buffer)
(defvar lyskom-keyboard-menu-prompt)
(defvar lyskom-keyboard-menu-selection)
(defvar lyskom-keyboard-menu-overlay)

(defvar lyskom-keyboard-menu-keymap nil)

(defun lyskom-keyboard-menu-read-char (prompt)
  "Read a single keyboard event from the keyboard.
Like read-event in Gnu Emacs or next-command-event in XEmacs."
  (message prompt)
  (sit-for 0)
  (lyskom-xemacs-or-gnu
   (let ((ev (next-command-event nil prompt)))
	    (when (eq 'key-press (event-type ev))
	      (cond ((eq (event-key ev) 'return) 'return)
		    ((event-to-character ev))
		    ((event-key ev))
		    (t nil))))
   (read-event)))

(defun lyskom-keyboard-menu-keys-for-string (string)
  "Return a list of suitable mnemonics for menu item STRING."
  (let ((a-uc nil)
        (a-lc nil)
        (b-uc nil)
        (b-lc nil)
        (c-uc nil)
        (c-lc nil))
    (lyskom-traverse word (string-split " " string)
      (unless (eq (elt word 0) ?\()
        (setq a-lc (cons (downcase (substring word 0 1)) a-lc)
              a-uc (cons (upcase (substring word 0 1)) a-uc))
        (when (> (length word) 1)
          (setq b-lc (cons (downcase (substring word 1 2)) b-lc)
                b-uc (cons (upcase (substring word 1 2)) b-uc)))
        (when (> (length word) 2)
          (setq c-lc (cons (downcase (substring word 2 3)) c-lc)
                c-uc (cons (upcase (substring word 2 3)) c-uc)))
        ))
    (mapcar (lambda (s)
              (lyskom-xemacs-or-gnu
               s
               (lyskom-encode-coding-string 
                s 
                (and (boundp 'default-keyboard-coding-system)
                     default-keyboard-coding-system))))
            (nconc (nreverse a-lc)
                   (nreverse b-lc)
                   (nreverse c-lc)
                   (nreverse a-uc)
                   (nreverse b-uc)
                   (nreverse c-uc)
                   ))))


(defun lyskom-keyboard-menu-frob-table (table)
  "Convert TABLE to a form suitable for keyboard menus.
TABLE is an alist whose elements' cars are strings. The result will
be an alist whose elements' cars are mnemonics for the string in the
same element's cdr."
  (let ((used-keys nil)
        (default-keys "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
        (default-keys-index 0))
    (nconc
     (mapcar 
      (lambda (element)
        (let* ((string (car element))
               (keys (lyskom-keyboard-menu-keys-for-string string))
               (mnemonic (lyskom-traverse key keys
                           (unless (member key used-keys)
                             (lyskom-traverse-break key)))))

          (while (and (not mnemonic)
                      (< default-keys-index (length default-keys)))
            (let ((key (substring default-keys 
                                  default-keys-index
                                  (1+ default-keys-index))))
              (unless (member key used-keys)
                (setq mnemonic key))))

          (setq used-keys (cons mnemonic used-keys))
          (list mnemonic string element)))
      table)
     (list (list "C-g" (lyskom-get-string 'keyboard-cancel) nil)))))



(defun lyskom-keyboard-menu-highlight-selected ()
  "Highlight the selected menu item"
  (set-buffer lyskom-keyboard-menu-buffer)
  (let ((pos (text-property-any (point-min) 
                                (point-max)
                                'lyskom-keyboard-menu-item
                                lyskom-keyboard-menu-selection)))
    (cond ((or (null pos)
               (null lyskom-keyboard-menu-selection))
           (lyskom-xemacs-or-gnu
            (set-extent-face lyskom-keyboard-menu-overlay nil)
            (overlay-put lyskom-keyboard-menu-overlay 'face nil))
           (goto-char (point-min)))
          (t (let ((start pos)
                   (end (or (next-single-property-change 
                             pos
                             'lyskom-keyboard-menu-item)
                            (point-max))))
               (lyskom-xemacs-or-gnu
                (set-extent-endpoints lyskom-keyboard-menu-overlay
                                      start end)
                (move-overlay lyskom-keyboard-menu-overlay
                              start end)))
             (goto-char pos)
             (lyskom-xemacs-or-gnu
              (set-extent-face lyskom-keyboard-menu-overlay kom-mark-face)
              (overlay-put lyskom-keyboard-menu-overlay
                           'face kom-mark-face))))
    (save-excursion
      (unless (lyskom-pos-visible-in-window-p pos)
        (recenter)))))


(defun lyskom-keyboard-menu-format-item (item &optional pad-len)
  "Format a single menu item as a string"
  (let ((string (format "%s (%s)" (elt item 1) (elt item 0))))
    (when (and pad-len (> pad-len (length string)))
      (setq string (concat string (make-string (- pad-len (length string))
                                               ?\ ))))
    (add-text-properties 0 
                         (length string)
                         (list 'lyskom-keyboard-menu-item item)
                         string)
    string))

(defun lyskom-keyboard-menu-insert (prompt menu)
  "Insert the text-based representation of the menu.
PROMPT is the menu title and MENU is the menu data."
  (let* ((line-length (apply 'max
                             (mapcar (lambda (x)
                                       (+ (length (elt x 0))
                                          (length (elt x 1))
                                          3))
                                     menu)))
         (columns (/ (+ (window-width) 1)
                     (+ line-length 2)))
         (dashes (make-string (+ (* line-length columns)
                                 (* 2 (1- columns))) ?-))
         (quit-item nil))

    (insert (format "%s\n%s\n" prompt dashes))
    (lyskom-traverse el menu
      (if (null (elt el 2))
          (setq quit-item el)
        (let ((string (lyskom-keyboard-menu-format-item el line-length)))
          (when (> (+ (current-column) (length string) 2) (window-width))
            (insert "\n"))
          (unless (bolp) (insert "  "))
          (insert string))))
    (unless (bolp)
      (insert "\n"))
    (insert dashes)
    (insert "\n")
    (when quit-item
      (insert (lyskom-keyboard-menu-format-item quit-item)))))




;;; ================================================================
;;; Interactive commands
;;; 


(defun lyskom-read-from-menu (prompt table)
  "Let the user select one of the values in TABLE.
TABLE is an alist whose elements' cars are strings.
Returns the selected string."
  (let* ((menu (lyskom-keyboard-menu-frob-table table))
         (buffer (get-buffer-create "*Keyboard menu*"))
         (menu-window-height 0))
    (save-current-buffer

      ;; Format the buffer

      (set-buffer buffer)
      (erase-buffer)
      (lyskom-keyboard-menu-insert prompt menu)

      (goto-char (point-min))
      (setq menu-window-height (+ 2 (count-lines (point-min) (point-max))))

      ;; Display the window and do the thing

      (save-window-excursion
        (if (< (- (window-height (selected-window))
                  menu-window-height) 5)
            (select-window
             (display-buffer buffer t))
          (split-window (selected-window) menu-window-height)
          (switch-to-buffer buffer))
        (let* ((lyskom-keyboard-menu-buffer buffer)
               (lyskom-keyboard-menu-menu menu)
               (lyskom-keyboard-menu-selection nil)
               (lyskom-keyboard-menu-overlay 
                (lyskom-xemacs-or-gnu
                 (make-extent 1 1 lyskom-keyboard-menu-buffer)
                 (make-overlay 0 0 lyskom-keyboard-menu-buffer)))
               (result nil))

          (lyskom-xemacs-or-gnu
           (set-extent-face lyskom-keyboard-menu-overlay nil)
           (overlay-put lyskom-keyboard-menu-overlay 'face nil))

          (condition-case nil
              (while (null result)
                (lyskom-keyboard-menu-highlight-selected)
                (let ((c (lyskom-keyboard-menu-read-char 
                          (lyskom-format "%#1s %#2s: %#3s"
                                         prompt
                                         (lyskom-get-string 'keyboard-menu-help)
                                         (or (elt lyskom-keyboard-menu-selection 1)
                                             "")))))
                  (cond ((eq (lookup-key global-map (vector c)) 'keyboard-quit)
                         (keyboard-quit))
                        ((eq c ?\C-a) (lyskom-keyboard-menu-beginning-of-line))
                        ((eq c ?\C-e) (lyskom-keyboard-menu-end-of-line))
                        ((or (eq c 'up)
                             (eq c ?\C-p))
                         (lyskom-keyboard-menu-up))
                        ((or (eq c ?\C-b)
                             (eq c 'left))
                         (lyskom-keyboard-menu-backward))
                        ((or (eq c 'down)
                             (eq c ?\C-n))
                         (lyskom-keyboard-menu-down))
                        ((or (eq c ?\C-f)
                             (eq c 'right))
                         (lyskom-keyboard-menu-forward))
                        ((or (eq c 'return)
                             (eq c ?\r)
                             (eq c ?\n))
                         (setq result lyskom-keyboard-menu-selection))
                        ((and (lyskom-characterp c)
                              (assoc (make-string 1 c) 
                                     lyskom-keyboard-menu-menu))
                         (if kom-keyboard-menu-immediate-selection
                             (setq result (assoc (make-string 1 c) 
                                                 lyskom-keyboard-menu-menu))
                           (setq lyskom-keyboard-menu-selection
                                 (assoc (make-string 1 c)
                                        lyskom-keyboard-menu-menu))))))
                (when lyskom-keyboard-menu-selection
                  (setq lyskom-keyboard-menu-menu
                        (lyskom-rotate-list lyskom-keyboard-menu-menu
                                            lyskom-keyboard-menu-selection))))
            (quit (setq result nil)))
          (elt result 2)
          )))))

(defun lyskom-keyboard-menu-up ()
  "Move up one menu item"
  (interactive)
  (let ((done nil))
    (while (not done)

      ;; Move up one line
      (let ((current-prefix-arg nil))
        (condition-case nil
            (call-interactively 'previous-line)
          (beginning-of-buffer (setq done t))))

      ;; Scan the items on this line until we find the one
      ;; just before point or just after point or around
      ;; point.

      (let ((selection nil)
            (start (save-excursion (beginning-of-line) (point)))
            (end (save-excursion (end-of-line) (point)))
            (pos (point)))
        (while (and (>= pos start) (null selection))
          (setq selection 
                (get-text-property pos 'lyskom-keyboard-menu-item)
                pos (1- pos)))

        (setq pos (point))
        (while (and (<= pos end) (null selection))
          (setq selection 
                (get-text-property pos 'lyskom-keyboard-menu-item)
                pos (1+ pos)))
        (when selection (setq done t
                              lyskom-keyboard-menu-selection selection))))))

(defun lyskom-keyboard-menu-down ()
  "Move down one menu item."
  (interactive)
  (let ((done nil))
    (while (not done)

      ;; Move up one line
      (let ((current-prefix-arg nil))
        (condition-case nil
            (call-interactively 'next-line)
          (end-of-buffer (setq done t))))

      ;; Scan the items on this line until we find the one
      ;; just before point or just after point or around
      ;; point.

      (let ((selection nil)
            (start (save-excursion (beginning-of-line) (point)))
            (end (save-excursion (end-of-line) (point)))
            (pos (point)))
        (while (and (>= pos start) (null selection))
          (setq selection 
                (get-text-property pos 'lyskom-keyboard-menu-item)
                pos (1- pos)))

        (setq pos (point))
        (while (and (<= pos end) (null selection))
          (setq selection 
                (get-text-property pos 'lyskom-keyboard-menu-item)
                pos (1+ pos)))
        (when selection (setq done t
                              lyskom-keyboard-menu-selection selection))))))




(defun lyskom-keyboard-menu-forward ()
  "Move right one menu item."
  (interactive)
  (let ((pos (point))
        (item nil))
    (while (and pos (or (null item) (eq item lyskom-keyboard-menu-selection)))
      (setq pos (next-single-property-change pos 'lyskom-keyboard-menu-item))
      (when pos
        (setq item (get-text-property pos 'lyskom-keyboard-menu-item))))
    (when item (setq lyskom-keyboard-menu-selection item))))


(defun lyskom-keyboard-menu-backward ()
  "Move left one menu item."
  (interactive)
  (let ((pos (point))
        (item nil))
    (while (and pos (or (null item) (eq item lyskom-keyboard-menu-selection)))
      (setq pos (previous-single-property-change pos 'lyskom-keyboard-menu-item))
      (when pos
        (setq item (get-text-property pos 'lyskom-keyboard-menu-item))))
    (when item (setq lyskom-keyboard-menu-selection item))))

(defun lyskom-keyboard-menu-beginning-of-line ()
  "Move to the first menu item of this line."
  (interactive)
  (beginning-of-line)
  (setq lyskom-keyboard-menu-selection
        (or (get-text-property (point) 'lyskom-keyboard-menu-item)
            (get-text-property (or (next-single-property-change 
                                    (point) 'lyskom-keyboard-menu-item
                                    nil (save-excursion (end-of-line) (point)))
                                   (point))
                               'lyskom-keyboard-menu-item))))

(defun lyskom-keyboard-menu-end-of-line ()
  "Move to the last menu item of this line."
  (interactive)
  (end-of-line)
  (setq lyskom-keyboard-menu-selection
        (if (get-text-property (point) 'lyskom-keyboard-menu-item)
            (get-text-property (point) 'lyskom-keyboard-menu-item)
          (let ((pos (previous-single-property-change 
                      (point) 'lyskom-keyboard-menu-item
                      nil (1- (save-excursion (beginning-of-line) (point))))))
            (when (and pos (not (zerop pos)))
              (or (get-text-property pos 'lyskom-keyboard-menu-item)
                  (get-text-property (1- pos)  'lyskom-keyboard-menu-item)))))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: command.el
;;;;
;;;; This file contains stuff regarding commands.
;;;;


;;; ======================================================================
;;; LysKOM user commands
;;; The new, blocking commands have a very similar structure
;;;
;;;  (defun kom-cmd (args)
;;;    "Documentation"
;;;    (interactive "...")
;;;    (lyskom-start-of-command 'kom-cmd)
;;;    (unwind-protect
;;;        (progn ...)
;;;      (lyskom-end-of-command)))
;;;
;;; This can now be written as
;;;
;;; (def-kom-command kom-cmd (args)
;;;   "Documentation"
;;;   (interactive "...")
;;;   ...)

(eval-and-compile
  (defun lyskom-fix-interactive-decl (decl command)
    (cond ((stringp (car (cdr decl))) decl)
          (t `(interactive (let ((lyskom-current-command ',command))
                             ,@(cdr decl)))))))




(defmacro def-kom-command (cmd args doc &rest forms)
  "Define a LysKOM command CMD with args ARGS and docstring DOC.

This is like a defun for LysKOM commands. After the docstring you may
have a series of keywords that alter the behavior of the command,
followed by an interactive declaration (required) and the body of the
command.

The following keywords are recognized:

:dead-ok        See the DEAD-OK paramter to lyskom-start-of-command
:may-interrupt  See the MAY-INTERRUPT parameter to lyskom-start-of-command
:prompt-format  See the PROMPT-FORMAT parameter to lyskom-start-of-command

Note that :prompt-format requires an argument."
  (let (interactive-decl may-interrupt dead-ok prompt-format)
    (while (memq (car forms) '(:prompt-format :dead-ok :may-interrupt))
      (cond ((eq (car forms) :prompt-format)
             (setq prompt-format (car (cdr forms)))
             (setq forms (cdr forms)))
            ((eq (car forms) :dead-ok)
             (setq dead-ok t))
            ((eq (car forms) :may-interrupt)
             (setq may-interrupt t)))
      (setq forms (cdr forms)))

    (when (and (listp (car forms))
               (eq 'interactive (car (car forms))))
      (setq interactive-decl (car forms) forms (cdr forms)))

    (if (not (stringp doc))
        (progn (message "!! No docstring for command %S" cmd)
               (setq forms (cons interactive-decl forms))
               (setq interactive-decl doc)
               (setq doc "")))
    (if (not (eq (car interactive-decl) 'interactive))
        (progn (message "!! Missing interactive declaration for %S; assuming \(interactive\)" cmd)
               (setq forms (cons interactive-decl forms))
               (setq interactive-decl '(interactive))))
    (let ((bufsym (make-symbol (format "%S-start-buffer" cmd)))
          (winsym (make-symbol (format "%S-start-window" cmd))))
      `(defun ,cmd ,args
         ,doc
         ,(lyskom-fix-interactive-decl interactive-decl cmd)
         (let ((lyskom-command-point (point)))
           (lyskom-start-of-command ',cmd ,may-interrupt ,dead-ok ,prompt-format)
           (let ((,bufsym (current-buffer))
                 (,winsym (selected-window)))
             (unwind-protect
                 (condition-case nil
                     (progn ,@forms)
                   (quit (ding)
                         (lyskom-insert-before-prompt
                          (lyskom-get-string 'interrupted))))
               (lyskom-save-excursion
                 (cond ((and (buffer-live-p ,bufsym)
                             (window-live-p ,winsym)
                             (eq (window-buffer ,winsym) ,bufsym))
                        (save-selected-window
                          (set-buffer ,bufsym)
                          (select-window ,winsym)
                          (lyskom-end-of-command)))

                       ((buffer-live-p ,bufsym)
                        (set-buffer ,bufsym)
                        (lyskom-end-of-command))

                       (t (lyskom-end-of-command)))))))))))


;;
;; def-kom-emacs-command works like def-kom-command, but the template 
;; is different. Commands defined this way will run as regular Emacs
;; commands when invoked outside of a LysKOM buffer. 
;;
;; The variable <cmd>-running-as-kom-command is non-nil when running
;; as a LysKOM command and nil otherwise.
;;
;; Note: this function catches *all* errors in lyskom-start-of-command
;;       which may not be what you want, so be careful.
;;
;; 
;; (defun kom-cmd (args)
;;   "Documentation"
;;   (interactive "...")
;;   (let ((kom-cmd-running-as-kom-command nil))
;;     (condition-case nil
;;         (progn (lyskom-start-of-command 'kom-cmd)
;;                (setq kom-cmd-running-as-kom-command t))
;;       (error nil))
;;     (unwind-protect
;;         (condition-case nil
;;             (progn ...)
;;           (quit (ding)
;;                 (lyskom-insert-before-prompt
;;                  (lyskom-get-string 'interrupted))))
;;       (and kom-cmd-running-as-kom-command (lyskom-end-of-command)))))
;; 

(defmacro def-kom-emacs-command (cmd args doc &rest forms)
  (let (interactive-decl may-interrupt dead-ok prompt-format)
    (while (memq (car forms) '(:prompt-format :dead-ok :may-interrupt))
      (cond ((eq (car forms) :prompt-format)
             (setq prompt-format (car (cdr forms)))
             (setq forms (cdr forms)))
            ((eq (car forms) :dead-ok)
             (setq dead-ok t))
            ((eq (car forms) :may-interrupt)
             (setq may-interrupt t)))
      (setq forms (cdr forms)))

    (when (and (listp (car forms))
               (eq 'interactive (car (car forms))))
      (setq interactive-decl (car forms) forms (cdr forms)))

    (if (not (stringp doc))
        (progn (message "!! No docstring for command %S" cmd)
               (setq forms (cons interactive-decl forms))
               (setq interactive-decl doc)
               (setq doc "")))
    (if (not (eq (car interactive-decl) 'interactive))
        (progn (message "!! Missing interactive declaration for %S; assuming \(interactive\)" cmd)
               (setq forms (cons interactive-decl forms))
               (setq interactive-decl '(interactive))))

    (let ((rsym (intern (format "%S-running-as-kom-command" cmd)))
          (bufsym (make-symbol (format "%S-start-buffer" cmd)))
          (winsym (make-symbol (format "%S-start-window" cmd))))
      `(defun ,cmd ,args
         ,doc
         ,(lyskom-fix-interactive-decl interactive-decl cmd)
         (let ((,rsym nil))
           (condition-case nil
               (progn (lyskom-start-of-command ',cmd ,may-interrupt ,dead-ok ,prompt-format)
                      (setq ,rsym t))
             (error nil))
           (let ((,bufsym (current-buffer))
                 (,winsym (selected-window)))
             (unwind-protect
                 (condition-case nil
                     (progn ,@forms)
                   (quit (ding)
                         (lyskom-insert-before-prompt
                          (lyskom-get-string 'interrupted))))
               (and ,rsym
                    (lyskom-save-excursion
                      (cond ((and (buffer-live-p ,bufsym)
                                  (window-live-p ,winsym)
                                  (eq (window-buffer ,winsym) ,bufsym))
                             (save-selected-window
                               (set-buffer ,bufsym)
                               (select-window ,winsym)
                               (lyskom-end-of-command)))

                            ((buffer-live-p ,bufsym)
                             (set-buffer ,bufsym)
                             (lyskom-end-of-command))

                            (t (lyskom-end-of-command))))))))))))


(put 'def-kom-command 'edebug-form-spec
     '(&define name lambda-list
	       [&optional stringp]	; Match the doc string, if present.
	       ("interactive" interactive)
	       def-body))

(put 'def-kom-emacs-command 'edebug-form-spec
     '(&define name lambda-list
	       [&optional stringp]	; Match the doc string, if present.
	       ("interactive" interactive)
	       def-body))



;;;; ================================================================
;;;;                User-level commands and functions.


(defsubst lyskom-command-name (command &optional language)
  "Get the command name for the command COMMAND"
  (condition-case nil
      (lyskom-get-string command 'lyskom-command language)
    (error nil)))

(defun lyskom-ok-command (alternative administrator)
  "Returns non-nil if it is ok to do such a command right now."
  (when (vectorp alternative) 
    (setq alternative (cons (elt alternative 0) (elt alternative 1))))
  (if administrator
      (not (memq (cdr alternative) lyskom-admin-removed-commands))
    (not (memq (cdr alternative) lyskom-noadmin-removed-commands))))

(defun kom-extended-command ()
  "Read a LysKOM function name and call the function."
  (interactive)
  (let ((fnc (lyskom-read-extended-command current-prefix-arg)))
    (cond
     (fnc (call-interactively fnc))
     (t (kom-next-command)))) )

(defvar lyskom-command-minibuffer-local-completion-map
  (let ((map (copy-keymap minibuffer-local-completion-map)))
    (define-key map " " 'lyskom-command-complete-word)
    map)
  "Keymap used for reading LysKOM names.")

(defvar lyskom-command-minibuffer-local-must-match-map
  (let ((map (copy-keymap minibuffer-local-must-match-map)))
    (lyskom-xemacs-or-gnu 
     (progn (lyskom-set-keymap-parent map lyskom-minibuffer-local-completion-map)
            (define-key map " " 'lyskom-command-complete-word))
     (define-key map " " 'lyskom-command-complete-word))
    map)
  "Keymap used for reading LysKOM names.")

(defun lyskom-read-extended-command (&optional prefix-arg prompt)
  "Reads and returns a command"
  (let* ((completion-ignore-case t)
	 (minibuffer-setup-hook minibuffer-setup-hook)
         (base-prompt (cond ((null prompt) (lyskom-get-string 'extended-command))
                            ((symbolp prompt) (lyskom-get-string prompt))
                            (t prompt)))
	 (alternatives (mapcar 
			(lambda (pair)
			  (cons 
			   (cdr pair)
			   (car pair)))
			(lyskom-get-strings lyskom-commands
					    'lyskom-command)))
	 (name nil)
         (prefix-text
          (cond ((eq prefix-arg '-) "- ")
                ((equal prefix-arg '(4)) "C-u ")
                ((integerp prefix-arg) 
                 (format "%d " prefix-arg))
                ((and (consp prefix-arg) 
                      (integerp (car prefix-arg)))
                 (format "%d " (car prefix-arg)))
                (t nil)))
         (prompt (if prefix-text
                     (concat prefix-text base-prompt)
                   base-prompt)))

    (let ((minibuffer-local-completion-map 
           lyskom-command-minibuffer-local-completion-map)
          (minibuffer-local-must-match-map 
           lyskom-command-minibuffer-local-must-match-map))
      (while (or (null name)
                 (string= "" name))
        (setq name (lyskom-completing-read prompt
                                           'lyskom-complete-command
                                           ;; lyskom-is-administrator is buffer-local and
                                           ;; must be evalled before the call to 
                                           ;; completing-read
                                           ;; Yes, this is not beautiful
                                           (list 'lambda '(alternative) ;
                                                 (list 'lyskom-ok-command 'alternative
                                                       lyskom-is-administrator))
                                           t
                                           nil
                                           'lyskom-command-history))))
    (cdr (lyskom-string-assoc name alternatives))))


(defun lyskom-update-command-completion ()
  "Build a list of alternatives for completion of LysKOM commands.
Each list element is a vector [NAME COMMAND CANONICAL]. NAME is the
command name, COMMAND is the command and CANONICAL is the name 
transformed for matching."
  (setq lyskom-command-alternatives
        (mapcar (lambda (el) 
                  (vector (cdr el)
                          (car el)
                          (lyskom-completing-strip-command
                           (lyskom-unicase (cdr el)))
			  (lyskom-unicase (cdr el))))
                (lyskom-get-strings lyskom-commands 'lyskom-command))))

(defun lyskom-lookup-command-by-name (string &optional predicate)
  "Look up the command that corresponds to a certain string."
  (lyskom-complete-command string predicate 'lyskom-lookup))

(defsubst lyskom-command-match-string-regexp (string)
  (concat 
   "^\\s-*"
   (replace-in-string (regexp-quote
                       (lyskom-unicase
                        (lyskom-completing-strip-command string)))
                      "\\s-+" "\\\\S-*\\\\s-+")
          "\\s-*"))

(defun lyskom-completing-strip-command (string)
  "Strip parens and crap from a name.
If optional DONT-STRIP-SPACES is non-nil, don't strip spaces at front
and back of the string."
  (while (string-match "([^()]*)" string) ; Strip nested parens
    (setq string (replace-match "" t t string)))
  (while (string-match "\\s-\\s-+" string) ; Collapse spaces
    (setq string (replace-match " " t t string)))
  (while (string-match "([^()]*$" string) ; Strip incomplete parens at end
    (setq string (substring string 0 (match-beginning 0))))
  string)

;;; FIXME: Below is an idea on how to do command completion more right.
;;;
;;; Precompute lists of words in all commands. Include the optional words 
;;; and mark them as optional. Possibly allow sublists in the list, and
;;; make lists of words into sublists and mark the entire sublist as
;;; optional.
;;;
;;; When matching, divide the input into a list of words. Start matchin
;;; prefixes against the list of words for a command. It goes something 
;;; like this:
;;;
;;; C = 0, I = 0
;;; while there are more words in the input and command
;;;    A = word I of the input
;;;    B = word C of the command
;;;    if B is an optional word then
;;;       N = index of word following optional group that B is part of
;;;       push N,I onto backtracking stack
;;;    if A is a prefix of B then
;;;       I = I + 1
;;;	  C = C + 1
;;;       next iteration of the loop
;;;    if the backtracking stack is empty then
;;;       return mismatch
;;;    pop X,Y from the backtracking stack
;;;    C = X
;;;    I = Y
;;;    next iteration of the loop
;;; end while
;;; if there are left-over words in C then
;;;    return a mismatch
;;; else
;;;    return a match (I,C)
;;;
;;; When doing a word completion we can let the completion function do the
;;; actual work. It computes the longest possible completion we can have
;;; (i.e. one full word more than what we've got) and hands that over to
;;; lyskom-complete-string. Computing the longest possible completion
;;; goes something like this:
;;;
;;; W = nil
;;; Store I,C for all matches
;;; for all matches M = 1 .. N do
;;;    do something useful
;;;
;;; The idea is to get the last word that matches the input by storing
;;; the results of the match computation and then looking at the
;;; following word in all possible completions. If the following word
;;; is a word in an optional group that is the same in all possible
;;; completions, then that plus the first C words of any of the
;;; possible completions is the maximum possible. If the next word is
;;; a word of an optional group that does *not* match in all
;;; possibles, then ignore the optional group and look at the next
;;; word instead.

(defun lyskom-complete-command (string predicate all)
  "Completion function for LysKOM commands."
  (when (string-match "^\\s-+" string)
    (setq string (substring string (match-end 0))))
  (let ((alternatives nil)
        (m-string (lyskom-command-match-string-regexp string))
        (u-string (lyskom-unicase string))
        (exact nil))
    (lyskom-traverse el lyskom-command-alternatives
      (when (and (string-match m-string (elt el 2))
                 (or (null predicate) (funcall predicate el)))
        (setq alternatives (cons (if (eq all 'lyskom-lookup) el (elt el 0)) alternatives))
	(if (string-equal u-string (elt el 3)) (setq exact el))))
    (cond 
     ((eq all 'lyskom-lookup) (and exact (elt exact 1)))
     ((eq all 'lambda) exact)
     (all alternatives)
     ((null alternatives) nil)
     ((and (= (length alternatives) 1) exact) t)
     (t (let ((tmp (lyskom-complete-string alternatives)))
	  (lyskom-maybe-recode-string
	   (if (string-match (concat (regexp-quote (lyskom-unicase tmp)) "\\s-") u-string)
	       (concat tmp " ")
	     tmp)))))))

(defun lyskom-command-complete-word ()
  (interactive)
  (let ((string (buffer-string)))
    (when (next-single-property-change 0 'read-only string)
      (setq string 
            (substring string 
                       (next-single-property-change 0 'read-only string))))

    (let ((completion (try-completion string
                                      minibuffer-completion-table
                                      minibuffer-completion-predicate)))
      (cond ((null completion) (lyskom-minibuffer-message " [No match]") nil)
            ((eq completion t) nil)
            (t (let* ((tmp string))
                 (when (and (string-equal (lyskom-unicase completion)
                                          (lyskom-unicase tmp))
                            (not (string-match "\\s-$" completion)))
                   (if (stringp (setq tmp (try-completion 
                                           (concat tmp " ")
                                           minibuffer-completion-table
                                           minibuffer-completion-predicate)))
                       (setq completion tmp)))
                 (if (string-equal (lyskom-unicase completion)
                                   (lyskom-unicase string))
                     (progn (minibuffer-completion-help) nil)
                   (delete-region (- (point-max) (length string))
                                  (point-max))

                   ;; Now we have the suggested completion
                   ;; Expand what's in the buffer by one real word.

                   (let* ((count (lyskom-command-complete-count-words string))
                          (result (lyskom-command-complete-word-truncate 
                                   completion count)))
                     (when (string= result string)
                       (setq result (lyskom-command-complete-word-truncate 
                                     completion (1+ count))))
                     (insert result))
                   t)))))))

(defvar lyskom-command-complete-regexp
  "\\(([^\)]*)\\s-+\\)*\\S-+\\(\\s-*([^\)]*)\\)*\\(\\s-+\\|\\'\\)")

(defun lyskom-command-complete-word-truncate (completion count)
  "Truncate completion COMPLETION to COUNT words."
  (let ((start 0))
    (while (and (> count 0)
                (string-match lyskom-command-complete-regexp completion start))
      (setq start (match-end 0)
            count (1- count)))
    (substring completion 0 start)))

(defun lyskom-command-complete-count-words (string)
  "Count number of real words in command name or completion STRING."
  (let ((start 0)
        (count 0))
    (while (string-match lyskom-command-complete-regexp string start)
      (setq start (match-end 0)
            count (1+ count)))
    count))



;;; The code below is an alternative implementation of 
;;; lyskom-command-complete-word that mucks with the
;;; contents of the minibuffer and then calls the regular
;;; minibuffer functions.
;;;
;;;
;;;(defun lyskom-command-complete-word-count-words (string)
;;;  (let ((count 0)
;;;        (start 0))
;;;    (while (string-match lyskom-command-complete-regexp string start)
;;;      (setq start (match-end 0)
;;;            count (1+ count)))
;;;    count))  
;;;
;;;(defun lyskom-command-complete-word ()
;;;  (interactive)
;;;  (let* ((string (buffer-string))
;;;         completions)
;;;    ;; Strip the prompt in Emacs 21
;;;    (when (next-single-property-change 0 'read-only string)
;;;      (setq string 
;;;            (substring string 
;;;                       (next-single-property-change 0 'read-only string))))
;;;
;;;    (setq completions (save-excursion
;;;                        (set-buffer lyskom-buffer)
;;;                        (lyskom-complete-command string 
;;;                                                 nil
;;;                                                 nil)))
;;;    (when (stringp completions)
;;;      (let ((original-count (lyskom-command-complete-word-count-words string))
;;;            (start 0))
;;;        (while (and (> original-count 0)
;;;                    (string-match lyskom-command-complete-regexp completions start))
;;;          (setq start (match-end 0)
;;;                original-count (1- original-count)))
;;;
;;;        (delete-region (- (point-max) (length string)) (point-max))
;;;        (let ((result (substring completions 0 start)))
;;;          (string-match "\\s-*\\'" result)
;;;          (insert (substring result 0 (match-beginning 0)))))))
;;;  (minibuffer-complete-word))


(defun lyskom-start-of-command (function &optional may-interrupt dead-ok prompt-format)
  "This function is run at the beginning of every LysKOM command.
It moves the cursor one line down, and +++ later it will tell the server
that the previous text has been read.

Argument FUNCTION is a string the string will be written in the buffer
on start of the command. If it is a symbol it searches for the corresponding
command name in lyskom-commands and writes this in the message buffer.

If optional argument MAY-INTERRUPT is present and non-nil,
don't signal an error if this call is interrupting another command.

If optional DEAD-OK is non-nil, don't signal an error if the session 
has been closed.

If optional PROMPT-FORMAT is non-nil, this will be used to format the prompt.
It should be a format string suitable for lyskom-format. Argument one is the
command name (a symbol or string).

Special: if lyskom-is-waiting then we are allowed to break if we set 
lyskom-is-waiting nil.

This function checks if lyskom-doing-default-command and
lyskom-first-time-around are bound. The text entered in the buffer is
chosen according to this"

  (cond ((or (not (boundp 'lyskom-proc))
             (not (boundp 'lyskom-buffer))
             (and (null lyskom-proc) (null lyskom-buffer)))
         (lyskom-error "%s" (lyskom-get-string 'not-lyskom-buffer)))

        ((and (not (or dead-ok
		       (memq function kom-relogin-inhibit-commands)))
              (or (not lyskom-proc)
                  (memq (process-status lyskom-proc) '(closed signal exited nil))))
	 (if (or (eq t kom-relogin-behaviour)
		 (and (eq 'ask kom-relogin-behaviour) 
		      (lyskom-j-or-n-p (lyskom-get-string 'resurrect-session))))
	     (lyskom (if (= 4894 lyskom-server-port) ; Should we do this different?
			 lyskom-server-name
		       (concat lyskom-server-name 
			       ":" 
			       (int-to-string lyskom-server-port)))
		     lyskom-pers-no
		     lyskom-default-password)
      (lyskom-error "%s" (lyskom-get-string 'dead-session)))))

  (if (and lyskom-is-waiting
           (listp lyskom-is-waiting))
      (progn
        (setq lyskom-is-waiting nil)
        (lyskom-end-of-command)))

  (setq lyskom-is-waiting nil)
  (if (and lyskom-executing-command (not may-interrupt))
      (lyskom-error "%s" (lyskom-get-string 'wait-for-prompt)))
  (if (not (and (boundp 'lyskom-doing-default-command)
                lyskom-doing-default-command))
      (cond
       (lyskom-first-time-around)
       ((stringp function) (lyskom-insert function))
       ((lyskom-command-name function)
        (if prompt-format 
            (lyskom-format-insert prompt-format function)
          (lyskom-insert (lyskom-command-name function)))))
    (save-excursion
      (if lyskom-current-prompt
          (let ((inhibit-read-only t))
            (goto-char (point-max))
            (beginning-of-line)
            (delete-region (point) (point-max)))))
    (lyskom-insert (lyskom-modify-prompt 
                    (cond ((stringp lyskom-current-prompt) 
                           (apply 'lyskom-format 
                                  lyskom-current-prompt
                                  lyskom-current-prompt-args))
                          ((symbolp lyskom-current-prompt)
                           (apply 'lyskom-format 
                                  (lyskom-get-string lyskom-current-prompt)
                                  lyskom-current-prompt-args))
                          (t (format "%S" lyskom-current-prompt)))
                    t)))
  (setq mode-line-process (lyskom-get-string 'mode-line-working))
  (if (lyskom-pos-visible-in-window-p (point-max))
      (save-excursion
        (goto-char (point-max))
        (lyskom-set-last-viewed)))
  (setq lyskom-executing-command t)
  (setq lyskom-current-command function)
  (setq lyskom-current-prompt nil)
  (lyskom-insert "\n")
  (if (and (eq (window-buffer (selected-window))
               (current-buffer))) 
      (progn
	(if (lyskom-pos-visible-in-window-p (1- (point-max)))
	    (goto-char (point-max)))
	(sit-for 0)))
  (run-hooks 'lyskom-before-command-hook)
  (if kom-page-before-command           ;Nice with dumb terminals.
      (if (or (not (listp kom-page-before-command))
              (memq function kom-page-before-command))
          (recenter 1))))


(defun lyskom-end-of-command ()
  "Print prompt, maybe scroll, prefetch info."
  (lyskom-save-excursion
    (message "")
    (lyskom-clean-all-buffer-lists)
    (while (and lyskom-to-be-printed-before-prompt
                (lyskom-queue->first lyskom-to-be-printed-before-prompt))
      (if (not (bolp)) (lyskom-insert "\n"))
      (lyskom-insert (car (lyskom-queue->first 
                           lyskom-to-be-printed-before-prompt)))
      (lyskom-queue-delete-first lyskom-to-be-printed-before-prompt))
    (setq lyskom-executing-command nil)
    (setq lyskom-current-command nil)
    (setq lyskom-current-prompt nil)	; Already set in s-o-c really
    (lyskom-scroll)
    (setq mode-line-process (lyskom-get-string 'mode-line-waiting))
    (if (lyskom-pos-visible-in-window-p (point-max) (selected-window))
        (lyskom-set-last-viewed))
    (lyskom-prefetch-and-print-prompt)
    (run-hooks 'lyskom-after-command-hook)
    (when (and (lyskom-have-feature idle-time)
               (not lyskom-is-anonymous))
      (lyskom-with-lyskom-buffer
        (initiate-user-active 'background nil)))
    (if kom-inhibit-typeahead
        (discard-input))
    ;; lyskom-pending-commands should probably be a queue or a stack.
    (when lyskom-pending-commands
      (let ((command (car lyskom-pending-commands)))
        (setq lyskom-pending-commands (cdr lyskom-pending-commands))
        (if (symbolp command)
            (call-interactively command)
          (eval command))))
    (when lyskom-slow-mode
      (buffer-enable-undo))))

(eval-and-compile (provide 'lyskom-command))

;;; command.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: buffers.el
;;;; Author: David Byers
;;;;
;;;; This file implements special buffer handling used in LysKOM
;;;;

;;;;
;;;; LYSKOM BUFFER MANAGEMENT
;;;;
;;;; Buffers are arranged in a tree rooted in a LysKOM buffer. There
;;;; is one tree for each session.
;;;;
;;;; Use lyskom-get-buffer-create to create new buffers
;;;; Use lyskom-display-buffer to display most buffers
;;;; Use lyskom-undisplay-buffer to undisplay those buffers
;;;;





;;;; ============================================================
;;;; Buffer hierarchy management
;;;;
;;;; This code should not be too dependent on LysKOM
;;;;

(defvar lyskom-buffer-children nil
  "List of buffers that are children to this buffer")

(defvar lyskom-buffer-inherited-variables nil
  "List of variables automatically inherited to sub-buffers when
they are created.")

(defvar lyskom-buffer-parent nil
  "Parent of buffer")

(defvar lyskom-killing-hierarchy nil
  "Non-nil while killing a buffer hierarchy.")

(make-variable-buffer-local 'lyskom-buffer-parent)
(lyskom-protect-variable 'lyskom-buffer-parent)

(make-variable-buffer-local 'lyskom-buffer-children)
(lyskom-protect-variable 'lyskom-buffer-children)

(make-variable-buffer-local 'lyskom-buffer-inherited-variables)
(lyskom-protect-variable 'lyskom-buffer-inherited-variables)




(defun lyskom-set-buffer-parent (buffer parent)
  "Set the parent buffer of BUFFER to PARENT. If buffer is already
a child of some buffer, reparent it."
  (save-current-buffer
    (set-buffer buffer)
    (if (and lyskom-buffer-parent
             (buffer-live-p lyskom-buffer-parent))
        (lyskom-remove-buffer-child lyskom-buffer-parent buffer))
    (setq lyskom-buffer-parent parent)
    (if parent (lyskom-add-buffer-child parent buffer))))

(defun lyskom-remove-buffer-child (buffer child)
  "Remove CHILD from BUFFER's list of children. Args: BUFFER CHILD"
  (save-current-buffer (set-buffer buffer)
                       (if (boundp 'lyskom-buffer-children)
                           (setq lyskom-buffer-children 
                                 (delq child lyskom-buffer-children)))))

(defun lyskom-add-buffer-child (buffer child)
  "Add CHILD as a child of BUFFER. Args: BUFFER CHILD"
  (save-current-buffer (set-buffer buffer)
                       (setq lyskom-buffer-children
                             (cons child lyskom-buffer-children))))

(defun lyskom-get-buffer-parent (buffer)
  "Return the parent of BUFFER or nil if it has no parent"
  (save-current-buffer (set-buffer buffer)
                       (and (boundp 'lyskom-buffer-parent)
                            lyskom-buffer-parent)))

(defun lyskom-get-buffer-children (buffer)
  "Return the list of children of buffer BUFFER or nil if there are none."
  (save-current-buffer (set-buffer buffer)
                       (and (boundp 'lyskom-buffer-children)
                            lyskom-buffer-children)))

(defun lyskom-buffer-root-ancestor (buffer)
  "Return the ultimate ancestor of buffer BUFFER."
  (let ((buffer-parent buffer))
    (while (setq buffer-parent
		 (lyskom-get-buffer-parent buffer))
      (setq buffer buffer-parent))
    buffer))

(defun lyskom-traverse-buffer-hierarchy (function buffer)
  "Apply FUNCTION to each descendent of BUFFER. Results are discarded."
  (let ((queue (list buffer))
        (done nil)
        (current nil))
    (while queue
      (setq current (car queue))
      (setq queue (cdr queue))
      (unless (or (memq current done)
                  (not (buffer-live-p current)))
        (setq done (cons current done))
        (setq queue (append queue (lyskom-get-buffer-children current)))
        (funcall function current)))))


(defun lyskom-map-buffer-children (function buffer)
  "Apply FUNCTION to each child of BUFFER and make a list of the results."
  (cond ((null buffer) nil)
	(t (cons (funcall function buffer)
		 (apply 'nconc 
			(mapcar (function
				 (lambda (x)
                                   (lyskom-map-buffer-children function x)))
				(lyskom-get-buffer-children buffer)))))))


(defun lyskom-buffer-hierarchy-query-kill-function ()
  "When querying if a buffer is to be killed, ensure that none of
the children object"
  (save-excursion
    (not (memq nil
               (mapcar (function
                        (lambda (buffer)
                          (if (buffer-live-p buffer)
                              (progn (set-buffer buffer)
                                     (run-hook-with-args-until-failure
                                      'kill-buffer-query-functions))
                            t)))
                       (lyskom-get-buffer-children (current-buffer)))))))
  

(defun lyskom-buffer-hierarchy-kill-hook ()
  "When killing a buffer, enure that its children also die"
  (let ((kill-buffer-query-functions nil)
        (lyskom-killing-hierarchy (or lyskom-killing-hierarchy
                                      (current-buffer))))
    (lyskom-set-buffer-parent (current-buffer) nil)
    (let ((buflist (lyskom-get-buffer-children (current-buffer))))
      (while buflist
        (kill-buffer (car buflist))
        (setq buflist (cdr buflist))))))


(defun lyskom-buffer-kill-trim-hook ()
  "When killing a buffer, run trimming hooks."
  (when (eq major-mode 'lyskom-mode)
    (let ((lyskom-trim-buffer-delete-to (point-max)))
      (lyskom-ignore lyskom-trim-buffer-delete-to)
      (run-hooks 'lyskom-trim-buffer-hook))))

(add-hook 'kill-buffer-hook 'lyskom-buffer-hierarchy-kill-hook)
(add-hook 'kill-buffer-hook 'lyskom-buffer-kill-trim-hook)
(add-hook 'kill-buffer-query-functions
          'lyskom-buffer-hierarchy-query-kill-function)

;;;; ======================================================================

(defun lyskom-update-unread-mode-line ()
  (setq lyskom-unread-mode-line 
	(lyskom-make-lyskom-unread-mode-line)))

;;;; ======================================================================
;;;; ======================================================================

(defun lyskom-clean-all-buffer-lists ()
  "Remove dead buffers from all relevant buffer lists"
  (setq lyskom-sessions-with-unread
        (lyskom-clean-buffer-list lyskom-sessions-with-unread)
        lyskom-sessions-with-unread-letters
        (lyskom-clean-buffer-list lyskom-sessions-with-unread-letters)
        lyskom-buffer-list
        (lyskom-clean-buffer-list lyskom-buffer-list))
  (lyskom-update-unread-mode-line)
  (lyskom-set-default 'lyskom-need-prompt-update t))

(defun lyskom-clean-buffer-list (buffers)
  "Remove all dead buffers from BUFFERS"
  (let ((result nil))
    (while buffers
      (if (lyskom-buffer-p (car buffers))
        (setq result (cons (car buffers) result))
        (save-current-buffer
          (set-buffer (car buffers))
          (setq lyskom-session-has-unread-letters nil)
          (setq lyskom-session-has-unreads nil)))
      (setq buffers (cdr buffers)))
    (nreverse result)))

(defun lyskom-remove-buffer-from-lists (&optional buffer)
    "Remove BUFFER from all internal lists.
If BUFFER is not specified, assume the current buffer"
  (unless buffer (setq buffer (current-buffer)))
  (save-match-data
    (lyskom-remove-unread-buffer buffer)  
    (setq lyskom-buffer-list
	  (delq buffer lyskom-buffer-list))
    (lyskom-update-unread-mode-line)
    (lyskom-set-default 'lyskom-need-prompt-update t)))

(defun lyskom-remove-unread-buffer (buffer &optional letters-only)
  (unless letters-only
    (lyskom-traverse-buffer-hierarchy 
     (lambda (x)
       (save-current-buffer (set-buffer x)
                            (setq lyskom-session-has-unreads nil)))
     buffer)
    (setq lyskom-sessions-with-unread 
          (delq buffer lyskom-sessions-with-unread)))
  (lyskom-traverse-buffer-hierarchy 
   (lambda (x)
     (save-current-buffer (set-buffer x)
                          (setq lyskom-session-has-unread-letters nil)))
   buffer)
  (setq lyskom-sessions-with-unread-letters
        (delq buffer lyskom-sessions-with-unread-letters))
  (lyskom-update-unread-mode-line)
  (lyskom-set-default 'lyskom-need-prompt-update t))
  

(defun lyskom-add-unread-buffer (buffer &optional letters)
  (unless (memq buffer lyskom-sessions-with-unread)
    (lyskom-traverse-buffer-hierarchy 
     (lambda (x)
       (save-current-buffer (set-buffer x)
                            (setq lyskom-session-has-unreads t)))
     buffer)
    (setq lyskom-sessions-with-unread
          (cons buffer lyskom-sessions-with-unread)))
  (unless (or (null letters)
              (memq buffer lyskom-sessions-with-unread-letters))
    (lyskom-traverse-buffer-hierarchy 
     (lambda (x)
       (save-current-buffer (set-buffer x)
                            (setq lyskom-session-has-unread-letters t)))
     buffer)
    (setq lyskom-sessions-with-unread-letters
          (cons buffer lyskom-sessions-with-unread-letters)))
  (lyskom-update-unread-mode-line) 
  (lyskom-set-default 'lyskom-need-prompt-update t))

(defvar lyskom-associated-buffer-list nil
  "List of (CATEGORY . BUFFER-LIST) listing all buffers of various
categories")

(make-variable-buffer-local 'lyskom-associated-buffer-list)
(lyskom-protect-variable 'lyskom-associated-buffer-list)

(defvar lyskom-buffer-category nil
  "Category of this buffer")

(make-variable-buffer-local 'lyskom-buffer-category)
(lyskom-protect-variable 'lyskom-buffer-category)

(def-kom-var lyskom-saved-window-configuration nil
  "The window configuration to return to when closing the window"
  protected
  local)

(def-kom-var lyskom-dedicated-frame nil
  "The frame dedicated to the current buffer"
  protected
  local)

(def-kom-var lyskom-dedicated-window nil
  "The window dedicated to the current buffer"
  protected
  local)

(defvar lyskom-undisplaying-hierarchy nil
  "The top of the buffer hierarchy being undisplayed.")



(defun lyskom-clean-up-buffer-category (cat)
  (let ((buffers (cdr (assq cat (lyskom-default-value 'lyskom-associated-buffer-list))))
        (result nil))
    (while buffers
      (when (buffer-live-p (car buffers))
        (setq result (cons (car buffers) result)))
      (setq buffers (cdr buffers)))
    (lyskom-set-buffers-of-category cat (nreverse result))))


(defun lyskom-set-buffers-of-category (category buflist)
  (let ((tmp (assq category (lyskom-default-value 'lyskom-associated-buffer-list))))
    (cond (tmp (setcdr tmp buflist))
          (t (lyskom-setq-default 
              lyskom-associated-buffer-list
              (cons (cons category buflist)
                    (lyskom-default-value 'lyskom-associated-buffer-list)))))))


(defun lyskom-buffers-of-category (cat)
  "Return all live buffers of catgory CAT"
  (lyskom-clean-up-buffer-category cat)
  (cdr (assq cat (lyskom-default-value 'lyskom-associated-buffer-list))))


(defun lyskom-add-buffer-of-category (buffer category)
  "Add BUFFER as a buffer of category CATEGORY"
  (let ((tmp (assq category (lyskom-default-value 'lyskom-associated-buffer-list))))
    (cond (tmp (setcdr tmp (cons buffer (cdr tmp))))
          (t (lyskom-setq-default 'lyskom-associated-buffer-list
                                  (cons (cons category (list buffer))
                                        (lyskom-default-value
                                         'lyskom-associated-buffer-list)))))))


(defun lyskom-quit-query ()
  (if (and (boundp 'lyskom-buffer)
           (local-variable-p 'lyskom-buffer (current-buffer))
             (eq lyskom-buffer (current-buffer))
             (lyskom-buffers-of-category 'write-texts))
    (unwind-protect
        (progn
          (display-buffer (car (lyskom-buffers-of-category 'write-texts)))
          (lyskom-ja-or-nej-p (lyskom-get-string 'quit-in-spite-of-unsent)))
      nil)
    t))

(add-hook 'kill-buffer-query-functions 'lyskom-quit-query)
(add-hook 'kill-emacs-query-functions 'lyskom-quit-query)

(defun lyskom-recode-buffer-name (name)
  (cond ((and (lyskom-multibyte-string-p name)
              (not enable-multibyte-characters))
	 (lyskom-encode-coding-string name 
			       (or (and lyskom-language
					(lyskom-language-coding
					 lyskom-language))
				   'raw-text)))
	((and (not (lyskom-multibyte-string-p name)) 
              enable-multibyte-characters)
	 (lyskom-decode-coding-string name 
                                      (or (and lyskom-language
                                               (lyskom-language-coding
                                                lyskom-language))
                                          'raw-text)))
	(t name)))

(defun lyskom-generate-new-buffer (name)
  (setq name (lyskom-recode-buffer-name name))
  (let ((buf (generate-new-buffer name)))
    (save-current-buffer
      (set-buffer buf))
    buf))

(defun lyskom-get-buffer-create (category name &optional unique)
  "Create a new buffer of category CATEGORY with name generated from NAME. 
If UNIQUE is non-nil, re-use the first existing buffer of category
CATEGORY, renaming it and killing its local variables.

The created buffer is made a child of the current buffer."
  (setq name (lyskom-recode-buffer-name name))
  (let ((buffers (lyskom-buffers-of-category category))
        (buffer nil))
    (if (and unique buffers)
        (progn (setq buffer (car buffers))
               (save-current-buffer (set-buffer buffer)
                                    (let ((inhibit-read-only t))
;;; +++ FIXME: This is that erase-buffer works if there are widgets
                                      (setq before-change-functions
                                            (delq 'widget-before-change
                                                  before-change-functions))
                                      (erase-buffer))
                                    (kill-all-local-variables)
                                    (if (equal (buffer-name (current-buffer))
                                               name)
                                        nil
                                      (rename-buffer name t))))
      (progn (setq buffer (generate-new-buffer name))
             (lyskom-add-buffer-of-category buffer category)))
    (lyskom-set-buffer-parent buffer (current-buffer))
    (lyskom-update-inherited-variables buffer)
    (save-current-buffer (set-buffer buffer)
                         (setq lyskom-buffer-category category))
    buffer))



(defun lyskom-update-inherited-variables (buffer)
  "Update all inherited variables in this buffer and propagate them
to all children"
  (save-excursion 
    (let ((variables nil)
          (tmp lyskom-inherited-variables))
      (set-buffer buffer)
      (when lyskom-buffer-parent
        (set-buffer lyskom-buffer-parent)
        (setq variables (mapcar 'symbol-value lyskom-inherited-variables))
        (set-buffer buffer)
        (while tmp
          (make-local-variable (car tmp))
          (set (car tmp) (car variables))
          (setq tmp (cdr tmp)
                variables (cdr variables))))
      (mapcar 'lyskom-update-inherited-variables
              lyskom-buffer-children))))



(defun lyskom-display-buffer (buffer &optional keep-frame-iconified)
  "Display the buffer BUFFER and select the window displaying it.

If BUFFER is already visible in some window in any frame, iconified or
otherwise, make that window visible. Otherwise display buffer as per 
which category it is in. Selects the window.

If optional argument KEEP-FRAME-ICONIFIED is non-nil, then don't
deiconify any frames.

Returns the window displaying BUFFER."


  (set-buffer buffer)
  (let ((windows (lyskom-get-buffer-window-list buffer nil 0))
        (iconified-frame nil)
        (visible-frame))

    ;;
    ;; Find out if the buffer is visible somewhere
    ;;

    (while windows
      (cond ((not (frame-live-p (window-frame (car windows)))))
            ((eq (frame-visible-p (window-frame (car windows))) 'icon)
             (setq iconified-frame (car windows)))
            ((frame-visible-p (window-frame (car windows)))
             (setq visible-frame (car windows))
             (setq windows nil)))
      (setq windows (cdr windows)))

    ;;
    ;; Display it
    ;;

    (cond
     (visible-frame (select-window visible-frame)
                    visible-frame)
     (iconified-frame (unless keep-frame-iconified 
                        (make-frame-visible (window-frame iconified-frame)))
                      (select-window iconified-frame)
                      iconified-frame)

     (t
      (let ((category lyskom-buffer-category)
            (window nil))
        (if (null category) 
            (progn (select-window (display-buffer buffer))
                   (selected-window))

          (let* ((sym (intern-soft (concat "kom-"
                                           (symbol-name category)
                                           "-in-window")))
                 (open (lyskom-default-value-safe sym))
                 (saved-window-configuration
                  (save-current-buffer
                    (set-buffer (or (and (boundp 'lyskom-buffer)
                                         lyskom-buffer)
                                    (current-buffer)))
                    (current-window-configuration)))
                 (dedicated-frame nil)
                 (dedicated-window nil))
            
            (cond 

             ;;
             ;; NULL -- Just switch to the buffer
             ;;

             ((null open)
              (switch-to-buffer buffer)
              (setq window (selected-window)))

             ;;
             ;; OTHER, OTHER-WINDOW -- Switch to in another window
             ;;

             ((memq open '(other other-window))
              (switch-to-buffer-other-window buffer)
              (setq dedicated-window (selected-window))
              (setq window (selected-window)))
                  
             ;;
             ;; OTHER-FRAME -- Switch to in another frame
             ;; Create frame if none exist
             ;;

             ((eq open 'other-frame)
              (if (eq (selected-frame) (next-frame))
                  (switch-to-buffer-other-frame buffer)
                (other-frame 1)
                (switch-to-buffer buffer))
              (setq window (selected-window)))
                  
             ;;
             ;; NEW-FRAME
             ;; Create a new frame and display buffer in that frame
             ;;

             ((eq open 'new-frame)
              (switch-to-buffer-other-frame buffer)
              (setq dedicated-frame (selected-frame))
              (setq dedicated-window (selected-window))
              (setq window (selected-window))
              (setq saved-window-configuration nil))

             ;;
             ;; String or buffer
             ;; Switch to buffer in window displaying named buffer
             ;; Prefer windows in selected frame
             ;;

             ((and (or (stringp open)
                       (bufferp open))
                   (lyskom-get-buffer-window-list open nil 'visible))
              (let ((tmp (lyskom-get-buffer-window-list open nil 'visible)))
                (setq window (car tmp))
                (while tmp
                  (if (eq (window-frame (car tmp))
                          (selected-frame))
                      (progn
                        (setq window (car tmp))
                        (setq tmp nil))
                    (setq tmp (cdr tmp))))
                (select-window window)
                (switch-to-buffer buffer)))

             ;;
             ;; Otherwise just switch
             ;;
             
             (t (switch-to-buffer buffer)
                (setq window (selected-window))))


            ;;
            ;; Set up kill-buffer-hooks and similar things
            ;;

            (select-window window)
            (set-buffer buffer)
            (make-local-variable 'lyskom-dedicated-frame)
            (setq lyskom-dedicated-frame dedicated-frame)
            (make-local-variable 'lyskom-dedicated-window)
            (setq lyskom-dedicated-window dedicated-window)
            (make-local-variable 'lyskom-saved-window-configuration)
            (setq lyskom-saved-window-configuration
                  saved-window-configuration)
            (lyskom-make-local-hook 'kill-buffer-hook)
            (add-hook 'kill-buffer-hook
                      'lyskom-undisplay-buffer-hook nil t)
            (put 'kill-buffer-hook 'permanent-local t)
            window)))))))


(defun lyskom-undisplay-buffer-hook ()
  (save-excursion (lyskom-undisplay-buffer (current-buffer))))

(defun lyskom-undisplay-buffer (&optional buffer)
  "Undisplay BUFFER. If buffer is not specified, undisplay the current
buffer"

  (setq buffer (or buffer (current-buffer)))
  (let ((dedicated-frame (and (boundp 'lyskom-dedicated-frame)
                              lyskom-dedicated-frame))
        (dedicated-window (and (boundp 'lyskom-dedicated-window)
                                       lyskom-dedicated-window))
        (lyskom-undisplaying-hierarchy (or buffer
                                           lyskom-undisplaying-hierarchy))
        (saved-window-configuration
         (and (boundp 'lyskom-saved-window-configuration)
              lyskom-saved-window-configuration)))
         
    (cond 

     ;;
     ;; If buffer has a dedicated frame AND
     ;; buffer is visible in that frame THEN
     ;; delete the frame
     ;;

     (dedicated-frame
      (when (memq dedicated-frame
                  (mapcar 'window-frame
                          (lyskom-get-buffer-window-list buffer nil t)))
        (delete-frame dedicated-frame)
        (setq dedicated-frame nil)))

     ((and lyskom-killing-hierarchy
           (not (eq lyskom-killing-hierarchy buffer))) nil)
     ((and lyskom-undisplaying-hierarchy
           (not (eq lyskom-undisplaying-hierarchy buffer))) nil)

     (dedicated-window
      (when (and (window-live-p dedicated-window)
                 (eq (get-buffer buffer)
                     (window-buffer dedicated-window)))
	(if (eq dedicated-window (next-window dedicated-window))
	    (bury-buffer buffer)
	    (delete-window dedicated-window))
	(setq dedicated-window nil))
      (when saved-window-configuration
        (lyskom-set-partial-window-configuration
         saved-window-configuration)))

     (saved-window-configuration
      (lyskom-set-partial-window-configuration
       lyskom-saved-window-configuration)))))

(defun lyskom-set-partial-window-configuration (configuration)
  "Set CONFIGURATION as the current window configuration with the exception
of point mark and window-start in all windows."
  (let* ((info nil)
         (binfo nil))
    (save-excursion
      (setq binfo
            (mapcar (function
                     (lambda (b)
                       (set-buffer b)
                       (list b (point) (mark t))))
                    (buffer-list)))
      (walk-windows (function
                     (lambda (w)
                       (set-buffer (window-buffer w))
                       (setq info
                             (cons
                              (list w
                                    (window-start w)
                                    (window-point w)
                                    (window-buffer w))
                              info))))
                    t t))
    (unwind-protect
        (set-window-configuration configuration)
      (save-selected-window
        (let (buffer window saved-point saved-mark saved-start)

          (while binfo
            (setq buffer (elt (car info) 0)
                  saved-point (elt (car info) 1)
                  saved-mark (elt (car info) 2))
            (when (buffer-live-p buffer)
              (set-buffer buffer)
              (goto-char saved-point)
              (set-mark saved-mark))
            (setq binfo (cdr binfo)))

          (while info
            (setq window (elt (car info) 0)
                  saved-start (elt (car info) 1)
                  saved-point (elt (car info) 2)
                  buffer (elt (car info) 3))

            (when (and (window-live-p window)
                       (eq (window-buffer window)
                           buffer))
              (set-window-start window saved-start)
              (set-window-point window saved-point))
            (setq info (cdr info))))))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: aux-items.el
;;;;
;;;; Implementation of aux-item specific stuff.
;;;; Hopefulle more of this will be made more general in the future.
;;;;

(def-kom-var lyskom-aux-item-definitions nil
  "List of aux item definitions.")

(defmacro def-aux-item (name number &rest data)
  ` (setq lyskom-aux-item-definitions 
           (add-to-list 'lyskom-aux-item-definitions
                        (cons ,number
                              (cons (cons 'name (quote ,name))
                                    (quote ,data))))))

(defun lyskom-aux-item-definition->name (def)
  (cdr (assq 'name def)))

(defun lyskom-aux-item-definition->number (def)
  (car def))


(defun lyskom-aux-item-try-call (item method default &rest args)
  (when (lyskom-aux-item-p item)
    (setq item (lyskom-find-aux-item-definition item)))
  (cond ((listp method)
         (let ((result nil)
               (found nil))
           (while method
             (when (lyskom-aux-item-definition-field item (car method))
               (setq result
                     (apply (lyskom-aux-item-definition-field item
                                                              (car method))
                            args)
                     found t
                     method nil))
             (setq method (cdr method)))
           (if found result default)))
        (t (if (lyskom-aux-item-definition-field item method)
             (apply (lyskom-aux-item-definition-field item method) args)
             default))))

(defun lyskom-aux-item-has-call (item method)
  "Return non-nil if aux-item ITEM has a defined method METHOD"
  (when (lyskom-aux-item-p item)
    (setq item (lyskom-find-aux-item-definition item)))
  (lyskom-aux-item-definition-field item method))

(defun lyskom-aux-item-call (def method &rest args)
  (apply 'lyskom-aux-item-try-call def method nil args))


(defun lyskom-aux-item-definition-field (def method)
  (when (lyskom-aux-item-p def)
    (setq def (lyskom-find-aux-item-definition def)))
  (cdr (assq method def)))


(defun lyskom-find-aux-item-definition (aux)
  (assq (aux-item->tag aux) lyskom-aux-item-definitions))

(defun lyskom-match-aux-items (item-list predicate)
  "Return a list of all aux-items in ITEM-LIST that match PREDICATE.
PREDICATE should receives a single aux-item as its argument and should
return non-nil if the item is to be included in the list."
  (let ((result nil))
    (while item-list
      (when (funcall predicate (car item-list))
        (setq result (cons (car item-list) result)))
      (setq item-list (cdr item-list)))
    (nreverse result)))

(defun lyskom-get-aux-item (item-list tag)
  "Return all aux-items in ITEM-LIST with tag TAG."
  (lyskom-match-aux-items item-list
                          (lambda (el) (eq (aux-item->tag el) tag))))


(defun lyskom-aux-item-terminating-button (item obj)
  (if obj
       (lyskom-format 
        " %#1@%[[*]%]" 
        (lyskom-default-button 
         'aux 
         (cond ((lyskom-text-stat-p obj) (list 'text
                                               (text-stat->text-no obj)
                                               (aux-item->aux-no item)))
               ((lyskom-conf-stat-p obj) (list 'conf
                                               (conf-stat->conf-no obj)
                                               (aux-item->aux-no item)))
               ((eq obj 'server) (list 'server nil (aux-item->aux-no item)))
               (t item))))
     ""))

(defun lyskom-aux-item-after-parse (item)
  (lyskom-aux-item-try-call item 'decode-data nil)
  item)

(defun lyskom-aux-item-output-data (item)
  (lyskom-aux-item-try-call item 
                            'encode-data
                            (aux-item->data item)
			    item))

(defun lyskom-aux-item-modify-list (item-list deleted added)
  "Return a copy of ITEM-LIST with items in DELETED removed and items
in ADDED added."
  (let ((new-nos (nconc (mapcar 'aux-item->aux-no deleted)
                        (mapcar 'aux-item->aux-no added))))
    (nconc (lyskom-filter-list (lambda (el) (not (memq (aux-item->aux-no el) new-nos)))
                        item-list)
           added)))

(defun lyskom-aux-item-validate (data &rest tests)
  "Validata aux-item data.
DATA is data to validate. TESTS are the tests to use.
A test can be a function or a regular expression to match.
Invalid tests are silently ignored."
  (not (lyskom-traverse test tests
         (condition-case nil
             (unless
                 (cond ((stringp test) (string-match test data))
                       ((functionp test) (funcall test data)))
               (lyskom-traverse-break t))
           (error (lyskom-traverse-break t))))))


;;; ======================================================================

(def-aux-item content-type 1
  (text-name aux-content-type-name)
  (parse . lyskom-parse-content-type)
  (text-print . lyskom-print-content-type)
  (info  . lyskom-aux-item-info))

(def-aux-item fast-reply 2
  (text-name aux-fast-reply-name)
  (parse . nil)
  (parse-data . lyskom-aux-item-decode-data)
  (encode-data . lyskom-aux-item-encode-data)
  (info . lyskom-aux-item-info))

(def-aux-item cross-reference 3
  (text-name aux-cross-reference-name)
  (status-print . lyskom-status-print-cross-reference)
  (text-print-when . comment)
  (parse . lyskom-parse-cross-reference)
  (parse-data . lyskom-aux-item-decode-data)
  (encode-data . lyskom-aux-item-encode-data)
  (text-print . lyskom-print-cross-reference)
  (edit-insert . lyskom-edit-insert-cross-reference)
  (info  . lyskom-aux-item-info))

(def-aux-item no-comments 4
  (text-name aux-no-comments-name)
  (text-print-when . footer)
  (parse . lyskom-parse-no-comments)
  (text-print . lyskom-print-no-comments)
  (edit-insert . lyskom-edit-insert-no-comments)
  (info  . lyskom-aux-item-info))

(def-aux-item personal-comment 5
  (text-name aux-personal-comment-name)
  (text-print-when . footer)
  (parse . lyskom-parse-personal-comments)
  (text-print . lyskom-print-personal-comments)
  (edit-insert . lyskom-edit-insert-personal-comments)
  (info  . lyskom-aux-item-info))

(def-aux-item request-confirmation 6
  (text-name aux-request-confirmation-name)
  (text-print-when . header)
  (parse . lyskom-parse-request-confirmation)
  (text-print . lyskom-print-request-confirmation)
  (edit-insert . lyskom-edit-insert-request-confirmation)
  (info  . lyskom-aux-item-info)
  (read-action . lyskom-request-confirmation-action))

(def-aux-item read-confirm 7
  (text-name aux-read-confirm-name)
  (text-print-when . header)
  (text-print . lyskom-print-read-confirm)
  (info  . lyskom-aux-item-info))

(def-aux-item redirect 8
  (text-name aux-redirect-name)
  (print . lyskom-print-redirect)
  (info  . lyskom-aux-item-info))

(def-aux-item x-face 9
  (text-name aux-x-face-name)
  (print . lyskom-print-xface)
  (info  . lyskom-aux-item-info))

(def-aux-item alternate-name 10
  (text-name aux-alternate-name-name)
  (text-print-when . header)
  (text-print . lyskom-print-alternate-name)
  (encode-data . lyskom-aux-item-encode-data)
  (parse-data . lyskom-aux-item-decode-data)
  (info  . lyskom-aux-item-info))

(def-aux-item pgp-signature 11
  (text-name aux-pgp-signature-name)
  (info  . lyskom-aux-item-info))

(def-aux-item pgp-public-key 12
  (text-name aux-pgp-public-key-name)
  (info  . lyskom-aux-item-info))

(def-aux-item e-mail-address 13
  (text-name aux-e-mail-address-name)
  (info  . lyskom-aux-item-info))

(def-aux-item faq-text 14
  (text-name aux-faq-text-name)
  (info . lyskom-aux-item-info)
  (text-header-line . (faq-in-text faq-in-text-by))
  (status-print . lyskom-status-print-faq-text))

(def-aux-item creating-software 15
  (text-name aux-creating-software-name)
  (info . lyskom-aux-item-info)
  (text-print-when . header)
  (parse-data . lyskom-aux-item-decode-data)
  (encode-data . lyskom-aux-item-encode-data)
  (text-print . lyskom-print-creating-software))

(def-aux-item mx-author 16
  (text-name aux-mx-author-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-from 17
  (text-name aux-mx-from-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-reply-to 18
  (text-name aux-mx-reply-to-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-to 19
  (text-name aux-mx-to-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-cc 20
  (text-name aux-mx-cc-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-date 21
  (text-name aux-mx-date-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-message-id 22
  (text-name aux-mx-message-id-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-in-reply-to 23
  (text-name aux-mx-in-reply-to-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-misc 24
  (text-name aux-mx-misc-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-allow-filter 25
  (text-name aux-mx-allow-filter-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-reject-forward 26
  (text-name aux-mx-reject-forward-name)
  (info . lyskom-aux-item-info))

(def-aux-item notify-comments 27
  (text-name aux-notify-comments-name)
  (info . lyskom-aux-item-info))

(def-aux-item faq-for-conf 28
  (text-name aux-faq-for-conf-name)
  (text-print . lyskom-print-faq-for-conf)
  (text-print-when . header)
  (info . lyskom-aux-item-info)
  (read-action . lyskom-faq-for-conf-action))


(def-aux-item recommended-conf 29
  (text-name aux-recommended-conf-name)
  (status-print . lyskom-print-recommended-conf)
  (info . lyskom-aux-item-info))

(def-aux-item allowed-content-type 30
  (text-name aux-allowed-content-type-name)
  (info . lyskom-aux-item-info))

(def-aux-item canonical-name 31
  (text-name aux-canonical-name-name)
  (info . lyskom-aux-item-info))

(def-aux-item mx-list-name 32
  (text-name aux-mx-list-name-name)
  (info . lyskom-aux-item-info)
  (status-print . lyskom-print-mx-list-name))

(def-aux-item send-comments-to 33
  (text-name aux-send-comments-to-name)
  (info . lyskom-aux-item-info)
  (status-print . lyskom-print-send-comments-to))

(def-aux-item world-readable 34
  (text-name aux-world-readable-name)
  (info . lyskom-aux-item-info)
  (text-print . lyskom-print-world-readable)
  (parse . lyskom-parse-world-readable)
  (edit-insert . lyskom-edit-insert-world-readable)
  (text-print-when . header))

(def-aux-item mx-refuse-import 35
  (text-name aux-mx-refuse-import)
  (info . lyskom-aux-item-info)
  (status-print . lyskom-print-mx-refuse-import))

(def-aux-item elisp-client-read-faq 10000
  (text-name aux-elisp-client-read-faq-name)
  (info . lyskom-aux-item-info)
  (status-print . lyskom-print-elisp-client-read-faq))

(def-aux-item elisp-client-rejected-invitation 10001
  (text-name aux-elisp-client-rejected-invitation-name)
  (info . lyskom-aux-item-info)
  (status-print . lyskom-print-elisp-client-rejected-invitation))

(def-aux-item elisp-client-enriched-headers 10002)

(def-aux-item mx-allow-envelope-sender-regexp 10105
  (text-name aux-mx-allow-envelope-sender-regexp)
  (info . lyskom-aux-item-info)
  (status-print . lyskom-print-mx-allow-envelope-sender-regexp))



;;; ================================================================



(defun lyskom-aux-item-decode-data (item)
  (set-aux-item->data 
   item
   (lyskom-decode-coding-string (aux-item->data item) lyskom-server-coding-system)))

(defun lyskom-aux-item-encode-data (item)
  (lyskom-encode-coding-string (aux-item->data item) lyskom-server-coding-system)
  )


(defun lyskom-aux-item-info (item header)
  (let ((def (lyskom-find-aux-item-definition item)))
    
    (concat 
     (lyskom-get-string 'aux-item-for)
     header
     "\n"
     (lyskom-format
      'aux-item-info
      (aux-item->aux-no item)
      (aux-item->tag item)
      (if def
          (lyskom-aux-item-definition->name def)
        "unknown")
      (aux-item->creator item)
      (lyskom-format-time 'date-and-time (aux-item->sent-at item))
      (if (aux-item-flags->deleted (aux-item->flags item))
          (format "(%s)" (lyskom-get-string 'deleted))
        "")
      (mapconcat 'identity
                 (delq nil
                       (list
                        (and (aux-item-flags->secret (aux-item->flags item))
                             (lyskom-get-string 'secret))
                        (and (aux-item-flags->anonymous (aux-item->flags item))
                             (lyskom-get-string 'hide-creator))
                        (and (aux-item-flags->inherit (aux-item->flags item))
                             (lyskom-get-string 'inherit))))
                 ", ")
      (cond ((not (aux-item-flags->inherit (aux-item->flags item)))
             (lyskom-get-string 'no-inheritance))
            ((zerop (aux-item->inherit-limit item))
             (lyskom-get-string 'unlimited-inherit))
            ((eq 1  (aux-item->inherit-limit item))
             (lyskom-get-string 'no-more-inherit))
            (t (lyskom-format 'inherit-steps
                              (1- (aux-item->inherit-limit item)))))
      (aux-item->data item)))))

                 

(defun lyskom-print-content-type (item &optional obj)
  (concat (lyskom-format 'content-type-aux (aux-item->data item))
          (lyskom-aux-item-terminating-button item obj)))

(defun lyskom-parse-content-type ()
  (and (looking-at (lyskom-get-string 'content-type-regexp))
       (match-string 1)))

(defun lyskom-parse-cross-reference ()
  (or (and (looking-at (lyskom-get-string 'cross-reference-text-regexp))
           (concat "T" (match-string 1)))
      (and (looking-at (lyskom-get-string 'cross-reference-conf-regexp))
           (concat "C" (match-string 1)))
      (and (looking-at (lyskom-get-string 'cross-reference-pers-regexp))
           (concat "P" (match-string 1)))))

(defun lyskom-edit-insert-cross-reference (item pers)
  (concat
   (lyskom-print-cross-reference item nil pers)
   (lyskom-edit-generate-aux-item-flags (aux-item->flags item))))

(defun lyskom-print-cross-reference (item &optional obj pers)
  (let ((pers (if (and (zerop (aux-item->creator item))
                       pers)
                  pers
                (aux-item->creator item))))

    ;; We use string-to-int here since we handle floats in 
    ;; lyskom-format.

    (concat
     (cond ((string-match "^P\\([0-9]+\\)" (aux-item->data item))
            (lyskom-format 'cross-reference-pers-aux 
                           (lyskom-string-to-number 
                            (match-string 1 (aux-item->data item)))
                           pers
                           ))
           ((string-match "^C\\([0-9]+\\)" (aux-item->data item))
            (lyskom-format 'cross-reference-conf-aux 
                           (lyskom-string-to-number 
                            (match-string 1 (aux-item->data item)))
                           pers
                           ))
           ((string-match "^T\\([0-9]+\\)" (aux-item->data item))
            (lyskom-format 'cross-reference-text-aux 
                           (lyskom-string-to-number 
                            (match-string 1 (aux-item->data item)))
                           pers
                           ))
           (t (lyskom-format 'strange-cross-reference 
                             (aux-item->data item)
                             pers
                             )))
     (lyskom-aux-item-terminating-button item obj)
     )))

(defun lyskom-status-print-cross-reference (item &optional obj pers)
  ;; We use string-to-int here since we handle floats in 
  ;; lyskom-format.
  (lyskom-insert 
   (concat
    (cond ((string-match "^P\\([0-9]+\\)" (aux-item->data item))
           (lyskom-format 'cross-reference-pers-status-aux 
                          (lyskom-string-to-number 
                           (match-string 1 (aux-item->data item)))
                          (aux-item->creator pers)
                          ))
          ((string-match "^C\\([0-9]+\\)" (aux-item->data item))
           (lyskom-format 'cross-reference-conf-status-aux 
                          (lyskom-string-to-number 
                           (match-string 1 (aux-item->data item)))
                          pers
                          ))
          ((string-match "^T\\([0-9]+\\)" (aux-item->data item))
           (lyskom-format 'cross-reference-text-status-aux 
                          (lyskom-string-to-number 
                           (match-string 1 (aux-item->data item)))
                          (aux-item->creator pers)
                          ))
          (t (lyskom-format 'strange-cross-reference-status 
                            (aux-item->data item)
                            (aux-item->creator pers)
                            )))
    (lyskom-aux-item-terminating-button item obj)
    "\n")))  

(defun lyskom-parse-no-comments ()
  (and (looking-at (lyskom-get-string 'no-comments-regexp))
       ""))

(defun lyskom-print-no-comments (item &optional obj)
  (concat (lyskom-format 'no-comments-aux)
          (lyskom-aux-item-terminating-button item obj)))

(defun lyskom-edit-insert-no-comments (item &optional obj)
  (concat
   (lyskom-format 'no-comments-edit-aux)
   (lyskom-edit-generate-aux-item-flags (aux-item->flags item))))

(defun lyskom-parse-personal-comments ()
  (and (looking-at (lyskom-get-string 'personal-comment-regexp))
       ""))

(defun lyskom-print-personal-comments (item &optional obj)
  (concat (lyskom-format 'personal-comment-aux)
          (lyskom-aux-item-terminating-button item obj)))

(defun lyskom-edit-insert-personal-comments (item &optional obj)
  (concat
   (lyskom-format 'personal-comment-edit-aux)
   (lyskom-edit-generate-aux-item-flags (aux-item->flags item))))

(defun lyskom-parse-request-confirmation ()
  (and (looking-at (lyskom-get-string 'request-confirmation-regexp))
       ""))

(defun lyskom-print-request-confirmation (item &optional obj)
  (concat (lyskom-format 'request-confirmation-aux
                         (aux-item->creator item))
          (lyskom-aux-item-terminating-button item obj)))

(defun lyskom-edit-insert-request-confirmation (item &optional obj)
  (concat
   (lyskom-format 'request-confirmation-edit-aux)
   (lyskom-edit-generate-aux-item-flags (aux-item->flags item))))

(defun lyskom-faq-for-conf-action (text-stat)
  (let ((faqs (text-stat-find-aux text-stat 28)))
    (lyskom-traverse aux faqs
      (condition-case nil
          (lyskom-register-read-faq (lyskom-string-to-int
                                     (aux-item->data aux) t)
                                    (text-stat->text-no text-stat))
       (lyskom-integer-conversion-error nil)))))

(defun lyskom-request-confirmation-action (text-stat)
  (let ((confirmations (text-stat-find-aux text-stat 7))
        (have-confirmation nil))
    (while confirmations
      (when (eq lyskom-pers-no (aux-item->creator (car confirmations)))
        (setq have-confirmation t)
        (setq confirmations nil))
      (setq confirmations (cdr confirmations)))
    (when  (not have-confirmation)
      (lyskom-scroll)
      (when (lyskom-j-or-n-p
             (lyskom-format (lyskom-get-string 'confirm-read-q)
                            text-stat))
        (let ((item (lyskom-create-aux-item 0 7 lyskom-pers-no
                                            nil 
                                            (lyskom-create-aux-item-flags
                                             nil nil nil nil nil nil nil nil)
                                            0 "")))
          (initiate-modify-text-info 'background
                                     nil
                                     (text-stat->text-no text-stat)
                                     nil
                                     (list item))
          (cache-del-text-stat (text-stat->text-no text-stat)))))))

(defun lyskom-print-read-confirm (item &optional obj)
  (concat 
   (lyskom-format 'read-confirm-aux
                  (aux-item->creator item)
                  (lyskom-format-time 'date-and-time (aux-item->sent-at item)))
   (lyskom-aux-item-terminating-button item obj)))



(defun lyskom-print-redirect (item &optional obj)
  ;; We use string-to-int here since we handle floats in 
  ;; lyskom-format.
  (concat
   (cond ((string-match "^E-mail:\\(.*\\)$" (aux-item->data item))
          (lyskom-format 'redirect-email-aux 
                         (match-string 1 (aux-item->data item))))
         ((string-match "^LysKOM:\\(.*\\)$" (aux-item->data item))
          (lyskom-format 'redirect-lyskom-aux 
                         (lyskom-string-to-number 
                          (match-string 1 (aux-item->data item))))))
   (lyskom-aux-item-terminating-button item obj)))   


(defun lyskom-print-xface (item &optional obj)
  (lyskom-xemacs-or-gnu
   (lyskom-maybe-add-face-to-string item
                                    (make-string 0 ?X))
   nil))

(defun lyskom-maybe-add-face-to-string (item string)
  (lyskom-xemacs-or-gnu
   (if (null item)
       string
     (setq item (car item))
     (unless (lyskom-find-face 'kom-xface)
       (make-face 'kom-xface))
     (let* ((h (concat "X-Face: " (aux-item->data item)))
            (g (intern h lyskom-xface-cache))
            (e (make-extent 0 (length string) string)))
       (if (boundp g)
           (setq g (symbol-value g))
         (set g (make-glyph
                 (list
                  (list 'global (cons '(tty) [nothing]))
                  (list 'global (cons '(win) 
                                      (vector 'xface ':data h))))))
         (setq g (symbol-value g))
         (set-glyph-face g 'kom-xface))
       (set-extent-begin-glyph e g)
       (set-extent-property e 'end-open nil)
       (set-extent-property e 'start-open nil)
       (set-extent-property e 'duplicable t)
       string))
   string))


(defun lyskom-print-alternate-name (item &optional obj)
  (concat "[" (aux-item->data item) "] "
          (lyskom-aux-item-terminating-button item obj)))

(defun lyskom-print-faq-format-subject (text text-stat text-no)
  (if (and text text-stat)
      (concat "\""
	      (cond ((string-match "\n" (text->decoded-text-mass text text-stat))
		     (substring (text->decoded-text-mass text text-stat) 0 (match-beginning 0)))
		    (t ""))
	      "\"")
    (lyskom-format 'no-such-text-m text-no)))

(defun lyskom-deferred-print-faq (text-stat defer-info)
  (if text-stat
      (initiate-get-text 
       'deferred 
       (lambda (text text-stat defer-info)
         (lyskom-replace-deferred defer-info 
                                  (lyskom-format "%#1r" 
                                                 (lyskom-print-faq-format-subject text
                                                                                  text-stat
                                                                                  (defer-info->data defer-info)))))
       (text-stat->text-no text-stat)
       text-stat
       defer-info)
    (lyskom-replace-deferred defer-info
                             (lyskom-print-faq-format-subject nil nil (defer-info->data defer-info)))))

(defun lyskom-status-print-faq-text (item &optional obj)
  (if (lyskom-aux-item-validate (aux-item->data item) 'lyskom-string-to-int)
      (let* ((text-no (lyskom-string-to-number (aux-item->data item)))
             (subject (if kom-deferred-printing
                          (lyskom-create-defer-info 'get-text-stat
                                                    text-no
                                                    'lyskom-deferred-print-faq
                                                    nil nil nil
                                                    text-no)
                        (blocking-do-multiple ((text (get-text text-no))
                                               (text-stat (get-text-stat text-no)))
                          (lyskom-print-faq-format-subject text text-stat text-no)))))
        (lyskom-format-insert 'faq-in-text-aux 
                              text-no
                              subject)
        (lyskom-insert (lyskom-aux-item-terminating-button item obj)))
    (lyskom-format-insert 'bad-faq-in-text-aux
                          (aux-item->data item)
                          `(face ,kom-warning-face)
                          (lyskom-aux-item-terminating-button item obj)))
  (lyskom-insert "\n"))

(defun lyskom-print-faq-for-conf (item &optional obj)
  (if (lyskom-aux-item-validate (aux-item->data item) 'lyskom-string-to-int)
      (let ((conf-no (lyskom-string-to-number (aux-item->data item))))
        (concat 
         (cond ((zerop conf-no) (lyskom-get-string 'faq-for-server-aux))
               (t (lyskom-format 'faq-for-conf-aux conf-no)))
         (lyskom-aux-item-terminating-button item obj)))
    (lyskom-format 'bad-faq-for-conf-aux 
                   (aux-item->data item)
                   `(face ,kom-warning-face)
                   (lyskom-aux-item-terminating-button item obj))))

(defun lyskom-print-creating-software (item &optional obj)
  (when (or kom-show-creating-software
            (lyskom-viewing-noconversion))
    (concat
     (lyskom-format 'creating-software-aux (aux-item->data item))
     (lyskom-aux-item-terminating-button item obj))))

(defun lyskom-print-send-comments-to (item &optional obj)
  (if (lyskom-aux-item-validate (aux-item->data item) 'lyskom-string-to-int)
      (let ((conf-no (lyskom-string-to-number (aux-item->data item))))
        (lyskom-format-insert 'status-send-comments-to
                              conf-no 
                              (lyskom-aux-item-terminating-button item obj)))
    (lyskom-format-insert 'bad-status-send-comments-to
                          (aux-item->data item)
                          `(face ,kom-warning-face)
                          (lyskom-aux-item-terminating-button item obj))))



(defun lyskom-parse-world-readable ()
  (and (looking-at (regexp-quote 
                    (lyskom-get-string 'world-readable-text-edit-aux)))
       ""))

(defun lyskom-edit-insert-world-readable (item &optional obj)
  (concat
   (lyskom-format 'world-readable-text-edit-aux)
   (lyskom-edit-generate-aux-item-flags (aux-item->flags item))))

(defun lyskom-print-world-readable (item &optional obj)
  (concat (lyskom-format 'world-readable-text-aux)
          (lyskom-aux-item-terminating-button item obj)))

(defun lyskom-print-mx-list-name (item &optional obj)
  (lyskom-format-insert 'conf-mx-list-name 
                        (aux-item->data item)
                        (lyskom-aux-item-terminating-button item obj)))

(defun lyskom-print-mx-refuse-import (item &optional obj)
  (let* ((sym (intern (format "mx-refuse-import-%s" (aux-item->data item))))
         (s (or (lyskom-try-get-string sym 'lyskom-message)
                (format "'%s'" (aux-item->data item)))))
    (lyskom-format-insert 'conf-mx-refuse-import s
                          (lyskom-aux-item-terminating-button item obj))))

(defun lyskom-print-recommended-conf (item &optional obj)
  (if (lyskom-aux-item-validate (aux-item->data item) 'lyskom-string-to-int)
      (let ((conf-no (lyskom-string-to-number (aux-item->data item))))
        (lyskom-format-insert 'recommended-conf-aux
                              conf-no
                              (lyskom-aux-item-terminating-button item obj)))
    (lyskom-format-insert 'bad-recommended-conf-aux
                          (aux-item->data item)
                          `(face ,kom-warning-face)
                          (lyskom-aux-item-terminating-button item obj))))

(defun lyskom-print-elisp-client-read-faq (item &optional obj)
  (when (lyskom-extended-status-information 'read-faq)
    (when (string-match "^\\([0-9]+\\) \\([0-9]+\\)" (aux-item->data item))
      (condition-case nil
          (let ((conf-no (lyskom-string-to-int (match-string 1 (aux-item->data item)) t))
                (text-no (lyskom-string-to-int (match-string 2 (aux-item->data item)) t)))
            (lyskom-format-insert 'status-read-faq-aux-item 
                                  conf-no 
                                  text-no
                                  (lyskom-aux-item-terminating-button item obj)))
        (lyskom-integer-conversion-error
         (lyskom-format-insert 'bad-status-read-faq-aux-item
                               (aux-item->data item)
                               `(face ,kom-warning-face)
                               (lyskom-aux-item-terminating-button item obj)))))))

(defun lyskom-print-elisp-client-rejected-invitation (item &optional obj)
  (if (lyskom-aux-item-validate (aux-item->data item) 'lyskom-string-to-int)
      (let ((conf-no (lyskom-string-to-number (aux-item->data item))))
        (lyskom-format-insert 'status-rejected-recommendation-aux-item
                              conf-no 
                              (lyskom-aux-item-terminating-button item obj)))
    (lyskom-format-insert 'bad-status-rejected-recommendation-aux-item
                          (aux-item->data item)
                          `(face ,kom-warning-face)
                          (lyskom-aux-item-terminating-button item obj))))

(defun lyskom-print-mx-allow-envelope-sender-regexp (item &optional obj)
  (lyskom-format-insert 'conf-mx-allow-envelope-sender-regexp
                        (aux-item->data item)
                        (lyskom-aux-item-terminating-button item obj)))


(provide 'lyskom-aux-items)
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: mime.el
;;;;

(lyskom-external-function mm-find-mime-charset-region)
(defun lyskom-mime-charset-for-text-xemacs (start end)
  (lyskom-try-require 'un-define)
  (if (lyskom-try-require 'mm-util)
      (let ((cs (mm-find-mime-charset-region start end)))
	(cond ((null cs) lyskom-server-coding-system)
	      ((null (cdr cs)) (car cs))
	      (t nil)))
    (lyskom-error "Unable to load required mm-util from gnus")))

(defun lyskom-mime-charset-for-text-gnu (start end)
  (let ((codings
         (delq nil 
               (mapcar (lambda (cs)
                         (cond ((eq cs 'undecided) lyskom-server-coding-system)
                               ((eq cs 'compound-text) nil)
                               ((or (coding-system-get cs 'mime-charset)
                                    (coding-system-get cs ':mime-charset)))))
                       (find-coding-systems-region start end)))))
    (cond ((memq lyskom-server-coding-system codings) 
           lyskom-server-coding-system)
	  ((lyskom-traverse cs kom-preferred-charsets
	     (when (memq cs codings)
	       (lyskom-traverse-break cs))))
	  (t (car codings)))))


(defun lyskom-mime-charset-for-text (start end)
  "Determine which MIME charset to use for region START..END.
If the server coding system is valid for the text, it will be
returned. Otherwise charsets are returned based on Emcas idea
of coding system priority.

In XEmacs, this function will only work if mm-util from Gnus
is present and follows the expected conventions."
  (lyskom-xemacs-or-gnu
   (lyskom-mime-charset-for-text-xemacs start end)
   (lyskom-mime-charset-for-text-gnu start end)))

(defun lyskom-mime-charset-coding-system (charset)
  (condition-case nil
      (and (lyskom-check-coding-system charset)
           charset)
    (error 'raw-text)))

(defun lyskom-mime-encode-string (data mime-charset)
  (let* ((coding-system (lyskom-mime-charset-coding-system mime-charset)))
    (when (and mime-charset coding-system)
      (cons mime-charset (lyskom-encode-coding-string data coding-system)))))

(defun lyskom-mime-decode-string (data charset)
  (let* ((coding-system (lyskom-mime-charset-coding-system charset)))
    (if coding-system
        (lyskom-decode-coding-string data coding-system)
      data)))

(defvar lyskom-mime-type-regexp)
(defvar lyskom-mime-parameter-regexp)
(defvar lyskom-mime-wrap-regexp)
(defvar lyskom-mime-comment-regexp)
(defconst lyskom-mime-parameter-name-match 1)
(defconst lyskom-mime-parameter-value-match 2)

(let* ((whitespace "[\000-\040]*")
       (tspecials "][()<>@,;:\\\"/?=\000-\040")
       (qstring "\"\\([^\"\\]\\|\\\\[\000-\377]\\)*\"")
       (notspecials (format "[^%s]+" tspecials))
       (comment-base "\\((\\([^()]*%s\\)*[^()]*)\\)"))

  (setq lyskom-mime-parameter-regexp 
        (concat whitespace ";" whitespace
                "\\(" notspecials "\\)"
                whitespace "=" whitespace
                "\\(" notspecials
                "\\|" qstring
                "\\)"
                "[^;]*"))
  (setq lyskom-mime-type-regexp
        (concat whitespace "\\(" notspecials "/" notspecials "\\)"))
  (setq lyskom-mime-wrap-regexp (concat "\\\\\\r?\n" whitespace))

  ;; We cheat with comments, and only allow limited nesting
  (setq lyskom-mime-comment-regexp comment-base)
  (let ((nesting 2))
    (while (> nesting 0)
      (setq lyskom-mime-comment-regexp
            (format lyskom-mime-comment-regexp comment-base))
      (setq nesting (1- nesting))))
      (setq lyskom-mime-comment-regexp
            (concat whitespace
                    (format lyskom-mime-comment-regexp "")
                    whitespace))
  )


;;; Parse content-type header according to RFC2045, with some
;;; exceptions. We do not allow comments everywhere, which is
;;; wrong, but I don't care. We do allow comments in many
;;; places, and hopefully those places people will use.

(defun lyskom-mime-decode-content-type (data)
  (let ((content-type nil)
        (params nil)
        (start 0))

    (when (and (string-match lyskom-mime-comment-regexp data start)
               (eq (match-beginning 0) start))
      (setq start (match-end 0)))
    (when (string-match lyskom-mime-type-regexp data)
      (setq content-type (downcase (match-string 1 data)))
      (setq start (match-end 1)))

    (when (and (string-match lyskom-mime-comment-regexp data start)
               (eq (match-beginning 0) start))
      (setq start (match-end 0)))
    (while (string-match lyskom-mime-parameter-regexp data start)
      (let ((param-name (intern (downcase (match-string lyskom-mime-parameter-name-match data))))
            (param-value (downcase (match-string lyskom-mime-parameter-value-match data))))
        (setq start (match-end 0))

        (when (string-match "^\"[\000-\377]*\"$" param-value)
          (setq param-value (substring param-value 1 -1)))
        (setq param-value (replace-in-string param-value
                                             lyskom-mime-wrap-regexp ""))
	(when (eq param-name 'charset)
	  (setq param-value (intern param-value))
	  (when (eq param-value 'us-ascii) (setq param-value 'iso-8859-1)))

	(when (eq param-name 'format)
	  (setq param-value (intern param-value)))

        (when (and (string-match lyskom-mime-comment-regexp data start)
                   (eq (match-beginning 0) start))
          (setq start (match-end 0)))

        (setq params (cons (cons param-name (or param-value t)) params)))
      )

    (cons content-type params)))


(provide 'lyskom-mime)
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: prefetch.el
;;;;
;;;; The (new) prefetch system.
;;;;


(def-kom-var lyskom-prefetch-queue nil
  "A queue where all prefetch requests are entered."
  local)

(defvar lyskom-inhibit-prefetch nil
  "When non-nil, disables the prefetch.")

(def-kom-var lyskom-pending-prefetch 0
  "Variable counting the number of unfinished prefetch requests."
  local)

(defun lyskom-setup-prefetch ()
  "Set up the prefetch system"
  (setq lyskom-prefetch-queue (lyskom-queue-create))
  (setq lyskom-pending-prefetch 0)
  (setq lyskom-inhibit-prefetch nil)
  (setq lyskom-membership-is-read 0))

(defun lyskom-reset-prefetch ()
  "Reset the prefetch system"
  (lyskom-setup-prefetch))

(defsubst lyskom-membership-is-read ()
  "Return t if the while membership list has been fetched, and nil otherwise."
  (eq lyskom-membership-is-read 't))


(defun lyskom-fetch-start-of-map (conf-stat membership)
  "Block fetching map for MEMBERSHIP until we see a text.
Start the prefetch for the remainder of the map."
  (let* ((first-local (1+ (membership->last-text-read membership)))
         (map (blocking-do 'local-to-global
                           (membership->conf-no membership)
                           first-local
                           lyskom-fetch-map-nos)))
    (when map
      (lyskom-enter-map-in-to-do-list map conf-stat membership)
      (when (text-mapping->later-texts-exist map)
        (lyskom-prefetch-map-using-conf-stat conf-stat
                                             (text-mapping->range-end map)
                                             membership)))))

(defun lyskom-prefetch-conf (conf-no)
  "Prefetch the conf-stat for the conference with number CONF-NO."
  (lyskom-queue-enter lyskom-prefetch-queue 
                      `(initiate-get-conf-stat prefetch
                                               lyskom-prefetch-handler 
                                               ,conf-no))
  (lyskom-continue-prefetch))

(defun lyskom-prefetch-pers (pers-no)
  "Prefetch the pers-stat for person with number PERS-NO."
  (lyskom-queue-enter lyskom-prefetch-queue
                      `(initiate-get-pers-stat prefetch
                                               lyskom-prefetch-handler
                                               ,pers-no))
  (lyskom-continue-prefetch))


(defun lyskom-prefetch-text (text-no)
  "Prefetch the text-stat for the text with number TEXT-NO."
  (lyskom-queue-enter lyskom-prefetch-queue
                      `(initiate-get-text-stat prefetch
                                               lyskom-prefetch-handler
                                               ,text-no))
  (lyskom-continue-prefetch))


(defun lyskom-prefetch-textmass (text-no)
  "Prefetch the text mass for the text with number TEXT-NO."
  (lyskom-queue-enter lyskom-prefetch-queue
                      `(initiate-get-text prefetch
                                          lyskom-prefetch-handler
                                          ,text-no))
  (lyskom-continue-prefetch))


(defun lyskom-prefetch-textauth (text-no)
  "Prefetch the text stat and the author of text number TEXT-NO."
  (lyskom-queue-enter lyskom-prefetch-queue
                      `(initiate-get-text-stat prefetch
                                               lyskom-prefetch-textauth-handler
                                               ,text-no))
  (lyskom-continue-prefetch))


(defun lyskom-prefetch-text-all (text-no)
  "Prefetch all info about the text with number TEXT-NO."
  (lyskom-queue-enter lyskom-prefetch-queue
                      `(initiate-get-text-stat prefetch
                                               lyskom-prefetch-text-all-handler
                                               ,text-no))
  (lyskom-continue-prefetch))

(defun lyskom-prefetch-text-stat-all (text-stat)
  "Prefetch all info about the text with text stat TEXT-STAT."
  (lyskom-prefetch-text-all-handler text-stat))

(defun lyskom-prefetch-texttree (text-no &optional only-new)
  "Prefetch all info about the text with number TEXT-NO and descends recursively.

If ONLY-NEW is non-nil and the text-stat in question is already
prefetched the prefetch is not done."
  (unless (and only-new (cache-get-text-stat text-no))
    (lyskom-queue-enter lyskom-prefetch-queue
                        `(initiate-get-text-stat prefetch
                                                 lyskom-prefetch-texttree-handler
                                                 ,text-no)))
  (lyskom-continue-prefetch))

(defun lyskom-prefetch-membership (pers-no)
  "Prefetch memberships for PERS-NO."
  (lyskom-queue-enter lyskom-prefetch-queue 
                      (list (lambda (pers-no)
                              (if (numberp lyskom-membership-is-read)
                                  (initiate-get-part-of-membership
                                   'prefetch 'lyskom-prefetch-membership-handler
                                   pers-no lyskom-membership-is-read
                                   lyskom-fetch-membership-length
                                   pers-no)
                                (lyskom-prefetch-handler)))
                            pers-no))
  (lyskom-continue-prefetch))

(defun lyskom-prefetch-one-membership (conf-no pers-no)
  "Prefetch the membership in CONF-NO for PERS-NO."
  (lyskom-queue-enter lyskom-prefetch-queue 
                      `(initiate-query-read-texts prefetch
                                                  lyskom-prefetch-read-texts-handler
                                                  ,pers-no
                                                  ,conf-no
                                                  t
                                                  ,lyskom-max-int
                                                  ,conf-no))
  (lyskom-continue-prefetch))

(defun lyskom-prefetch-map (conf-no membership)
  "Prefetches a map for conf CONF-NO."
  (lyskom-prefetch-map-from conf-no
                            (1+ (membership->last-text-read membership))
                            membership))

(defun lyskom-prefetch-map-from (conf-no first-local membership)
  "Prefetches a map for conf CONFNO starting att FIRST-LOCAL."
  (lyskom-queue-enter lyskom-prefetch-queue
                      `(initiate-get-conf-stat prefetch
                                               lyskom-prefetch-confstatformap-handler
                                               ,conf-no
                                               ,first-local
                                               ,membership))
  (lyskom-continue-prefetch))

(defun lyskom-prefetch-map-using-conf-stat (conf-stat first-local membership)
  "Prefetches a map for conf CONFSTAT starting att FIRST-LOCAL."
  (lyskom-queue-enter lyskom-prefetch-queue
                      `(initiate-local-to-global prefetch
                                                 lyskom-prefetch-map-handler
                                                 ,(conf-stat->conf-no conf-stat)
                                                 ,first-local
                                                 ,lyskom-fetch-map-nos
                                                 ,conf-stat
                                                 ,first-local
                                                 ,membership))
  (lyskom-continue-prefetch))

(defun lyskom-prefetch-texts (texts)
  "Prefetches a list of texts."
  (when texts
    (lyskom-queue-enter lyskom-prefetch-queue
                        `(initiate-get-text-stat prefetch
                                                 lyskom-prefetch-texts-handler
                                                 ,(car texts)
                                                 ,(cdr texts))))
  (lyskom-continue-prefetch))

(defun lyskom-prefetch-cancel-prefetch-map (conf-no &optional queue))


;;; ================================================================
;;; Internal prefetch functions

(defun lyskom-stop-prefetch ()
  "Stop the prefetch process temporarily."
  )

(defun lyskom-start-prefetch ()
  "Start the whole prefetch process"
  (lyskom-continue-prefetch))

(defun lyskom-continue-prefetch ()
  "Called after each prefetch is finished and also when the whole prefetch
process is started. Used to keep prefetch going."
  (unless lyskom-inhibit-prefetch
    (let ((lyskom-inhibit-prefetch t))
      (while (and (< lyskom-pending-prefetch lyskom-prefetch-limit)
                  (lyskom-prefetch-one-item)
                  (++ lyskom-pending-prefetch))))))

(defun lyskom-prefetch-one-item ()
  "Get the first element of the prefetch data structure and fetch it.
Return t if an element was prefetched, otherwise return nil."
  (let ((item (lyskom-queue-delete-first lyskom-prefetch-queue)))
    (cond ((null item) nil)
          ((functionp (car item)) (apply (car item) (cdr item)) t)
          (t (signal 'lyskom-internal-error
                     (list "unknown prefetch key" (car item)))))))



;;; ================================================================
;;; Handlers
;;;

(defun lyskom-prefetch-textauth-handler (text-stat)
  "Prefetch the conf-stat of the author of the text TEXT-STAT."
  (-- lyskom-pending-prefetch)
  (lyskom-stop-prefetch)
  (when text-stat
    (lyskom-prefetch-conf (text-stat->author text-stat)))
  (lyskom-start-prefetch))

(defun lyskom-prefetch-text-all-handler (text-stat)
  "Prefetch all info neccessary to write the text with text-stat TEXT-STAT."
  (-- lyskom-pending-prefetch)
  (lyskom-stop-prefetch)
  (lyskom-prefetch-conf (text-stat->author text-stat))
  (lyskom-prefetch-textmass (text-stat->text-no text-stat))
  (lyskom-traverse misc (text-stat->misc-info-list text-stat)
   (let ((type (misc-info->type misc)))
     (cond
      ((memq type lyskom-recpt-types-list)
       (lyskom-prefetch-conf (misc-info->recipient-no misc)))
      ((eq type 'COMM-IN)
       (lyskom-prefetch-textauth (misc-info->comm-in misc)))
      ((eq type 'FOOTN-IN)
       (lyskom-prefetch-textauth (misc-info->footn-in misc)))
      ((eq type 'COMM-TO)
       (lyskom-prefetch-textauth (misc-info->comm-to misc)))
      ((eq type 'FOOTN-TO)
       (lyskom-prefetch-textauth (misc-info->footn-to misc)))
      (t nil))))
  (lyskom-start-prefetch))



(defun lyskom-prefetch-texttree-handler (text-stat)
  "Prefetch all info neccessary to write the text with text-stat TEXT-STAT.
Then prefetch all info (texttree) of comments."
  (-- lyskom-pending-prefetch)
  (when text-stat
    (lyskom-stop-prefetch)
    (lyskom-prefetch-conf (text-stat->author text-stat))
    (lyskom-prefetch-textmass (text-stat->text-no text-stat))
    (lyskom-traverse misc (text-stat->misc-info-list text-stat)
     (let ((type (misc-info->type misc)))
       (cond
	((memq type lyskom-recpt-types-list)
	 (lyskom-prefetch-conf (misc-info->recipient-no misc)))
	((eq type 'COMM-IN)
	 (lyskom-prefetch-texttree (misc-info->comm-in misc) t))
	((eq type 'FOOTN-IN)
	 (lyskom-prefetch-texttree (misc-info->footn-in misc) t))
	((eq type 'COMM-TO)
	 (lyskom-prefetch-textauth (misc-info->comm-to misc)))
	((eq type 'FOOTN-TO)
	 (lyskom-prefetch-textauth (misc-info->footn-to misc)))
	(t nil)))))
  (lyskom-start-prefetch))


(defun lyskom-prefetch-read-texts-handler (membership pers-no)
  (-- lyskom-pending-prefetch)
  (lyskom-stop-prefetch)
  (when membership
    (let ((old-mship (lyskom-try-get-membership
                      (membership->conf-no membership) t)))
      (if old-mship
          (set-membership->read-texts old-mship
                                      (membership->read-texts membership))
        (lyskom-add-memberships-to-membership (list membership)))
      (when (and (lyskom-visible-membership membership)
                 (lyskom-prefetch-map (membership->conf-no membership)
                                      membership))))
    (lyskom-start-prefetch)))


(defun lyskom-prefetch-membership-handler (memberships pers-no)
  "Handle the return of the membership prefetch call."
  (-- lyskom-pending-prefetch)
  (lyskom-stop-prefetch)
  (lyskom-add-memberships-to-membership memberships)
  (if (and (numberp lyskom-membership-is-read)
           (< (length memberships) lyskom-fetch-membership-length))
        (setq lyskom-membership-is-read t)
    (setq lyskom-membership-is-read (+ lyskom-membership-is-read
                                       lyskom-fetch-membership-length))
    (lyskom-prefetch-membership pers-no))
  (lyskom-start-prefetch))


(defun lyskom-prefetch-confstatformap-handler (conf-stat first-local membership)
  "Now that we have the conf-stat we can fetch the map."
  (-- lyskom-pending-prefetch)
  (lyskom-stop-prefetch)
  (lyskom-prefetch-map-using-conf-stat conf-stat first-local membership)
  (lyskom-start-prefetch))


(defun lyskom-prefetch-map-handler (map conf-stat first-local membership)
  "Handle the return of the membership prefetch call.
Maps are `cached' in lyskom-to-do-list."
  (-- lyskom-pending-prefetch)
  (lyskom-stop-prefetch)
  (let ((next-start (and map (text-mapping->range-end map))))
    (when map
      (when (text-mapping->later-texts-exist map)
	(lyskom-prefetch-map-using-conf-stat conf-stat
                                             next-start
                                             membership))
      (lyskom-enter-map-in-to-do-list map conf-stat membership)))
  (lyskom-start-prefetch)
  (lyskom-update-prompt)
  (lyskom-set-mode-line))


(defun lyskom-prefetch-texts-handler (text-stat texts queue)
  "Prefetch all info neccessary to write the text with text-stat TEXT-STAT."
  (-- lyskom-pending-prefetch)
  (lyskom-stop-prefetch)
  (lyskom-prefetch-conf (text-stat->author text-stat))
  (lyskom-prefetch-textmass (text-stat->text-no text-stat))
  (lyskom-traverse misc (text-stat->misc-info-list text-stat)
    (let ((type (misc-info->type misc)))
      (cond ((memq type lyskom-recpt-types-list)
             (lyskom-prefetch-conf (misc-info->recipient-no misc)))
            ((eq type 'COMM-IN)
             (lyskom-prefetch-textauth (misc-info->comm-in misc)))
            ((eq type 'FOOTN-IN)
             (lyskom-prefetch-textauth (misc-info->footn-in misc)))
            ((eq type 'COMM-TO)
             (lyskom-prefetch-textauth (misc-info->comm-to misc)))
            ((eq type 'FOOTN-TO)
             (lyskom-prefetch-textauth (misc-info->footn-to misc)))
            (t nil))))
  (lyskom-prefetch-texts texts)
  (lyskom-start-prefetch))


(defun lyskom-prefetch-handler (&rest data)
  "Handler called after each simple prefetch request is done."
  (-- lyskom-pending-prefetch)
  (lyskom-continue-prefetch))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: startup.el
;;;;
;;;; This file contains functions that are called only when lyskom
;;;; is loaded, started or when a new user is logged in during a 
;;;; session.
;;;;


;;; ================================================================
;;;                         Start kom.


(defvar lyskom-www-proxy-connect-phase 1
  "Phase when reading connection response from www-proxy:
1: (initial phase) waiting for the
   string \"HTTP/1.0 2000 Connection established\"
2: we have seen the connection string. After this, it may come lines stating
   proxy-agents and other things. They all seem to end with an empty line,
   so in this phase we wait for an empty line.")


(eval-and-compile
  (defun lyskom-compilation-in-progress ()
    "Returns non-nil if the client currently is being compiled, else nil."

    ;; This horrid code is inspired from how cl-compiling-file in the
    ;; cl package works.
    (let ((buf (car (delq nil
                          (mapcar (lambda (sym)
                                    (and (boundp sym) (symbol-value sym)))
                                  '(bytecomp-outbuffer byte-compile-outbuffer outbuffer))))))
      (and (bufferp buf) (equal (buffer-name buf) " *Compiler Output*")))))

(defun lyskom-is-running-compiled ()
  (condition-case nil
      (or (eval-when-compile (lyskom-compilation-in-progress))
          (byte-code-function-p (symbol-function 'lyskom-is-running-compiled)))
    (error nil)))


;;;###autoload
(defun lyskom (&optional host username password session-priority invisiblep)
  "Start a LysKOM session.
Optional arguments: HOST, USERNAME, PASSWORD and INVISIBLEP.

A numeric prefix argument sets the session priority. A prefix argument
of `C-u', on the other hand, logs in the session without notifying other
clients of the event. See lyskom-mode for details on lyskom."
  (interactive (list (lyskom-read-server-name)
		     nil
		     nil
		     (if current-prefix-arg
			 (prefix-numeric-value current-prefix-arg)
		       nil)
		     (and current-prefix-arg (listp current-prefix-arg))))

  (run-hooks 'lyskom-init-hook)
  (setq username
	(or username 
	    kom-default-user-name
	    (getenv "KOMNAME")))
  (setq password
	(or password
	    (when (and lyskom-default-password ; Got default password?
		       (or (not lyskom-server-name) ; For this host?
			   (string= lyskom-server-name
				    host)))
	      lyskom-default-password)
	    kom-default-password
	    (getenv "KOMPASSWORD")))
  (if (zerop (length host))
      (let* ((env-kom (getenv "KOMSERVER"))
	     (canon (or (lyskom-string-rassoc env-kom kom-server-aliases)
                        (lyskom-string-rassoc env-kom kom-builtin-server-aliases))))
	(setq host (or (car canon)
		       env-kom
		       lyskom-default-server
		       kom-default-server))))
  (let ((port 4894)
	(init-done nil))
    (cond				;Allow "nanny:4892" to use port 4892.
     ((string-match ":" host)
      (setq port (lyskom-string-to-number (substring host (match-end 0))))
      (cond
       ((zerop (match-beginning 0))
	(setq host (or lyskom-default-server kom-default-server)))
       (t
	(setq host (substring host 0 (match-beginning 0)))))))

    (let* ((duplicate-buffers
            (delq nil
                  (mapcar (lambda (buffer)
                            (and (lyskom-buffer-p buffer t)
                                 (buffer-name buffer)
                                 (string-match (regexp-quote host)
                                               (buffer-name buffer))
                                 (save-current-buffer
                                   (set-buffer buffer)
                                   (and 
                                    (boundp 'lyskom-server-port)
                                    (eq port lyskom-server-port)))
                                 buffer))
                          (buffer-list))))
	   (name nil)
	   (proc nil)
           (buffer (car duplicate-buffers))
	   ;; (alive (lyskom-buffer-p buffer))
           (reused-buffer nil))
      

      ;;<<<<<<< startup.el
      ;;      (if (and buffer
      ;;	       alive
      ;;=======
      (if (and (lyskom-buffer-p buffer nil)
	       (not (prog1
			(j-or-n-p (lyskom-get-string
				   'start-new-session-same-server))
		      (message ""))))
	  (progn 
            (switch-to-buffer buffer)
            (setq reused-buffer t))
	(unwind-protect
	    (progn
	      (cond ((and buffer (not (lyskom-buffer-p buffer)))
		     (set-buffer buffer)
                     (setq reused-buffer t)
		     (goto-char (point-max))
                     (lyskom-insert
                      (format (lyskom-get-string 'new-session-in-buffer)
                              (lyskom-format-time
                               'timeformat-day-yyyy-mm-dd-hh-mm-ss)))
                     (setq name (buffer-name buffer)))
		    (t
		     (setq buffer (lyskom-generate-new-buffer host))
		     (setq name (buffer-name buffer))))
              (let* ((www-proxy-host-string
                      (cond ((stringp kom-www-proxy) kom-www-proxy)
                            ((listp kom-www-proxy)
                             (or (cdr (lyskom-string-assoc host kom-www-proxy))
                                 (cdr (assq t kom-www-proxy))
                                 nil))
                            (t nil)))
                     (www-proxy-host nil)
                     (www-proxy-port nil)
                     (match (string-match "\\(.*\\):\\([0-9]+\\)"
                                          (or www-proxy-host-string "")))
                     (headers nil))
                (setq www-proxy-host (or (and match
                                          (match-string 1 www-proxy-host-string))
                                     www-proxy-host-string)
                      www-proxy-port (or (and match
                                          (lyskom-string-to-number
                                           (match-string 2
                                                         www-proxy-host-string)))
                                     80))
                (cond (www-proxy-host
                       (setq headers 
                             (cond ((stringp kom-www-proxy-headers)
                                    (list kom-www-proxy-headers))
                                   ((and (listp kom-www-proxy-headers)
                                         (stringp (car kom-www-proxy-headers)))
                                    kom-www-proxy-headers)
                                   ((and (listp kom-www-proxy-headers)
                                         (consp (car kom-www-proxy-headers)))
                                    (cdr (or (lyskom-string-assoc www-proxy-host
                                                                  kom-www-proxy-headers)
                                             (assq t kom-www-proxy-headers))))))
                       (setq proc (lyskom-open-network-stream name buffer
                                                              www-proxy-host
                                                              www-proxy-port))
                       ;; We do explicit coding
                       (lyskom-set-process-coding-system proc 'no-conversion 'no-conversion)

		       ;; Install our filter.
		       ;; Do this before we send the CONNECT command to
		       ;; the proxy, in case the proxy answers fast.
		       (setq lyskom-www-proxy-connect-phase 1)
		       (set-process-filter proc
					   'lyskom-www-proxy-connect-filter)
		       (lyskom-set-process-query-on-exit-flag proc nil)

                       (lyskom-process-send-string 
                        proc
                        (format "CONNECT %s:%d HTTP/1.0\r\n"
                                host port))

                       (mapc (lambda (header)
                               (lyskom-process-send-string proc header)
                               (lyskom-process-send-string proc "\r\n"))
                               headers)
                       (lyskom-process-send-string proc "\r\n")


		       ;; Now wait for the answer from the proxy
		       ;;
		       ;; This is because anything we send before the
		       ;; connection ack will be thrown away by the proxy
		       ;; so it is bad to try to start talking with the
		       ;; server before the connection is up.
		       (while (eq 'lyskom-www-proxy-connect-filter
				  (process-filter proc))
			 (accept-process-output proc))
		       ;; Now the proxy has connected to the kom server
		       )
                      (t (setq proc (lyskom-open-network-stream name buffer
                                                                host port))
                         ;; We do explicit coding
                         (lyskom-set-process-coding-system proc 'no-conversion 'no-conversion))))
	      (switch-to-buffer buffer)
	      (lyskom-mode)		;Clearing lyskom-default...
	      (setq lyskom-buffer buffer)
	      (setq lyskom-server-name host)
	      (setq lyskom-server-port port)
	      (setq lyskom-proc proc)
	      (lyskom-setup-faces-for-buffer)
              (lyskom-check-configuration)
	      (lyskom-insert
	       (lyskom-format 'try-connect lyskom-clientversion host))
	      (set-process-filter proc 'lyskom-connect-filter)
              (lyskom-set-process-query-on-exit-flag proc nil)
	      (lyskom-process-send-string
	       proc
	       (concat "A"
		       (lyskom-format-objects
			(concat (user-login-name)
				"%" (system-name)))
                       "\n"))
	      (while (eq 'lyskom-connect-filter (process-filter proc))
		(accept-process-output proc))
	      ;; Now we have got the correct response.
	      (set-process-sentinel proc 'lyskom-sentinel)

	      (save-current-buffer
		(lyskom-init-parse buffer))

              ;; Set up timestamps and stuff
              (lyskom-set-connection-time-format t)

              ;; Async messages
              (lyskom-accept-async)

	      ;; +++PREFETCH+++
	      (lyskom-setup-prefetch)

	      ;; Tell the server who we are
	      (initiate-set-client-version 'background nil
					   "lyskom.el" lyskom-clientversion)

              (setq lyskom-server-version-info (blocking-do 'get-version-info))
              (when (or (null lyskom-server-version-info)
                        (<= (version-info->protocol-version 
                             lyskom-server-version-info) 7))
                (lyskom-error 'too-old-server))
	      (lyskom-setup-client-for-server-version)

	      (setq lyskom-server-info (blocking-do 'get-server-info))
	      (lyskom-format-insert 
	       'connection-done
	       (version-info->software-version lyskom-server-version-info))

              (when (lyskom-have-call 85)
                (setq lyskom-collate-table (blocking-do 'get-collate-table))
                (setq lyskom-char-classes nil)
                (lyskom-update-command-completion))
              (if (not (zerop (server-info->motd-of-lyskom
			       lyskom-server-info)))
		  (blocking-do-multiple ((text (get-text 
                                                (server-info->motd-of-lyskom
                                                 lyskom-server-info)))
                                         (text-stat (get-text-stat
                                                     (server-info->motd-of-lyskom
                                                      lyskom-server-info))))
                    (let* ((conf-stat (and text-stat (blocking-do 'get-conf-stat (text-stat->author text-stat))))
                           (str (and text text-stat (text->decoded-text-mass text text-stat))))
                      (if (null text-stat)
                          (lyskom-get-string 'lyskom-motd-was-garbed)
                        (lyskom-insert (lyskom-get-string 'server-has-motd))
                        (when (string-match "\n" str)
                          (setq str (substring str (match-end 0))))
                        (lyskom-format-insert 
                         "%#2$%#1s\n"
                         (if kom-dashed-lines
                             (make-string kom-text-header-dash-length ?-)
                           "")
                         (when kom-highlight-dashed-lines
                           `(face ,(or kom-dashed-lines-face
                                       lyskom-default-dashed-lines-face))))

                        (lyskom-format-insert "%#2$%#1t\n"
                                              str
                                              (when kom-highlight-text-body
                                                `(face ,(or kom-text-body-face
                                                            lyskom-default-text-body-face))))

                        (lyskom-format-insert
                         "%#2$%#1s\n"
                         (lyskom-format-text-footer 
                          text-stat
                          conf-stat
                          (cond ((eq (text-stat->author text-stat) 0)
                                 (lyskom-get-string 'person-is-anonymous))
                                (conf-stat (conf-stat->name conf-stat))
                                (t (lyskom-format 'person-does-not-exist 
                                                  (text-stat->author text-stat))))
                          kom-text-footer-format
                          lyskom-last-text-format-flags)
                         (when kom-highlight-dashed-lines
                           `(face ,(or kom-dashed-lines-face
                                       lyskom-default-dashed-lines-face))))
                        ))))

	      ;; Can't use lyskom-end-of-command here.
	      (setq lyskom-executing-command nil)
	      ;; Log in
	      (kom-start-anew t session-priority invisiblep username password)
	      (if (memq lyskom-buffer lyskom-buffer-list)
		  (while (not (eq lyskom-buffer (car lyskom-buffer-list)))
		    (setq lyskom-buffer-list
			  (nconc (cdr lyskom-buffer-list)
				 (list (car lyskom-buffer-list)))))
		(setq lyskom-buffer-list
		      (cons lyskom-buffer lyskom-buffer-list)))
              ;; We're done
	      (setq init-done t)
              )
	  ;; Something went wrong. Lets cleanup everything. :->
	  (if init-done
	      nil
	    (if proc (delete-process proc))
	    (unless reused-buffer (kill-buffer buffer))))))))


(defun lyskom-accept-async ()
  (blocking-do 'accept-async '(5 7 8 9 11 12 13 14 15 16 17 18 19 20 21 22))
  (let* ((ans (blocking-do 'query-async)))
    (unless (memq 15 (listify-vector ans))
      ;; async-new-text is not implemented, so use async-new-text-old
      (blocking-do 'accept-async '(0 5 7 8 9 11 12 13 14 16 17 18 19 20 21 22)))))


(defun lyskom-www-proxy-connect-filter (proc output)
  "Receive connection acknowledgement from proxy."
  (if lyskom-debug-communications-to-buffer
      (lyskom-debug-insert proc "-----> " output))
  (cond
   ((and (= lyskom-www-proxy-connect-phase 1)
	 (string-match "^HTTP/1\\.. 200.*\r\n" output))
    (setq lyskom-www-proxy-connect-phase 2)
    ;; safety check: see if the empty line is already in this output
    (lyskom-www-proxy-connect-filter proc output))

   ((and (= lyskom-www-proxy-connect-phase 2)
	 (string-match "^\r\n" output))
    (set-process-filter proc 'lyskom-connect-filter))))

(defun lyskom-open-network-stream (name buffer host service)
  (let ((relay (lyskom-setup-ssh-relay host service buffer)))
    (if relay
        (open-network-stream name
                             buffer
                             "127.0.0.1"
                             (get relay 'relay-port))
    (open-network-stream name
                         buffer
                         host
                         service))))


(defun lyskom-setup-ssh-relay (server port kom-buffer)
  (when kom-ssh-relay-host
    (let* ((procname (format "ssh<%s:%s:%d>" kom-ssh-relay-host server port))
           (bufname (concat " *" procname "*"))
           (proc (get-process procname))
           (procsym (intern procname))
           (relay-port (and proc
                            (eq (process-status proc) 'run)
                            (get procsym 'relay-port)))
           (msg nil))
      (unwind-protect
          (save-current-buffer
            (set-buffer (get-buffer-create bufname))
            (if relay-port
                (lyskom-message 
                 "%s" 
                 (lyskom-format (setq msg 'using-ssh-connection)
                                kom-ssh-relay-host))
              (when proc (delete-process proc))
              (setq relay-port (+ 10000 (random 20000)))
              (put procsym 'relay-host 
                   (if (string-match "@" kom-ssh-relay-host)
                       (substring kom-ssh-relay-host (1+ (match-beginning 0)))
                     kom-ssh-relay-host))
              (put procsym 'relay-port relay-port)
              (put procsym 'num-connected 0)
              (lyskom-message
               "%s"
               (lyskom-format (setq msg 'opening-ssh-connection)
                              kom-ssh-relay-host))
              (goto-char (point-max))
              (insert "\n--- new connection ---\n")
              (let ((old-lc-all (getenv "LC_ALL")))
                (unwind-protect
                    (progn
                      (setenv "LC_ALL" "C")
                      (setq proc (start-process
                                  procname
                                  bufname
                                  kom-ssh-command "-n" "-x"
                                  "-L" (format "%d:%s:%d" relay-port server port)
                                  kom-ssh-relay-host
                                  "sh -c \"while :; do echo ok; sleep 600; done\"")))
                  (setenv "LC_ALL" old-lc-all)))
              (lyskom-set-process-query-on-exit-flag proc nil))
            (while (progn
                     (goto-char (point-max))
                     (re-search-backward "^--- .* ---$" nil t)
                     (not (re-search-forward "^ok$" nil t)))
              (when (or (re-search-forward kom-ssh-general-errors nil t)
			(not (eq 'run (process-status proc))))
                (cond ((match-string 1)
                       (process-send-string 
                        proc
                        (concat (lyskom-silent-read (match-string 1)) "\n"))
                       (delete-region (match-beginning 0) 
                                      (match-end 0)))
                      (t (error (lyskom-get-string 'ssh-cant-connect)
                                (buffer-substring-no-properties
                                 (progn (beginning-of-line) (point))
                                 (progn (skip-chars-forward "^\n\r")
                                        (point)))))))
              (sleep-for 0.5))

	    ;; Check that local forwarding works.

	    (save-excursion
	      (goto-char (point-max))
	      (re-search-backward "^--- .* ---$" nil t)
	      (when (re-search-forward kom-ssh-forwarding-errors nil t)
		(kill-process proc)
		(error (lyskom-get-string 'ssh-cant-connect)
                                (buffer-substring-no-properties
                                 (progn (beginning-of-line) (point))
                                 (progn (skip-chars-forward "^\n\r")
                                        (point))))))

            (setq proc nil)
            (lyskom-message 
             "%s"
             (lyskom-format (concat 
                             (lyskom-format msg kom-ssh-relay-host)
                             (lyskom-get-string 'done)))))
        (if proc (delete-process proc)))

      (save-current-buffer
        (set-buffer kom-buffer)
        (put procsym 'num-connected (1+ (or (get procsym 'num-connected) 0)))
        (make-local-variable 'lyskom-ssh-proxy)
        (setq lyskom-ssh-proxy procsym))
      )))


(defun lyskom-setup-client-for-server-version ()
  "Setup flags according to protocol versions."
  (lyskom-clear-features)
  (let ((protocol-version 
         (version-info->protocol-version lyskom-server-version-info)))

  (when (>= protocol-version 8)
    (lyskom-set-feature long-conf-types t))

  (when (>= protocol-version 9)
    (lyskom-set-feature dynamic-session-info t)
    (lyskom-set-feature idle-time t))

  (when (>= protocol-version 10)
    (lyskom-set-feature bcc-misc t)
    (lyskom-set-feature aux-items t)
    (lyskom-set-feature highest-call 105)
    (lyskom-set-feature local-to-global t))

  (when (>= protocol-version 11)
    (lyskom-set-feature read-ranges t)
    (lyskom-set-feature highest-call 110))
  ))


(defun lyskom-connect-filter (proc output)
  "Receive connection acknowledgement from server."
  (if lyskom-debug-communications-to-buffer
      (lyskom-debug-insert proc "-----> " output))
  (cond
   ((string-match "^LysKOM\n" output)
    (set-process-filter proc 'lyskom-filter))))





;;; ================================================================
;;;                        Start anew


(defun kom-start-anew (&optional lyskom-first-time-around session-priority invisiblep username password)
  "Start/login as a new person. If INVISIBLEP is not nil, the login will not be
shown to other users."
  (interactive)
  (lyskom-start-of-command 'kom-start-anew)
  (lyskom-completing-clear-cache)
  (let ((new-me nil)
	(login-successful nil)
        (ignored-user-area-vars nil))
    (unwind-protect
        (progn
          (if lyskom-first-time-around
              (lyskom-init-membership)
            (lyskom-tell-internat 'kom-tell-login))
          ;; We can't allow the prefetch to go on after the new user
          ;; is logged in, but to shut down the prefetch would be too
          ;; brutal, since the new login might be cancelled. To
          ;; prevent the blocking-do calls below from allowing
          ;; prefetch we set lyskom-inhibit-prefetch locally.
          (let ((lyskom-inhibit-prefetch t))
            (while (not new-me)

              (if (and lyskom-first-time-around
                       username)
                  ;; This is nil if we can't find a unique match.
                  (setq new-me
			(if (integerp username)
			    username
			(conf-z-info->conf-no
                         (lyskom-lookup-conf-by-name username
                                                     '(pers))))))
              (if new-me
                  nil
                (let ((name nil))
                  (while (or (null name) (string= name ""))
                    (setq name (lyskom-read-conf-name 'what-is-your-name
                                                      '(pers none) nil nil t)))
                  (setq new-me
                        (or (conf-z-info->conf-no 
                             (lyskom-lookup-conf-by-name name '(pers)))
                            (lyskom-create-new-person name)))))
              ;; Now new-me contains a number of a person.
              ;; Lets log him in.
              (if new-me
                  (let ((conf-stat (blocking-do 'get-conf-stat new-me))
                        (lyskom-inhibit-minibuffer-messages t))

                    ;; Previously this code used lyskom-pers-no
                    ;; directly instead of new-me, but that caused
                    ;; problem with asynchrounous code trying to
                    ;; access it.
                    ;;
                    ;; Setting lyskom-pers-no fscks up other things
                    ;; if we do keyboard-quit in the middle, so don't.
                    ;;
                    ;; (setq lyskom-pers-no new-me)

                    ;; DEBUG
                    (if (null conf-stat)
                        (lyskom-insert "You don't exist. Go away.\n"))

                    (lyskom-insert (concat (conf-stat->name conf-stat) "\n"))
                    (setq lyskom-first-time-around nil)
                    (if (blocking-do 'login new-me
                                     (setq password
					   (or password
					     ;; Use password read when creating
					     ;; the person when loggin in new
					     ;; users
					     (or lyskom-is-new-user
						 (lyskom-silent-read
						  (lyskom-get-string 'password)))))
					   (if invisiblep 1 0))
                        (progn
                          (if lyskom-is-new-user
                              (blocking-do 'add-member
                                           (server-info->conf-pres-conf lyskom-server-info)
                                           new-me
                                           100 
                                           1
                                           (lyskom-create-membership-type
                                            nil nil nil nil nil nil nil nil)))
			  (setq login-successful t))
                      (lyskom-insert-string 'wrong-password)
		      (setq lyskom-default-password nil)
                      (setq password nil)
                      (when (lyskom-get-aux-item 
                             (server-info->aux-item-list lyskom-server-info)
                             13)        ; e-mail
                        (lyskom-insert 'wrong-password-help)
                        (mapc (lambda (el)
                                  (lyskom-format-insert 'wrong-password-email
                                                        (aux-item->data el)))
                                (lyskom-get-aux-item 
                                 (server-info->aux-item-list lyskom-server-info)
                                 13)    ; e-mail
                                ))
                      (setq new-me nil))
                    (setq lyskom-is-new-user nil))))

            ;; Now we are logged in.
            (setq lyskom-pers-no new-me)
            (setq lyskom-is-administrator nil)
            (lyskom-insert-string 'are-logged-in)

            (unless (lyskom-is-running-compiled)
              (lyskom-insert-string 'warning-about-uncompiled-client))

            (unless lyskom-dont-read-user-area
              (setq ignored-user-area-vars (lyskom-read-options)))

	    (if kom-remember-password
		(setq lyskom-default-password password))

	    ;Update mode-line string if needed (as early as possible).

	    (lyskom-mode-name-from-host) 
	    
            (when (or session-priority kom-default-session-priority)
              (setq lyskom-session-priority
                    (or session-priority kom-default-session-priority)))
            (lyskom-run-hook-with-args 'lyskom-change-conf-hook
                                       lyskom-current-conf
                                       0)
            (lyskom-run-hook-with-args 'kom-change-conf-hook
                                       lyskom-current-conf
                                       0)
            (setq lyskom-current-conf 0)
            (cache-set-marked-texts (blocking-do 'get-marks))
            ;; What is this variable? It is never used. It is ust to
            ;; fill the cache?
            (let ((lyskom-who-am-i (blocking-do 'who-am-i)))
              (if lyskom-who-am-i (setq lyskom-session-no lyskom-who-am-i))))

          ;; If login succeeded, clear the caches and set the language

          (when login-successful
            (clear-all-caches)
            (lyskom-init-membership)
            (when (lyskom-set-language (lyskom-default-language) 'local)
              (unless lyskom-have-one-login
                (lyskom-set-language (lyskom-default-language) 'global)
                (setq-default lyskom-language lyskom-language)
                (setq-default kom-default-language (list lyskom-language)))
              (lyskom-format-insert-before-prompt
               'language-set-to
               (lyskom-language-name (lyskom-default-language))
               "Help on changing languages" 
                `(face underline
                       mouse-face highlight
                       lyskom-button t
                       lyskom-button-text ""
                       lyskom-button-type func
                       lyskom-buffer ,(current-buffer)
                       lyskom-button-arg 
                       (kom-help ("Changing Languages" . language-help))))

              )
            (setq lyskom-have-one-login t))

          (when ignored-user-area-vars
            (lyskom-format-insert-before-prompt
             'ignored-user-area-var
             (mapconcat (lambda (x) (symbol-name (car x)))
                        ignored-user-area-vars
                        "\n    ")
             (lyskom-string= lyskom-clientversion lyskom-settings-version)
             lyskom-settings-version
             lyskom-clientversion
             72)
            (setq lyskom-saved-unknown-variables ignored-user-area-vars))

          ;; Show motd and encourage writing a presentation

          (let ((conf-stat (blocking-do 'get-conf-stat lyskom-pers-no)))
            (if (and conf-stat
                     (/= (conf-stat->msg-of-day conf-stat) 0))
                (progn
                  (lyskom-insert-string 'you-have-motd)
                  (lyskom-view-text (conf-stat->msg-of-day conf-stat))))
            (lyskom-nag-about-presentation t))

          (setq lyskom-is-new-user nil)

          ;; Start the prefetch and update some basic caches
          (lyskom-refetch))
      (lyskom-end-of-command)))

  ;; Run the hook kom-login-hook. We don't want to hang the
  ;; login, just because something crashed here.

  (condition-case err
      (progn
        (run-hooks 'lyskom-login-hook)
        (run-hooks 'kom-login-hook))
    (error (lyskom-format-insert-before-prompt
            'error-in-login-hook (format "%s" err)))))


(defun lyskom-refetch ()
  "Resets and fetches all reading info.
This is called at login and after prioritize and set-unread."

  ;; The whole membership!

  (lyskom-init-membership)
  (setq lyskom-to-do-list (lyskom-create-read-list))
  (setq lyskom-reading-list (lyskom-create-read-list))


  (lyskom-reset-prefetch)
  (let ((lyskom-inhibit-prefetch t))
    (let ((unreads (blocking-do 'get-unread-confs lyskom-pers-no)))
      (lyskom-traverse conf-no (conf-no-list->conf-nos unreads)
        (lyskom-prefetch-one-membership conf-no lyskom-pers-no)))
    (lyskom-prefetch-membership lyskom-pers-no))
  (lyskom-start-prefetch)

  (condition-case nil
      (progn (lyskom-update-read-faqs)
             (lyskom-clean-read-faqs lyskom-pers-no)
             (lyskom-update-rejected-recommendations)
             (lyskom-startup-check-faqs)
             (lyskom-startup-check-recommended-memberships))
    (error nil)
    (quit nil)))

(defun lyskom-create-new-person (name)
  "A new user named NAME (or an old that mis-spelled his name)."
  (lyskom-insert
   (lyskom-format 'first-greeting name))
  (lyskom-scroll)
  (cond
   ((ja-or-nej-p (lyskom-format 'is-name-correct name))
    (let ((password (lyskom-silent-read (lyskom-get-string 'personal-password))))
      (cond
       ((not (equal password 
		    (lyskom-silent-read (lyskom-get-string 'repeat-password))))
	;; Failed to enter the same password twice
	(lyskom-insert-string 'repeat-failure)
	nil)
       (t
	;; Entered the same password twice
	(let ((new-person (blocking-do 'create-person name
                                       password
                                       (lyskom-create-flags nil
                                                            nil
                                                            nil
                                                            nil
                                                            nil
                                                            nil
                                                            nil
                                                            nil)
                                       nil)))
	  (if (null new-person)
	      (lyskom-insert-string 'could-not-create-you)
	    ;; Raise a flag so the user will be added to the
	    ;; presentation conference after login
	    (setq lyskom-is-new-user password))
	  new-person)))))
   (t
    ;; Do not create a new person
    nil)))


(defvar lyskom-known-servers)
(defun lyskom-complete-server-name (string pred flag)
  "Programmed completion function for lyskom-read-server-name."
  (cond ((null flag)			; try-completion
	 (or (try-completion string lyskom-known-servers) string))
	((eq flag 'lambda)		; test-completion
	 (cond ((assoc string lyskom-known-servers) t)
	       ((try-completion string lyskom-known-servers) nil)
	       (t t)))
	((eq flag 't)			; all-completions
	 (or (all-completions string lyskom-known-servers)
	     (list string)))))

(defun lyskom-read-server-name ()
  "Read the name of a LysKOM server.
Copmpletion is done on the servers i kom-server-aliases and
kom-builtin-server-aliases. If an alias name is entered, the
corresponding address is returned."
  ;; Create a completion table like
  ;; (("kom.lysator.liu.se" . "kom.lysator.liu.se")
  ;;  ("LysKOM" . "kom.lysator.liu.se"))
  (let ((lyskom-known-servers
	 (append (mapcar (lambda (pair)
			   (cons (car pair) (car pair)))
			 (append kom-server-aliases kom-builtin-server-aliases))
		 (mapcar (lambda (pair)
			   (cons (cdr pair) (car pair)))
			 (append kom-server-aliases kom-builtin-server-aliases))))
	(completion-ignore-case t)
	server)
    (setq lyskom-known-servers (lyskom-maybe-frob-completion-table lyskom-known-servers))

    (while (null server)
      (setq server (lyskom-completing-read (lyskom-format 'server-q)
					   'lyskom-complete-server-name
                                           nil t
                                           (cons (or (getenv "KOMSERVER")
                                                     lyskom-default-server
                                                     kom-default-server
                                                     "") 0))))
    (or (cdr (lyskom-string-assoc server lyskom-known-servers))
	server)))


;;; ================================================================
;;;                        The LysKOM mode.

;; The LysKOM mode should not be inherited if we create a new buffer
;; and default-major-mode is nil.
(put 'lyskom-mode 'mode-class 'special)

(defun lyskom-mode ()
  "\\<lyskom-mode-map>Mode for LysKOM client.
Commands:
\\[kom-next-command]	Do the default action. This can be to read the next text,select
	next conference with unread texts or whatever the prompt says.
\\[kom-go-to-conf]	Go to a conference. LysKOM will ask you for a conference
	and make you a member of it if you are not already.
\\[kom-list-conferences]	List conferences matching a given string.
\\[kom-list-persons]	List persons matching a given string.

\\[kom-list-news]	List the conferences you have unread texts in.
\\[kom-go-to-next-conf]	Go to the next conference with unread texts.

\\[kom-membership]	Display a buffer with the list of conferences you are member in.

\\[kom-quit]	Leave this LysKOM session.
\\[kom-who-is-on]	Show a list of all the users of lyskom right now.

\\[kom-extended-command]	Read a command using the minibuffer and execute it.
	This is another way to give commands.

\\[kom-write-text]	Start writing a new text.
\\[kom-write-comment]	Start writing a comment to the last read article.
\\[kom-private-answer]	Start writing a personal answer to the author of the last
	read article.
\\[kom-send-letter]	Start writing a letter to a person or conference.

\\[kom-page-next-command]	Clear the page and do what \\[kom-next-command] does.
\\[kom-line-next-command]	Do what \\[kom-next-command] does, but scroll at most 1 line.

0 .. 9	Give a numeric argument to the next command.
\\[describe-mode]	Display this help text.

\\[kom-busy-wait]	Put the lyskom-session in wait mode. The next created text with 
	a priority higher than that of the next conference you are going
	to will be read directly when it is created.
\\[kom-set-unread]	Mark a number of texts as unread.
\\[kom-jump]	Skip (mark as read) all the comments to this article recursively.
\\[kom-display-time]	Show the current date and time.

\\[kom-change-presentation]	Change your presentation.
\\[kom-view]	View the specified text.
\\[kom-view-commented-text]	View the text that the current text comments or is a footnote to.
\\[kom-review-presentation]	Show the presentation for a person or a conferencce.

\\[kom-review-comments]	View all comments to the current text.
\\[kom-review-tree]	View all comments to the current text and step through the tree
	in depth-first order.

\\[kom-find-root-review]	View the complete comment tree.
\\[kom-find-root]	Show the root text of this comment tree.
\\[kom-review-by-to]	View the last (first or all) article written by named author
	in a named conference.

\\[kom-mark-text]	Create a mark on a text.
\\[kom-unmark-text]	Remove the mark on a text.
\\[kom-review-marked-texts]	View all text marked with a certain mark.
\\[kom-review-all-marked-texts]	View all marked text.

\\[kom-view-next-new-text]	Read the next text from the list of unread.
\\[kom-review-next]	Continue the viewing.
\\[kom-review-stack]	Show the stack of things we are viewing.
\\[kom-review-clear]	Clear the stack of things we are viewing.
\\[kom-review-backward]	Toggles the read order of reviewed texts. This can only be done
	when viewing texts with \\[kom-review-by-to], \\[kom-review-marked-texts] and \\[kom-review-all-marked-texts].

\\[kom-status-conf]	Show the status of a conference.
\\[kom-status-person]	Show the status of a person
\\[kom-save-text]	Save the text you are looking at to a file.

\\[kom-get-abuse]	Get an insulting text.
\\[kom-get-appreciation]	Get an encouraging text.

\\[kom-add-self]	Become a member of a conference.
\\[kom-sub-self]	Removes you as a member of a conference.

All bindings (this is here due to the fact that inconsistensies while 
developping this package are frequent):
\\{lyskom-mode-map}
Entry to this mode runs lyskom-mode-hook.

Functions and variables beginning with kom- are intended for the user to see,
set or call.
Functions and variables beginning with lyskom- are not intended for the user
to see, set of call."
  (interactive)
  (lyskom-clear-vars)
  (lyskom-mode-name-from-host)
  (setq mode-line-buffer-identification
	; Must start with a string to be handled properly, it seems.
	(list "" 'mode-line-server-name  ": " 'mode-line-conf-name))

  (setq major-mode 'lyskom-mode)
  (setq mode-name "LysKOM")
  (setq mode-line-process (lyskom-get-string 'mode-line-working))
  (use-local-map lyskom-mode-map)
  (lyskom-set-menus 'lyskom-mode lyskom-mode-map)
  (run-hooks 'lyskom-mode-hook)

  (buffer-disable-undo (current-buffer))
  (setq buffer-read-only t))

(defun lyskom-clear-vars ()
  "Set up buffer-local vars."
  (lyskom-save-variables (lyskom-proc
                          lyskom-pers-no
                          lyskom-last-viewed
                          lyskom-unparsed-buffer
                          lyskom-unparsed-marker
                          lyskom-server-info
                          lyskom-server-name)
      (kill-all-local-variables))
    (lyskom-setup-local-variables)
    (setq lyskom-do-when-done (cons kom-do-when-done kom-do-when-done))
    (setq lyskom-output-queues (make-vector 10 nil))
    (setq lyskom-collate-table lyskom-default-collate-table)
    (setq lyskom-char-classes nil)
    (let ((i 0))
      (while (< i 10)
        (aset lyskom-output-queues i (lyskom-queue-create))
        (setq i (1+ i))))
    (setq lyskom-pending-calls nil)
    (lyskom-set-mode-line
     (lyskom-get-string 'not-present-anywhere)))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: reading.el
;;;;
;;;; This file contains functions that manage membership and reading
;;;; lists, namely lyskom-membersip lyskom-reading-list and
;;;; lyskom-to-do-list. These are called both from prefetch and from
;;;; startup procedures.
;;;; 


(defun lyskom-enter-map-in-to-do-list (map conf-stat membership)
  "Takes a MAP and enters all its listed text-nos in the conference CONF-STAT.
This works by modifying the lyskom-to-do-list which in some cases
also means modifying the lyskom-reading-list. The zero text-nos are skipped."
  (let ((list (lyskom-list-unread map membership))
        (mship (lyskom-try-get-membership (conf-stat->conf-no conf-stat))))
    (when (and list mship)
      (read-list-enter-read-info
       (lyskom-create-read-info 
	'CONF conf-stat
	(membership->priority 
	 (lyskom-try-get-membership (conf-stat->conf-no conf-stat)))
	(lyskom-create-text-list
	 list))
       lyskom-to-do-list))))


(defun lyskom-sort-to-do-list ()
  "Sort lyskom-to-do-list in order of membership priorities. 
The priorities for CONF elements are updated to match the membership
priorities. Elements that are not of type CONF appear first on the list
within their priority. This may not be totally accurate, but it's a
reasonable guess."
  (let ((todo (read-list->all-entries lyskom-to-do-list))
        (info nil))

    ;; Update the priorities in the read list

    (while todo
      (setq info (car todo))
      (setq todo (cdr todo))
      (when (eq (read-info->type info) 'CONF)
        (let ((mship 
               (lyskom-get-membership 
                (conf-stat->conf-no (read-info->conf-stat info)) t)))
          (when mship
            (set-read-info->priority info
                                     (membership->priority mship))))))

    ;; Sort the todo list

    (setq lyskom-to-do-list (cons 'READ-LIST
                                  (sort (read-list->all-entries lyskom-to-do-list)
                                        'lyskom-read-info-<)))
    (lyskom-update-prompt)))

(defun lyskom-read-info-< (a b)
  (cond ((< (read-info->priority a) (read-info->priority b)) nil)
        ((> (read-info->priority a) (read-info->priority b)) t)

        ;; Both are confs of equal priority; check position in mship
        ((and (eq (read-info->type a) 'CONF) 
              (eq (read-info->type b) 'CONF))
         (< (lyskom-membership-position 
             (conf-stat->conf-no (read-info->conf-stat a)))
            (lyskom-membership-position 
             (conf-stat->conf-no (read-info->conf-stat b)))))

        ;; A is a CONF and B is not; B is greater.
        ((eq (read-info->type a) 'CONF) nil)

        ;; Both are not CONF, so A is not less than B
        (t t)))



;;; ================================================================
;;; Fundamental membership cache functions

(def-komtype mship-list-node (prev next data))
(def-komtype membership-list ((head :automatic nil)
                              (tail :automatic nil)
                              (size :automatic 0)))
;;; (def-komtype smship (id priority position))


;;; ----------------------------------------------------------------
;;; INSERTION FUNCTIONS
;;;
;;; There are three functions for inserting memberships into a list.
;;;
;;; Use lyskom-membership-list-insert when inserting memberships in
;;; random order. It uses a heuristic based on the priority of the
;;; membership to determine whether to search from the front of back
;;; of the list.
;;;
;;; Use lyskom-membership-list-append when you know that the position
;;; of the membership is towards the end of the list.
;;;
;;; Use lyskom-membership-list-prepend when you know that the position
;;; of the membership is towards the front of the list.
;;;
;;;
;;; The heuristic is optimized for randomly inserting memberships. If
;;; the list is near-full and a single membership is to be inserted,
;;; other heuristics might perform better. If the approximate position
;;; of the membership is already known, this heuristic is not the best.
;;;

(defun lyskom-membership-list-compare-next (mship next &optional after)
  (and next
       (or (> (membership->priority (mship-list-node->data next))
              (membership->priority mship))
           (and (= (membership->priority (mship-list-node->data next))
                   (membership->priority mship))
                (membership->position mship)
                (if after
                    (>= (membership->position mship)
                        (membership->position (mship-list-node->data next)))
                  (> (membership->position mship)
                     (membership->position (mship-list-node->data next))))))))

(defun lyskom-membership-list-compare-prev (mship prev)
  (and prev
       (or (< (membership->priority (mship-list-node->data prev))
              (membership->priority mship))
           (and (= (membership->priority (mship-list-node->data prev))
                   (membership->priority mship))
                (membership->position mship)
                (<= (membership->position mship)
                    (membership->position (mship-list-node->data prev)))))))  


(defun lyskom-membership-list-insert (mship-list mship &optional simulate)
  "Insert a new membership MSHP into MSHIP-LIST."
  (if (and (membership-list->head mship-list)
           (membership-list->tail mship-list)
           (< (- (membership->priority mship)
                 (membership->priority (mship-list-node->data (membership-list->head mship-list))))
              (- (membership->priority (mship-list-node->data (membership-list->tail mship-list)))
                 (membership->priority mship))))
      (lyskom-membership-list-append mship-list mship simulate)
    (lyskom-membership-list-prepend mship-list mship simulate)))


(defun lyskom-membership-list-insert-position (mship prev next)
  "Return position MSHIP should have if inserted between PREV and NEXT.
Internal function for lyskom-membership-list-insert."

  ;; If it already has a position that is between the positions of cur and prev
  ;; then we do not alter that position. If it has some other position or no
  ;; position, set its position to the position of cur (which is appropriate
  ;; when adding a new membership). We do this even if there is room between
  ;; prev and cur since such a hole probably indicates that we haven't gotten
  ;; the entire membership from the server -- once we have all memberships there
  ;; shouldn't be any holes left.
  
  (if (or (null (membership->position mship))
          (<= (membership->position mship)
              (if prev (membership->position (mship-list-node->data prev)) -1))
          (>= (membership->position mship)
              (if next (membership->position (mship-list-node->data next)) 
                lyskom-max-int)))
      (cond (next (membership->position (mship-list-node->data next)))
            (prev (1+ (membership->position (mship-list-node->data prev))))
            (t 0))
    (membership->position mship)))

(defun lyskom-membership-list-prepend (mship-list mship &optional simulate)
  "Insert new membership MSHIP into MSHIP-LIST."
  (let ((cur (membership-list->head mship-list))
        (prev nil))

    ;; Search for the element in the list at which we want to insert
    ;; the new membership.

    (while (lyskom-membership-list-compare-next mship cur)
      (setq prev cur cur (mship-list-node->next cur)))


    (if simulate
        (lyskom-membership-list-insert-position mship prev cur)

      (let ((new (lyskom-create-mship-list-node prev cur mship)))
        (set-membership->position 
         mship (lyskom-membership-list-insert-position mship prev cur))

        ;; If cur is nil, then we want to insert at the end of the list
        ;; If prev is nil, then we want to insert at the beginning of the list
        ;; If both are nil, the list is empty and we are inserting the first element

        (if prev
            (set-mship-list-node->next prev new)
          (set-membership-list->head mship-list new))
        (if cur
            (set-mship-list-node->prev cur new)
          (set-membership-list->tail mship-list new))

        (setq prev new)

        ;; If the position we chose for the new element collides with the position of
        ;; the element following it, we adjust the positions of following elements
        ;; until all elements again have unique positions.

        (while (and cur (eq (membership->position (mship-list-node->data prev))
                            (membership->position (mship-list-node->data cur))))
          (set-membership->position (mship-list-node->data cur)
                                    (1+ (membership->position (mship-list-node->data cur))))
          (setq prev cur cur (mship-list-node->next cur)))

        (set-membership-list->size mship-list (1+ (membership-list->size mship-list)))
        new))))


(defun lyskom-membership-list-append (mship-list mship &optional simulate)
  "Like lyskom-insert-membership, but searches from the end of the list"
  (let ((cur (membership-list->tail mship-list))
        (prev nil))

    (while (lyskom-membership-list-compare-prev mship cur)
      (setq prev cur cur (mship-list-node->prev cur)))

    (if simulate


      (if (or (null (membership->position mship))
              (<= (membership->position mship)
                  (if cur (membership->position (mship-list-node->data cur)) -1))
              (>= (membership->position mship)
                  (if prev (membership->position (mship-list-node->data prev)) lyskom-max-int)))
          (set-membership->position mship
                                (cond (prev (membership->position (mship-list-node->data prev)))
                                      (cur (1+ (membership->position (mship-list-node->data cur))))
                                      (t 0))))

    (if simulate
        (lyskom-membership-list-insert-position mship prev cur)

      (let ((new (lyskom-create-mship-list-node cur prev mship)))
        (set-membership->position 
         mship (lyskom-membership-list-insert-position mship cur prev))

        ;; If cur is nil, then we want to insert at the end of the list
        ;; If prev is nil, then we want to insert at the beginning of the list
        ;; If both are nil, the list is empty and we are inserting the first element

        (if cur
            (set-mship-list-node->next cur new)
          (set-membership-list->head mship-list new))
        (if prev
            (set-mship-list-node->prev prev new)
          (set-membership-list->tail mship-list new))

        ;; Set up for scanning back to the end of the list to adjust positions
        ;; of elements that are after the newly inserted element.

        (setq prev new cur (mship-list-node->next new))

        ;; If the position we chose for the new element collides with the position of
        ;; the element following it, we adjust the positions of following elements
        ;; until all elements again have unique positions.

        (while (and cur (eq (membership->position (mship-list-node->data prev))
                            (membership->position (mship-list-node->data cur))))
          (set-membership->position (mship-list-node->data cur)
                                    (1+ (membership->position (mship-list-node->data cur))))
          (setq prev cur cur (mship-list-node->next cur)))

        (set-membership-list->size mship-list (1+ (membership-list->size mship-list)))
        new)))))


(defun lyskom-membership-list-delete (mship-list node)
  "Remove NODE from MSHIP-LIST"
  (if (mship-list-node->next node)
      (set-mship-list-node->prev (mship-list-node->next node)
                                 (mship-list-node->prev node))
    (set-membership-list->tail mship-list
                               (mship-list-node->prev node)))

  (if (mship-list-node->prev node)
      (set-mship-list-node->next (mship-list-node->prev node)
                                 (mship-list-node->next node))
    (set-membership-list->head mship-list (mship-list-node->next node)))

  (setq node (mship-list-node->next node))
  (while node
    (set-membership->position (mship-list-node->data node)
                              (1- (membership->position (mship-list-node->data node))))
    (setq node (mship-list-node->next node)))

  (set-membership-list->size mship-list (1- (membership-list->size mship-list))))

(defun lyskom-membership-list-move (mship-list node)
  "Move the node NODE in MSHIP-LIST to its new position.
Returns non-nil value if the membership actually moved."
  (let* ((prev (mship-list-node->prev node))
         (next (mship-list-node->next node))
         (mship (mship-list-node->data node))
         (moved nil)
         (new-pos nil))

    (cond
     ((lyskom-membership-list-compare-next mship next t) ; Move right
      (setq prev nil)
      (while (lyskom-membership-list-compare-next mship next t)
        (setq moved t)
        (setq new-pos (membership->position (mship-list-node->data next)))
        (set-membership->position (mship-list-node->data next) (1- new-pos))
        (setq prev next next (mship-list-node->next next)))
      (set-membership->position mship new-pos))

     ((lyskom-membership-list-compare-prev mship prev) ; Move left
      (setq next nil)
      (while (lyskom-membership-list-compare-prev mship prev)
        (setq moved t)
        (setq new-pos (membership->position (mship-list-node->data prev)))
        (set-membership->position (mship-list-node->data prev) (1+ new-pos))
        (setq next prev prev (mship-list-node->prev prev)))
      (set-membership->position mship new-pos))

     (t                                 ; Stay in the same place
      (setq prev (mship-list-node->prev node))
      (setq next (mship-list-node->next node))
      (setq new-pos (membership->position mship))))


    ;; Remove the node from its current position

    (if (mship-list-node->prev node)
        (set-mship-list-node->next (mship-list-node->prev node)
                                   (mship-list-node->next node))
      (set-membership-list->head mship-list (mship-list-node->next node)))
    (if (mship-list-node->next node)
        (set-mship-list-node->prev (mship-list-node->next node)
                                   (mship-list-node->prev node))
      (set-membership-list->tail mship-list (mship-list-node->prev node)))

    ;; Splice it into the list at the new position

    (set-mship-list-node->prev node prev)
    (set-mship-list-node->next node next)

    (if prev 
        (set-mship-list-node->next prev node)
      (set-membership-list->head mship-list node))

    (if next
        (set-mship-list-node->prev next node)
      (set-membership-list->tail mship-list node))
    moved
  ))



;;; ================================================================
;;; The following functions are concerned with managing the cache

(def-kom-var lyskom-mship-cache nil
  "Membership cache. Do not alter directly."
  local)

(defsubst lyskom-mship-cache-index () (aref lyskom-mship-cache 0))
(defsubst lyskom-mship-cache-data  () (aref lyskom-mship-cache 1))

(defun lyskom-mship-cache-create ()
  "Initialize the membership cache to empty."
  (vector (lyskom-make-hash-table :size 300 :test 'eq) 
          (lyskom-create-membership-list)))

(defun lyskom-mship-cache-get (conf-no)
  "Get the membership for CONF-NO from the membership cache."
  (lyskom-gethash conf-no (lyskom-mship-cache-index)))

(defun lyskom-mship-cache-put (mship)
  "Add MSHIP to the membership cache."
  (lyskom-puthash (membership->conf-no mship)
                  (lyskom-membership-list-insert (lyskom-mship-cache-data) mship)
                  (lyskom-mship-cache-index)))

(defun lyskom-mship-cache-append (mship)
  "Add MSHIP to the membership cache."
  (lyskom-puthash (membership->conf-no mship)
                  (lyskom-membership-list-append (lyskom-mship-cache-data) mship)
                  (lyskom-mship-cache-index)))

(defun lyskom-mship-cache-del (conf-no)
  "Delete CONF-NO from the membership cache."
  (let ((node (lyskom-mship-cache-get conf-no)))
    (when node
      (lyskom-membership-list-delete (lyskom-mship-cache-data) node)
      (lyskom-remhash conf-no (lyskom-mship-cache-index)))))

(defun lyskom-add-memberships-to-membership (memberships)
  "Adds newly fetched MEMBERSHIPS to the membership list."
  (lyskom-with-lyskom-buffer
    (lyskom-traverse mship memberships
      (unless (lyskom-mship-cache-get (membership->conf-no mship))
        (lyskom-mship-cache-append mship)))))

(defun lyskom-insert-membership (mship)
  "Add MSHIP into lyskom-membership, sorted by priority."
  (lyskom-with-lyskom-buffer
    (lyskom-mship-cache-put mship)
    (lp--update-buffer (membership->conf-no mship))
    ))


(defun lyskom-replace-membership (mship)
  "Replace the membership MSHIP."
  (lyskom-with-lyskom-buffer
    (let ((node (lyskom-mship-cache-get (membership->conf-no mship))))
      (set-mship-list-node->data node mship)
      (if node
          (when (lyskom-membership-list-move (lyskom-mship-cache-data) node)
            (lyskom-sort-to-do-list))
        (lyskom-mship-cache-put mship)))
    (lp--update-buffer (membership->conf-no mship))))

(defun lyskom-remove-membership (conf-no)
  "Remove the membership for CONF-NO from lyskom-membership."
  (lyskom-with-lyskom-buffer
    (lyskom-mship-cache-del conf-no)
    (lp--update-buffer conf-no)))

(defun lyskom-membership-position (conf-no)
  "Return the position of the membership for CONF-NO."
  (lyskom-with-lyskom-buffer
    (membership->position (lyskom-get-membership conf-no t))))


(defun lyskom-init-membership ()
  "Initialize membership cache and information."
  (setq lyskom-mship-cache
        (lyskom-mship-cache-create)))

(defun lyskom-membership-length ()
  "Return the size of the membership list."
  (membership-list->size (lyskom-mship-cache-data)))

(defun lyskom-get-membership (conf-no &optional want-passive)
  "Return membership for conference CONF-NO.
If optional WANT-PASSIVE is non-nil, also return passive memberships.

If the membership has not been cached, a blocking call is made to the
server to attempt to get it, so this function should not be used from
a callback function."
  (lyskom-with-lyskom-buffer
    (or (lyskom-try-get-membership conf-no want-passive)
        (and (not (lyskom-membership-is-read))
             (let ((mship (blocking-do 'query-read-texts lyskom-pers-no conf-no t 0)))
               (when (and mship (lyskom-visible-membership mship))
                 (lyskom-add-membership mship conf-no))
               (when (or want-passive
                         (not (membership-type->passive (membership->type mship))))
                 mship))))))


(defun lyskom-try-get-membership (conf-no &optional want-passive)
  "Return membership for conference CONF-NO.
If optional WANT-PASSIVE is non-nil, also return passive memberships.

This call does not block. If the membership has not been cached, this
call will return nil." 
  (lyskom-with-lyskom-buffer
    (let ((mship (lyskom-mship-cache-get conf-no)))
      (when (and mship
                 (or want-passive
                     (not (membership-type->passive 
                           (membership->type
                            (mship-list-node->data mship))))))
        (mship-list-node->data mship)))))


(defun lyskom-query-membership-position (priority position)
  "Return the real position of a membership with PRIORITY and POSITION.
The position returned is the position at which the membership would be
placed in the membership cache."
  (let ((mship (lyskom-create-membership position nil nil priority 
                                         nil nil nil nil nil)))
    (lyskom-membership-list-insert (lyskom-mship-cache-data) mship t)))





;;; ================================================================
;;; Testing


;;;(defun lps (l)
;;;  (let ((cur (membership-list->head l))
;;;        res)
;;;    (while cur
;;;      (setq res (cons (cons (smship->id (mship-list-node->data cur))
;;;                            (smship->position (mship-list-node->data cur)))
;;;                      res)
;;;            cur (mship-list-node->next cur)))
;;;    (nreverse res)))
;;;
;;;
;;;
;;;(progn (setq l (lyskom-create-membership-list))
;;;(setq a (lyskom-create-smship 'a 255 0))
;;;(setq b (lyskom-create-smship 'b 100 1))
;;;(setq c (lyskom-create-smship 'c 100 2))
;;;(setq d (lyskom-create-smship 'd 100 3))
;;;(setq e (lyskom-create-smship 'e  50 4))
;;;(setq f (lyskom-create-smship 'f  50 5))
;;;(setq g (lyskom-create-smship 'g  20 6))
;;;(setq h (lyskom-create-smship 'h   1 7)))
;;;
;;;(setq x (lyskom-create-smship 'x 100 nil))
;;;
;;;(mapcar (lambda (el) (lyskom-membership-list-insert l el)) (list c g f b d a x))

;; (let ((mx 0)
;;       (times nil))
;;   (while (<= mx 500)
;; ;    (garbage-collect)
;;     (message "%d" mx)
;;     (let ((enter-time nil)
;;           (exit-time nil)
;;           (l (lyskom-create-membership-list))
;;           (i 0)
;;           (x 0)
;;           (y 0))
;;       (garbage-collect)
;;       (setq mx (+ mx 10))
;;       (setq enter-time (current-time))
;;       (while (< i mx)
;;         (let* ((val (random mx))
;;                (el (lyskom-create-smship i val (- mx val))))
;;           (lyskom-membership-list-insert l el))
;;         (setq i (1+ i)))
;;       (setq exit-time (current-time))
;;       (princ (format "%05d %.8f %.8f %4d %4d\n"
;;                      mx 
;;                      (elp-elapsed-time enter-time exit-time)
;;                      (/ (elp-elapsed-time enter-time exit-time) mx)
;;                      x
;;                      y))
;;       (sit-for 0)
;;       (setq times (cons (cons mx
;;                               (elp-elapsed-time enter-time exit-time)) times))
;; ))
;;   times)


;; (defun lyskom-map-membership-list (fn list)
;;   (let ((cur (membership-list->tail list))
;;         (res nil))
;;     (while cur
;;       (setq res (cons (funcall fn (mship-list-node->data cur)) res)
;;             cur (mship-list-node->prev cur)))
;;     res))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: internal.el
;;;;
;;;; Here are internal functions that handles the different kom-queue
;;;; calls. Add a call, apply the handler when a call is done.
;;;;
;;;; Originally written:    ceder
;;;; Completely rewritten:  Inge Wallin
;;;;


;;;; ================================================================
;;;;                          Variables.


(defvar lyskom-call-data nil
  "This is an assoc-list of data for the kom-queues.
Each element on the list has the following format:

(NAME . KOM-QUEUE)

NAME    is an atom, the name of the kom-queue.	A kom-queue is a way
        to send questions to the LysKOM server and deal with the replies 
        in a controlled way.

KOM-QUEUE is a kom-queue.")

(make-variable-buffer-local 'lyskom-call-data)


;;; ================================================================
;;;                   Data type kom-queue

;;;
;;; Each kom-queue consists of the following 4 fields:
;;;     PENDING -
;;;         a list where each element represents a call to a service on 
;;;         the server, or a call to lyskom-collect, lyskom-use or 
;;;         lyskom-run. The elements are lists. The first element on each 
;;;         list is a key to what it represents, as described below.
;;;     COLLECT-FLAG - 
;;;          t means this queue is collecting results for a future 
;;;          lyskom-use of lyskom-list-use. The results are collected
;;;          on collect-queue
;;;     COLLECT-QUEUE -
;;;          This is where the results described above are collected.
;;;     HALTED -
;;;          An integer counting the number of times this queue is halted.
;;;          A call to lyskom-halt increments this counter, a call to
;;;          lyskom-resume decrements it.
;;;
;;;
;;; The items on PENDING is one of the following:
;;;
;;; 	('CALL REF-NO PARSER PARSER-DATA HANDLER HANDLER-DATA)
;;; 		A call that has not yet returned.
;;; 
;;; 	('PARSED REF-NO RESULT HANDLER HANDLER-DATA)
;;; 		A call that has returned, but the result can not be
;;; 		handled until all previous calls has returned.
;;; 
;;; 	('COLLECT)
;;; 		Marks the start of a sequence of results that will be
;;; 		handled by a multi-handler or multi-list-handler.
;;; 
;;; 	('COLLECT-IGNORE)
;;; 		Marks the start of a sequence of results that will be 
;;; 		handled by a multi-handler of multi-list-handler with
;;; 		errors stripped away.
;;; 
;;; 	('USE MULTI-HANDLER MULTI-HANDLER-DATA)
;;; 		Marks the end of a sequence. MULTI-HANDLER is called
;;; 		when all calls before this element have been handled.
;;; 		MULTI-HANDLER is a function whose first arguments are
;;; 		the results from calls between previous COLLECT and
;;; 		this USE.  MULTI-HANDLER-DATA is optional.  If it
;;; 		exists it is a list of more arguments to send to
;;; 		MULTI-HANDLER.
;;; 
;;; 	('LIST-USE MULTI-HANDLER MULTI-HANDLER-DATA)
;;; 		Marks the end of a sequence. MULTI-HANDLER is called
;;; 		when all calls before this element have been handled.
;;; 		MULTI-HANDLER is a function whose first argument is a
;;; 		list of all results from calls between previous
;;; 		COLLECT and this LIST-USE. MULTI-HANDLER-DATA is
;;; 		optional. If it exists it is a list of more arguments
;;; 		to send to MULTI-HANDLER.
;;; 
;;; 	('RUN FUNCTION FUNCTION-ARGS)
;;; 		Run FUNCTION when all calls before this have been handled.
;;;
;;;


(defun kom-queue-create ()
  "Creates a new instance of an empty kom-queue."
  (vector (lyskom-queue-create)
	  nil
	  (lyskom-queue-create)
	  0))


(defun kom-queue->pending (queue)
  "Returns the pending field of the kom-queue QUEUE."
  (elt queue 0))


(defun kom-queue->collect-flag (queue)
  "Returns the collect-flag field of the kom-queue QUEUE."
  (elt queue 1))


(defun kom-queue->collect-queue (queue)
  "Returns the collect-queue field of the kom-queue QUEUE."
  (elt queue 2))


(defun set-kom-queue-collect-flag (queue new-val)
  "Set the collect-flag field of the kom-queue QUEUE to NEW-VAL."
  (aset queue 1 new-val))


(defun kom-queue-halt (queue)
  "Halts the kom-queue QUEUE."
  (aset queue 3 (1+ (elt queue 3))))


(defun kom-queue-resume (queue)
  "Resume execution on the kom-queue QUEUE."
  (if (eq (elt queue 3) 0)
      (signal 'lyskom-internal-error
	      (list "kom-queue-resume called on an unhalted queue: "
		    queue))
    (aset queue 3 (1- (elt queue 3)))))


(defun kom-queue-is-halted (queue)
  "Return t if the kom-queue QUEUE is halted at least once."
  (> (elt queue 3) 0))



;;; ================================================================
;;;               Entry points to this communication packet.


(defun lyskom-collect (kom-queue)
  "Collect the results of future calls via KOM-QUEUE.
The results of the calls will be available to the multi-handler.
See lyskom-use and lyskom-list-use."
  (lyskom-call-add kom-queue 'COLLECT))


(defun lyskom-collect-ignore-err (kom-queue)
  "Collect the result of future calls via KOM-QUEUE.
The result of the calls will be available to the multi-handler with the calls
producing errors stripped."
  (lyskom-call-add kom-queue 'COLLECT-IGNORE))


(defun lyskom-use (kom-queue multi-handler &rest multi-handler-data)
  "Use the previously collected results from calls to the server.
Args: KOM-QUEUE MULTI-HANDLER &rest MULTI-HANDLER-DATA

MULTI-HANDLER is a function that is called when all previous results
have been handled. MULTI-HANDLER-DATA is a list of additional
arguments the multi-handler wants.

See also lyskom-list-use."
  (lyskom-call-add kom-queue 'USE multi-handler multi-handler-data)
  (lyskom-check-call kom-queue))


(defun lyskom-list-use (kom-queue multi-handler &rest multi-handler-data)
  "Use the previously collected results from calls to the server.
Args: KOM-QUEUE MULTI-HANDLER &rest MULTI-HANDLER-DATA

MULTI-HANDLER is a function that is called when all previous
results have been handled. The first argument is a list of
the results. MULTI-HANDLER-DATA is a list of additional
arguments the multi-handler wants.

The difference between lyskom-use and lyskom-list-use is the way
the MULTI-HANDLER receives the data. lyskom-list-use sends them as
a list, lyskom-use as different parameters."
  (lyskom-call-add kom-queue 'LIST-USE multi-handler multi-handler-data)
  (lyskom-check-call kom-queue))


(defun lyskom-run (kom-queue function &rest function-args)
  "Call a function when all calls have been handled.
Args: KOM-QUEUE FUNCTION &rest FUNCTION-ARGS
Register a FUNCTION that shall be called with FUNCTION-ARGS when
all previous calls to the server via KOM-QUEUE have been handled."
  (lyskom-call-add kom-queue 'RUN function function-args)
  (lyskom-check-call kom-queue))


(defun lyskom-halt (queue-name)
  "Prohibit execution of handlers on QUEUE-NAME.
The execution will resume when (lyskom-resume KOM-QUEUE) is called."
  (let ((queue-pair (assq queue-name lyskom-call-data)))
    (if (null queue-pair)
	(setq queue-pair (lyskom-add-new-queue queue-name)))
    (kom-queue-halt (cdr queue-pair))))
    

(defun lyskom-resume (kom-queue)
  "Resume execution of waiting handlers on KOM-QUEUE.
See documentation for lyskom-halt."
  (let ((queue (assq kom-queue lyskom-call-data)))
    (cond
     ((null queue)			;A new kom-queue?
      (signal 'lyskom-internal-error
	      (list "lyskom-resume called on an unused queue:"
		    kom-queue)))
     ((kom-queue-is-halted (cdr queue)) ;A halted queue?
      (kom-queue-resume (cdr queue))    ;Resume execution on the queue.
      (lyskom-check-call kom-queue))	;Run any pending handlers.
     (t					;The queue was not halted. This
      (signal 'lyskom-internal-error	;is an error.
	      (list "lyskom-resume:"
		    kom-queue "(not halted)"))))))


(defun lyskom-call (kom-queue ref-no handler handler-data
			       parser &rest parser-data)
  "Add information about a call that has not yet returned to kom-queue.
Arguments: KOM-QUEUE REF-NO HANDLER HANDLER-DATA
	   PARSER &rest PARSER-DATA."
  (lyskom-call-add kom-queue 'CALL ref-no 
		   parser parser-data handler handler-data))

(defun lyskom-fake-call (kom-queue ref-no handler handler-data)
  "Add information about a call that will not return from the server,
but will be filled in by some other function."
  (lyskom-call-add kom-queue 'CALL ref-no
                   nil nil handler handler-data))

(defun lyskom-complete-call (kom-queue ref-no result)
  "Force a call placed on KOM-QUEUE with reference number REF-NO to return 
RESULT. This should only be used to complete calls placed on the queue using
lyskom-fake-call, or the parser might get confused."
  (let ((call-info (lyskom-locate-ref-no kom-queue ref-no)))
    (if call-info
        (progn
          (lyskom-tr-call-to-parsed call-info result)
          (lyskom-check-call kom-queue)))))


(defun lyskom-cancel-calls (ref-nos)
  "Cancel the calls in the list REF-NOS.
Calls that have been sent to the server will have their callbacks
canceled, but as they have been sent, the client must wait for them
to complete in some way."
  (let ((unsent nil))
    (lyskom-traverse queue lyskom-output-queues
      (lyskom-traverse ref-no ref-nos
        (when (assq ref-no (car (cdr queue)))
          (setq unsent (cons ref-no unsent)))))

    ;;    (lyskom-format-insert "REF-NOS\n")
    ;;    (lyskom-format-insert "    %#1s\n" (format "%S" ref-nos))
    ;;    (lyskom-format-insert "UNSENT\n")
    ;;    (lyskom-format-insert "    %#1s\n" (format "%S" unsent))

    (lyskom-traverse queue lyskom-output-queues
      (lyskom-queue-remove-matching queue
                                    (lambda (el) (memq (car el) unsent))))

    (lyskom-traverse queue lyskom-call-data
      (lyskom-queue-remove-matching (kom-queue->pending (cdr queue))
                                    (lambda (el) 
                                      (or (and (eq (car el) 'CALL)
                                               (memq (car (cdr el)) unsent))
                                          (and (eq (car el) 'PARSED)
                                               (memq (car (cdr el)) ref-nos)))))
      (lyskom-traverse call-data (car (cdr (kom-queue->pending (cdr queue))))
        (when (and (eq (car call-data) 'CALL)
                   (memq (car (cdr call-data)) ref-nos))
          ;;          (lyskom-format-insert "Canceling callback %#1d\n" (car (cdr call-data)))
          (setcdr call-data
                  (list (elt (cdr call-data) 0) ; ref-no
                        (elt (cdr call-data) 1) ; parser
                        (elt (cdr call-data) 2) ; parser-data
                        nil             ; handler, then handler-data
                        nil)))))
    (lyskom-check-output-queues)))


;;;; ================================================================
;;;;                     Internal functions.



(defun lyskom-add-new-queue (queue-name)
  "Add QUEUE-NAME to lyskom-call-data as an empty queue.
Return a dotted pair consisting of the QUEUE-NAME and the new queue."
  (let ((new-queue-list (list (cons queue-name (kom-queue-create)))))
    (if (null lyskom-call-data)
	(setq lyskom-call-data new-queue-list)
      (nconc lyskom-call-data new-queue-list))
    (car new-queue-list)))

(defun lyskom-set-queue-priority (queue-name priority)
  (put queue-name 'lyskom-queue-priority priority))

(defun lyskom-queue-priority (queue-name)
  (or (get queue-name 'lyskom-queue-priority)
      0))

(defun lyskom-call-add (queue-name type &rest data)
  "Add an entry to the kom-queue QUEUE-NAME. The entry is of type TYPE
and third argument DATA contains the rest of the necessary data."
  (let ((queue (assoc queue-name lyskom-call-data)))
    (if (null queue)
	(setq queue (lyskom-add-new-queue queue-name)))
    (lyskom-queue-enter (kom-queue->pending (cdr queue))
			(cons type data))))


(defun lyskom-send-packet (kom-queue string)
  "Send a packet to the server.
Add info on lyskom-pending-calls. Update lyskom-ref-no.
Args: KOM-QUEUE STRING."

  ;; Queue it
  (lyskom-queue-enter (aref lyskom-output-queues
			    (lyskom-queue-priority kom-queue))
		      (cons lyskom-ref-no string))
  (setq lyskom-pending-calls
	(cons (cons lyskom-ref-no kom-queue)
	      lyskom-pending-calls))
  (setq lyskom-ref-no (1+ lyskom-ref-no))
  (when (< lyskom-ref-no 0) (setq lyskom-ref-no 1))

  ;; Send something from the output queues
  (lyskom-check-output-queues))

(defun lyskom-check-output-queues ()
  "Check for pending calls to the server.
Send calls from queues with higher priority first, and make sure that at
most lyskom-max-pending-calls are sent to the server at the same time."
  (catch 'done
    (let ((i 9))
      (while (and lyskom-ok-to-send-new-calls
		  (< lyskom-number-of-pending-calls
		     lyskom-max-pending-calls))
	(while (lyskom-queue-isempty (aref lyskom-output-queues i))
	  (-- i)
	  (if (< i 0) (throw 'done nil)))
	(let ((entry (lyskom-queue-delete-first
		      (aref lyskom-output-queues i))))
	  (++ lyskom-number-of-pending-calls)
	  (lyskom-process-send-string
	   lyskom-proc
	   (concat (number-to-string (car entry)) (cdr entry) "\n")))))))

(defun lyskom-decrease-pending-calls ()
  "A reply has come.
Send a pending call or decrease lyskom-number-of-pending-calls."
  (-- lyskom-number-of-pending-calls)
  (if (< lyskom-number-of-pending-calls 0)
      (setq lyskom-number-of-pending-calls 0))
  (lyskom-check-output-queues))
	

(defun lyskom-process-send-string (process string)
  "Send PROCESS the contents of STRING.
STRING is split in several parts if the operating system can't deal
with big strings."
  (let ((tries 0))
  (while
      (condition-case err
	  (progn (lyskom-process-send-string-2 process string)
		 nil)
	(file-error
	 (if lyskom-debug-communications-to-buffer
	     (lyskom-debug-insert process "Error: " (format "%s" err))
;;;	     (save-excursion
;;;	       (set-buffer (get-buffer-create
;;;			    lyskom-debug-communications-to-buffer-buffer))
;;;	       (save-excursion
;;;		 (goto-char (point-max))
;;;		 (insert "\n" 
;;;			 (format "%s" process)
;;;			 (concat "Error: " (format "%s" err))))
;;;	       (set-buffer (process-buffer process)))
	   )
	 (cond
	  ((and (string= "writing to process" (car (cdr err)))
		(or (string= "message too long" (car (cdr (cdr err))))
		    (string= "no more processes" (car (cdr (cdr err)))))
		(> lyskom-max-packet-size 1))
	   ;; Seems to be impossible to write too long strings to TCP/IP.
	   ;; This happens on a Sequence Balance with packets longer than
	   ;; 2048 bytes. Decrease lyskom-max-packet-size and retry.
	   (setq lyskom-max-packet-size (/ lyskom-max-packet-size 2))
	   t)

	  ((and (string= "writing to process" (car (cdr err)))
		(string= "host is unreachable" (car (cdr (cdr err)))))
	   ;; The net is currently shaky. We try again in a while.
	   (lyskom-message "%s" (lyskom-format 'shaky-tcp
					       (make-string (++ tries) ?.)))
	   (sit-for 4)
	   (lyskom-message "%s" (lyskom-get-string 'retrying-tcp))
	   t)
	  
	  (t
	   ;; It was some unknown file-error. Pass it down.
	   (signal (car err) (cdr err)))))))))


(defun lyskom-process-send-string-2 (process string)
  "Send PROCESS the contents of STRING as input.
PROCESS may be a process name. At most lyskom-max-packet-size bytes
is sent with each packet. If STRING is longer it is splitted."
  (cond
   ((<= (length string) lyskom-max-packet-size)
    (process-send-string
     process
     (progn
       (if lyskom-debug-communications-to-buffer
	   (lyskom-debug-insert process "To " string))
       string)))
   (t
    (let ((i 0))
      (while (< i (length string))
	(process-send-string
	 process
	 (let ((string (substring string
				  i
				  (min (length string)
				       (+ i lyskom-max-packet-size)))))
	   (if lyskom-debug-communications-to-buffer
	       (save-current-buffer
		 (set-buffer (get-buffer-create 
			      lyskom-debug-communications-to-buffer-buffer))
		 (save-excursion
		   (goto-char (point-max))
		   (insert "\n"
                           "To " process ": "))
		 (set-buffer (process-buffer process))))
	   string))
	(setq i (+ i lyskom-max-packet-size)))))))


(defun lyskom-check-call (queue-name)
  "Check lyskom-call-data and call handlers, multi-handlers and functions.
Args: QUEUE-NAME.
HALTED -> stop
CALL -> stop
PARSED -> handle ->
       -> collect-flag? 'COLLECT -> add on temporary resultlist.
	 	        'COLLECT-IGNORE -> add on temporary resultlist if not 
			 		   error.
		        no  -> delete from lyskom-call-data.
COLLECT -> collect-flag? yes -> error!
			 no  -> set collect-flag.
COLLECT-IGNORE -> collect-flag? yes -> error!
				no -> set collect-flag.
USE ->      call handler. Delete previous parts.
LIST-USE -> call handler. Delete previous parts.
RUN ->      call function. Delete. Not allowed inside COLLECT/USE."
  (let* ((queue (cdr-safe (assoc queue-name lyskom-call-data)))
         (type nil)
         (first-pending (lyskom-queue->first (kom-queue->pending queue))))
    (while (and queue
                (not (or (kom-queue-is-halted queue)
                         (lyskom-queue-isempty (kom-queue->pending queue))
                         (eq (car first-pending) 'CALL))))
      (setq type (car first-pending))

      (cond

       ((eq type 'PARSED)
	(kom-queue-halt queue)
	(unwind-protect
	    (lyskom-apply-handler first-pending)
	  (kom-queue-resume queue))
        (if (or (eq (kom-queue->collect-flag queue) 'COLLECT)
                (and (eq (kom-queue->collect-flag queue) 'COLLECT-IGNORE)
                     (car (cdr (cdr first-pending)))))
            (lyskom-queue-enter (kom-queue->collect-queue queue)
                                (car (cdr (cdr first-pending))))))

       ((eq type 'COLLECT)
        (if (kom-queue->collect-flag queue)
            (signal 'lyskom-internal-error
                    '("lyskom-check-call COLLECT."))
          (set-kom-queue-collect-flag queue 'COLLECT)
          (lyskom-queue-make-empty (kom-queue->collect-queue queue))))

       ((eq type 'COLLECT-IGNORE)
        (if (kom-queue->collect-flag queue)
            (signal 'lyskom-internal-error
                    '("lyskom-check-call COLLECT-IGNORE."))
          (set-kom-queue-collect-flag queue 'COLLECT-IGNORE)
          (lyskom-queue-make-empty (kom-queue->collect-queue queue))))

       ((eq type 'USE)
        (if (not (kom-queue->collect-flag queue))
            (signal 'lyskom-internal-error
                    '("lyskom-check-call USE.")))
        (kom-queue-halt queue)
        (unwind-protect
            (lyskom-apply-multi-handler
             first-pending (lyskom-queue->all-entries
                            (kom-queue->collect-queue queue)))
          (kom-queue-resume queue))
        (set-kom-queue-collect-flag queue nil))

       ((eq type 'LIST-USE)
        (if (not (kom-queue->collect-flag queue))
            (signal 'lyskom-internal-error
                    '("lyskom-check-call LIST-USE.")))
        (kom-queue-halt queue)
        (unwind-protect
            (lyskom-apply-multi-list-handler
             first-pending (lyskom-queue->all-entries
                            (kom-queue->collect-queue queue)))
          (kom-queue-resume queue))
        (set-kom-queue-collect-flag queue nil))

       ((eq type 'RUN)
        (if (kom-queue->collect-flag queue)
            (signal 'lyskom-internal-error
                    '("lyskom-check-call RUN.")))
        (kom-queue-halt queue)
        (unwind-protect
            (lyskom-apply-function first-pending)
          (kom-queue-resume queue)))
       
       (t (signal 'lyskom-internal-error
                  (list 'lyskom-check-call
                        "unknown key:"
                        (car first-pending)))))

      (lyskom-queue-delete-first (kom-queue->pending queue))
      (setq first-pending (lyskom-queue->first (kom-queue->pending queue))))))

;;; Quit is NOT ok to press while the handler is running. inhibit-quit
;;; should be t when these are called.
(defun lyskom-apply-handler (pending)
  "Apply a handler. Args: PENDING.
PENDING is an entry of the list as described in documentation for the variable
lyskom-call-data. The car on the list must be a PARSED:
	('PARSED RESULT HANDLER HANDLER-DATA)"
  (if (car (cdr (cdr (cdr pending))))
      (apply (car (cdr (cdr (cdr pending))))	;Handler
	     (car (cdr (cdr pending)))	;Result
	     (car (cdr (cdr (cdr (cdr pending)))))))) ;Handler-data

(defun lyskom-apply-multi-handler (pending result-list)
  "Apply a handler for a lyskom-collect - lyskom-use construct."
  (apply (car (cdr pending))		;Multi-handler
	 (nconc result-list
		(car (cdr (cdr pending)))))) ;Multi-handler-data


(defun lyskom-apply-multi-list-handler (pending result-list)
  "Apply a handler for a lyskom-collect - lyskom-list-use construct."
  (apply (car (cdr pending))		;Multi-handler
	 (cons result-list
	       (car (cdr (cdr pending)))))) ;Multi-handler-data
    
(defun lyskom-apply-function (pending)
  (setcar pending 'HALTED)
  (apply (car (cdr pending))	    ;Function.
	 (car (cdr (cdr pending)))))  ;Function-args.
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; This file contains functions for sending requests to the server
;;;; and parsing the result.
;;;;


;;; ================================================================
;;; Macro for defining services

;;; (defmacro def-kom-service (name args &rest body)
;;;   "Create an initiate call. NAME and ARGS are the name and arguments for the call.
;;; If BODY starts with a string, that is the documentation string.
;;; If BODY consists of (call N PARSER), generate a simple call
;;; for RPC number N, and parse the result with PARSER."
;;;   (let ((function-name (intern (concat "initiate-" (symbol-name name))))
;;;         (doc-string nil)
;;;         (auto-call nil)
;;;         (buffer-save (intern (format "initiate-%s-saved-buffer" name))))
;;;     (when (stringp (car body))
;;;       (setq doc-string (car body))
;;;       (setq body (cdr body)))
;;; 
;;;     (when (and (listp (car body))
;;;                (eq 'call (car (car body))))
;;;       (setq auto-call (car body))
;;;       (setq body (cdr body)))
;;; 
;;;     (` (defun (, function-name) (kom-queue handler (,@ args) &rest data)
;;;          (, 
;;;           (or doc-string 
;;;               (format "Initiate %S on server\nArgs: KOM-QUEUE HANDLER %s &rest DATA"  
;;;                       name (mapconcat 
;;;                             (function
;;;                              (lambda (x)
;;;                                (format "%s" (upcase (symbol-name x)))))
;;;                             args
;;;                             " "))))
;;;          (let (((, buffer-save) (current-buffer)))
;;;            (unwind-protect
;;;                (progn
;;;                  (and (not lyskom-output-queues) (set-buffer lyskom-buffer))
;;;                  (,@ (if auto-call
;;;                          (` ((lyskom-call kom-queue lyskom-ref-no handler data (quote (, (elt auto-call 2))))
;;;                              (lyskom-send-packet kom-queue (lyskom-format-objects (, (elt auto-call 1)) (,@ args)))))
;;;                        body)))
;;;              (set-buffer (, buffer-save))))))))

(defmacro lyskom-server-call (&rest body)
  "Macro to protect initiate-somethings 
from being called in the wrong buffer."
  `(let ((initiate-something-saved-buffer (current-buffer)))
     (unwind-protect
	 (prog2 (or lyskom-output-queues (set-buffer lyskom-buffer))
	     lyskom-ref-no
	   ,@body)
       (set-buffer initiate-something-saved-buffer))))

(put 'lyskom-server-call 'lisp-indent-function 0)
(put 'lyskom-server-call 'edebug-form-spec t)
     

;;; ================================================================
;;;                     Requests for services


(defun initiate-login-old (kom-queue handler pers-no password &rest data)
  "Log in on server.
Args: KOM-QUEUE HANDLER PERS-NO PASSWORD &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 0 pers-no password))))


(defun initiate-login (kom-queue handler pers-no password status 
                                 &rest data)
  "Log in on server.
Args: KOM-QUEUE HANDLER PERS-NO PASSWORD STATUS &rest DATA.
Status is 0 for visible login and 1 for invisible login."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 62 pers-no
                                                         password status))))


(defun initiate-logout (kom-queue handler &rest data)
  "Log out from server.
Args: KOM-QUEUE HANDLER &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 1))))


(defun initiate-pepsi (kom-queue handler conf-no &rest data)
  "Change working conference.
Args: KOM-QUEUE HANDLER CONF-NO &rest DATA."
  (lyskom-server-call
    (let ((mship (lyskom-try-get-membership conf-no t)))
      (when mship
        (set-membership->last-time-read mship (lyskom-current-client-time))))
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 2 conf-no))))

(defun initiate-change-name (kom-queue handler
				       conf-no new-name
				       &rest data)
  "Change the name of a conference.
Args: KOM-QUEUE HANDLER CONF-NO NEW-NAME &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 3 conf-no new-name))))


(defun initiate-change-what-i-am-doing (kom-queue handler what &rest data)
  "Tell server what you are doing.
Args: KOM-QUEUE HANDLER WHAT &rest DATA.
WHAT is a string."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 4 what))))
  

(defun initiate-create-person (kom-queue handler name 
                                         password pers-flags
                                         aux-items 
                                             &rest data)
  "Create a new person.
Args: KOM-QUEUE HANDLER NAME PASSWORD &rest DATA."
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
   (if (lyskom-have-call 89)
       (lyskom-send-packet kom-queue (lyskom-format-objects 89
                                                            name
                                                            password
                                                            pers-flags
                                                            (cons 'LIST 
                                                                  aux-items)))
     (lyskom-send-packet kom-queue (lyskom-format-objects 5 name password)))))


;;; Call 6 is get-person-stat-old, and is obsoleted by call 49.

(defun initiate-set-priv-bits (kom-queue handler pers-no priv-bits &rest data)
  "Set priv-bits of a person.
Args: KOM-QUEUE HANDLER PERS-NO PRIV-BITS &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 7 pers-no priv-bits))))


(defun initiate-set-passwd (kom-queue handler
				      pers-no old-pw new-pw
				      &rest data)
  "Set the password of a person.
Args: KOM-QUEUE HANDLER PERS-NO OLD-PW NEW-PW &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 8 pers-no old-pw new-pw))))


;;; 
;;; This function has a ridiculous name!  It ought to be called
;;; get-membership.  Unfortunately this name is already taken
;;; by another call.
;;;
(defun initiate-query-read-texts (kom-queue handler
					    pers-no 
                                            conf-no
                                            want-read-ranges
                                            max-ranges
                                            &rest data)
  "Get a membership struct describing the membership of PERS-NO in CONF-NO.
Args: KOM-QUEUE HANDLER PERS-NO CONF-NO &rest DATA"
  (lyskom-server-call
    (cond ((lyskom-have-call 107)
           (lyskom-call kom-queue lyskom-ref-no handler data 
                        'lyskom-parse-membership-11)
           (lyskom-send-packet kom-queue
                               (lyskom-format-objects 107 pers-no conf-no
                                                      want-read-ranges
                                                      max-ranges)))
          ((lyskom-have-call 98)
           (lyskom-call kom-queue lyskom-ref-no handler data 
                        'lyskom-parse-membership-10)
           (lyskom-send-packet kom-queue
                               (lyskom-format-objects 98 pers-no conf-no)))
          (t
           (lyskom-call kom-queue lyskom-ref-no handler data 
                        'lyskom-parse-membership-old)
           (lyskom-send-packet kom-queue
                               (lyskom-format-objects 9 pers-no conf-no))))))


(defun initiate-create-conf (kom-queue handler
				       conf-name conf-type
                                       aux-items &rest data)
  "Add a member to a conference.
Args: KOM-QUEUE HANDLER CONF-NAME CONF-TYPE &rest DATA."
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
   (if (lyskom-have-call 88)
       (lyskom-send-packet kom-queue
                           (lyskom-format-objects 88 conf-name conf-type
                                                  (cons 'LIST aux-items)))
     (lyskom-send-packet kom-queue
                         (lyskom-format-objects 10 conf-name conf-type)))))


(defun initiate-delete-conf (kom-queue handler conf-no &rest data)
  "Delete a conference.
Args: KOM-QUEUE HANDLER CONF-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 11 conf-no))))



(defun initiate-lookup-name (kom-queue handler name &rest data)
  "See what conferences match NAME.
Args: KOM-QUEUE HANDLER NAME &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-conf-list)
    (lyskom-send-packet kom-queue (lyskom-format-objects 12 name))))
					    

;;; Call 13 is get-conf-stat-old, which is obsoleted by 50.


(defun initiate-add-member (kom-queue handler
				      conf-no 
                                      pers-no
                                      priority
                                      where
                                      type
				      &rest data)
  "Add a member to a conference.
Args: KOM-QUEUE HANDLER CONF-NO PERS-NO PRIORITY WHERE &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (if (lyskom-have-call 100)
        (lyskom-send-packet kom-queue
                            (lyskom-format-objects 100 conf-no pers-no 
                                                   priority where type))
      (lyskom-send-packet kom-queue
                          (lyskom-format-objects 14 conf-no pers-no 
                                                 priority where)))))


(defun initiate-sub-member (kom-queue handler
				      conf-no pers-no
				      &rest data)
  "Subtract a member from a conference.
Args: KOM-QUEUE HANDLER CONF-NO PERS-NO  &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 15 conf-no pers-no))))


(defun initiate-set-presentation (kom-queue handler
					    conf-no text-no
					    &rest data)
  "Set presentation of a conference.
Args: KOM-QUEUE HANDLER CONF-NO TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 16 conf-no text-no))))


(defun initiate-set-conf-motd (kom-queue handler conf-no text-no &rest data)
  "Set motd of a conference.
Args: KOM-QUEUE HANDLER CONF-NO TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 17 conf-no text-no))))


(defun initiate-set-supervisor (kom-queue handler conf-no admin
					  &rest data)
  "Set supervisor of a conference.
Args: KOM-QUEUE HANDLER CONF-NO ADMIN &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 18 conf-no admin))))


(defun initiate-set-permitted-submitters (kom-queue handler conf-no perm-sub
						    &rest data)
  "Set permitted submitters of a conference.
Args: KOM-QUEUE HANDLER CONF-NO PERM-SUB &rest DATA.
PERM-SUB is a conference number. All members in that conference might
write texts in CONF-NO. If PERM-SUB is zero everyone is allowed to
write texts in CONF-NO."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 19 conf-no perm-sub))))


(defun initiate-set-super-conf (kom-queue handler conf-no super-conf
					  &rest data)
  "Set superconference of a conference.
Args: KOM-QUEUE HANDLER CONF-NO SUPER-CONF &rest DATA.
Unauthorized attempts to write texts to CONF-NO will bounce to SUPER-CONF."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue 
                        (lyskom-format-objects 20 conf-no super-conf))))


(defun initiate-set-conf-type (kom-queue handler conf-no conf-type &rest data)
  "Set type of a conference.
Args: KOM-QUEUE HANDLER CONF-NO CONF-TYPE &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 21 conf-no conf-type))))


(defun initiate-set-garb-nice (kom-queue handler conf-no garb-nice &rest data)
  "Set garb-nice of a conference.
Args: KOM-QUEUE HANDLER CONF-NO GARB-NICE &rest DATA.
Texts in CONF-NO will live approximately GARB-NICE days."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 22 conf-no garb-nice))))


(defun initiate-get-marks (kom-queue handler &rest data)
  "Get all marked texts.
Args: KOM-QUEUE HANDLER &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-mark-list)
    (lyskom-send-packet kom-queue (lyskom-format-objects 23))))


(defun initiate-mark-text (kom-queue handler
                                     text-no mark-type
                                     &rest data)
  "Mark a text.
Args: KOM-QUEUE HANDLER TEXT-NO MARK-TYPE &rest DATA.
MARK-TYPE is currently a number, but this should maybe be
changed (internally in the elisp-klient) to something similar to
a conf-type (with several bits that are 't' or 'nil' that is)."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 
                         (if (lyskom-have-call 72) 72 24) 
                         text-no mark-type))))

(defun initiate-unmark-text (kom-queue handler
                                     text-no
                                     &rest data)
  "Unmark a text.
Args: KOM-QUEUE HANDLER TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (if (lyskom-have-call 73)
        (lyskom-send-packet kom-queue (lyskom-format-objects 73 text-no)))
      (lyskom-send-packet kom-queue (lyskom-format-objects 24 text-no 0))))


(defun initiate-get-text (kom-queue handler text-no &rest data)
  "Get text from LysKOM server.
Args: KOM-QUEUE HANDLER TEXT-NO &rest DATA."
  (lyskom-server-call
    (let ((text (cache-get-text text-no)))
      (cond
       ((null text)			;Cached info?
        (lyskom-call kom-queue		;No, ask the server.
                     lyskom-ref-no
                     handler data
                     'lyskom-parse-text text-no)
                                        ;(princ text-no (get-buffer-create "text"))+++
                                        ;(terpri (get-buffer-create "text"))
        (lyskom-send-packet kom-queue (lyskom-format-objects 25 text-no
                                                             0 lyskom-max-int)))
       (t
                                        ;Cached info. 
        (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) text handler data)
        (lyskom-check-call kom-queue))))))


(defun initiate-get-text-stat (kom-queue handler text-no &rest data)
  "Get text-stat from LysKOM server.
Args: KOM-QUEUE HANDLER TEXT-NO &rest DATA."
  (lyskom-server-call
   (let ((text-stat (cache-get-text-stat text-no)))
     (cond
      ((null text-stat)			;Cached info?
       (lyskom-call kom-queue		;No, ask the server.
                    lyskom-ref-no
                    handler data
                    (if (lyskom-have-call 90)
                        'lyskom-parse-text-stat
                      'lyskom-parse-text-stat-old)
                    text-no)
       ;;(princ text-no (get-buffer-create "text-stat"))+++
       ;;(terpri (get-buffer-create "text-stat"))
       (lyskom-send-packet kom-queue
                           (lyskom-format-objects
                            (if (lyskom-have-call 90) 90 26)
                            text-no)))
      (t
                                        ;Cached info. 
       (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) text-stat handler data)
       (lyskom-check-call kom-queue))))))


(defun initiate-mark-as-read (kom-queue handler conf-no text-list &rest data)
  "Mark all texts in TEXT-LIST as read in CONF-NO. Args: CONF-NO TEXT-LIST."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 27 conf-no 
                                               (cons 'LIST text-list)))))


(defun initiate-create-text (kom-queue 
                             handler
                             message
                             misc-list
                             aux-items
                             &rest data)
  "Create a new text.
Args: KOM-QUEUE HANDLER MESSAGE MISC-LIST AUX-ITEMS &rest DATA.
MESSAGE is a string. MISC-LIST should be created by lyskom-create-misc-list."
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
   (if (lyskom-have-call 86)
       (lyskom-send-packet kom-queue
                           (lyskom-format-objects 86
                                                  (cons 'STRING message)
                                                  misc-list
                                                  (cons 'LIST aux-items)))
     (lyskom-send-packet kom-queue
                         (lyskom-format-objects 28 
						(cons 'STRING message) 
						misc-list)))))

				     
(defun initiate-delete-text (kom-queue handler text-no &rest data)
  "Delete a text.
Args: KOM-QUEUE HANDLER TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 29 text-no))))


(defun initiate-add-recipient (kom-queue handler
					 text-no conf-no type
					 &rest data)
  "Add a recipient to a text.
Args: KOM-QUEUE HANDLER TEXT-NO CONF-NO TYPE &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet 
     kom-queue
     (lyskom-format-objects 30 text-no conf-no
                            (cond ((eq type 'RECPT) 0)
                                  ((eq type 'CC-RECPT) 1)
                                  ((eq type 'BCC-RECPT) 
                                   (if (lyskom-have-feature bcc-misc)
                                       15 1)))))))

(defun initiate-sub-recipient (kom-queue handler
					 text-no conf-no 
					 &rest data)
  "Subtract a recipient from a text.
Args: KOM-QUEUE HANDLER TEXT-NO CONF-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 31 text-no conf-no))))


(defun initiate-add-comment (kom-queue handler
                                       comment-text-no text-no
                                       &rest data)
  "Add a comment to a text.
Args: KOM-QUEUE HANDLER COMMENT-TEXT-NO TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 32 comment-text-no text-no))))

				       

(defun initiate-sub-comment (kom-queue handler
                                       comment-text-no text-no
                                       &rest data)
  "Subtract a comment from a text.
Args: KOM-QUEUE HANDLER COMMENT-TEXT-NO TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 33 comment-text-no text-no))))


(defun initiate-get-map (kom-queue handler conf-no first-local
				   no-of-texts &rest data-list)
  "Get mapping from local to global text-nos for CONF-NO from server.
Args: KOM-QUEUE HANDLER CONF-NO FIRST-LOCAL NO-OF-TEXTS DATA-LIST.
Use z-initiate-get-map instead. This function has severe performance losses
with big maps."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data-list 'lyskom-parse-map)
    (lyskom-send-packet kom-queue 
                        (lyskom-format-objects 34 conf-no
                                               first-local no-of-texts))))

(defun z-initiate-get-map  (kom-queue handler conf-no first-local
                                      no-of-texts &rest data)
  "Get mapping from local to global text-nos for CONF-NO from server.
Args: KOM-QUEUE HANDLER CONF-NO FIRST-LOCAL NO-OF-TEXTS &rest DATA.
This function will automatically split fetching of big maps to small
chunks of lyskom-fetch-map-nos texts/chunk if KOM-QUEUE is not already
used to collect a result. This currently gives a big performance gain.
Unfortunately it is impossible (or at least very hard) to do the same
thing when a collect is in progress. This will of course be fixed in
protocol B."
  (lyskom-server-call
    (cond
     ((kom-queue->collect-flag (cdr-safe (assq kom-queue lyskom-call-data)))
      ;; Use oldstyle single big map. Sorry.
      (apply 'initiate-get-map
             kom-queue handler conf-no first-local no-of-texts data))
     (t
      ;; You win.
      (initiate-get-map kom-queue 'lyskom-receive-partial-map conf-no
                        first-local lyskom-fetch-map-nos
                        (+ lyskom-fetch-map-nos first-local)
                        (- no-of-texts lyskom-fetch-map-nos)
                        conf-no nil kom-queue data handler)))))

(defun lyskom-receive-partial-map (map first-local no-of-texts
				       conf-no map-so-far kom-queue
				       data-list handler)
  "Receive a partial map and start fetching a new chunk."
  (lyskom-server-call
    (let ((map-list (nconc map-so-far (list map))))
      (if (<= no-of-texts 0)
          (apply handler (apply 'lyskom-map-concat map-list) data-list)
        (initiate-get-map kom-queue 'lyskom-receive-partial-map conf-no
                          first-local lyskom-fetch-map-nos
                          (+ lyskom-fetch-map-nos first-local)
                          (- no-of-texts lyskom-fetch-map-nos)
                          conf-no map-list kom-queue data-list handler)))))


(defun initiate-get-time (kom-queue handler &rest data)
  "Get time from server.
Args: KOM-QUEUE HANDLER &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-time)
    (lyskom-send-packet kom-queue (lyskom-format-objects 35))))


(defun initiate-get-server-info (kom-queue handler &rest data)
  "Get info about the server"
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no
                handler
                data
                (if (lyskom-have-call 94)
                    'lyskom-parse-server-info
                  'lyskom-parse-server-info-old))
   (lyskom-send-packet kom-queue
                       (lyskom-format-objects
                        (if (lyskom-have-call 94) 94 36)))))


(defun initiate-add-footnote (kom-queue handler
					footnote-text-no text-no
					&rest data)
  "Add a footnote to a text.
Args: KOM-QUEUE HANDLER FOOTNOTE-TEXT-NO TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 37 footnote-text-no text-no))))


(defun initiate-sub-footnote (kom-queue handler
					footnote-text-no text-no
					&rest data)
  "Subtract a footnote from a text.
Args: KOM-QUEUE HANDLER FOOTNOTE-TEXT-NO TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 38 footnote-text-no text-no))))


;;; Call 39, who-is-on-old, is obsoleted by call 63.


(defun initiate-set-unread (kom-queue handler conf-no no-of-unread &rest data)
  "Set number of unread texts in a certain conference.
Args: KOM-QUEUE HANDLER CONF-NO NO-OF-UNREAD &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 40 conf-no no-of-unread))))


(defun initiate-set-motd-of-lyskom (kom-queue handler text-no &rest data)
  "Set message of the day of LysKOM.
Args: KOM-QUEUE HANDLER TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 41 text-no))))


(defun initiate-enable (kom-queue handler level &rest data)
  "Set security level.
Args: KOM-QUEUE HANDLER LEVEL &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 42 level))))

;;; Call 43 is sync. Starting with version 1.9 of lyskomd it is a
;;; privileged operation, so there is no harm in having the function
;;; easily available any more.

(defun initiate-sync (kom-queue handler &rest data)
  "Sync the LysKOM datbase. This is a prioritized call."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 43))))

;;; Call 44 is shutdown. Use 'kill -HUP' instead.

(defun initiate-shutdown (kom-queue handler parameter &rest data)
  "Shutdown the server.
Args: KOM-QUEUE HANDLER PARAMETER &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 44 parameter))))


(defun initiate-broadcast (kom-queue handler message &rest data)
  "Send a broadcast message to all logged in users.
Args: KOM-QUEUE HANDLER MESSAGE &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 45 message))))



(defun initiate-get-membership (kom-queue handler pers-no &rest data)
  "Get membership-list for PERS-NO from server.
Args: KOM-QUEUE HANDLER PERS-NO &rest DATA."
  (lyskom-server-call
    (cond ((lyskom-have-call 108)
           (lyskom-call kom-queue lyskom-ref-no handler data
                        'lyskom-parse-membership-list-11)
           (lyskom-send-packet 
            kom-queue 
            (lyskom-format-objects 108 pers-no 0 lyskom-max-int
                                   1 lyskom-max-int)))
          ((lyskom-have-call 99)
           (lyskom-call kom-queue lyskom-ref-no handler data
                        'lyskom-parse-membership-list-10)
           (lyskom-send-packet 
            kom-queue 
            (lyskom-format-objects 99 pers-no 0 lyskom-max-int 1)))
          (t (lyskom-call kom-queue lyskom-ref-no handler data
                          'lyskom-parse-membership-list-old)
             (lyskom-send-packet 
              kom-queue 
              (lyskom-format-objects 46 pers-no 0 lyskom-max-int 1))))))


(defun initiate-get-part-of-membership (kom-queue handler pers-no first length
						  &rest data)
  "Get membership-list for PERS-NO from server.
Args: KOM-QUEUE HANDLER PERS-NO FIRST-IN-LIST LENGHT &rest DATA."
  (lyskom-server-call
    (cond ((lyskom-have-call 108)
           (lyskom-call kom-queue lyskom-ref-no handler data
                        'lyskom-parse-membership-list-11)
           (lyskom-send-packet 
            kom-queue 
            (lyskom-format-objects 108 pers-no first length
                                   1 1)))
          ((lyskom-have-call 99)
           (lyskom-call kom-queue lyskom-ref-no handler data
                        'lyskom-parse-membership-list-10)
           (lyskom-send-packet 
            kom-queue 
            (lyskom-format-objects 99 pers-no first length 0)))
          (t (lyskom-call kom-queue lyskom-ref-no handler data
                          'lyskom-parse-membership-list-old)
             (lyskom-send-packet 
              kom-queue 
              (lyskom-format-objects 46 pers-no first length 0))))))


(defun initiate-get-created-texts (kom-queue handler pers-no first-local
					     no-of-texts &rest data)
  "Get a part of the list of created texts for a person.
Args: KOM-QUEUE HANDLER PERS-NO FIRST-LOCAL NO-OF-TEXTS &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-map)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 47 pers-no
                                               first-local no-of-texts))))


(defun initiate-get-members (kom-queue handler conf-no first-local
				       no-of-members &rest data)
  "Get a part of the list of members in a conference.
Args: KOM-QUEUE HANDLER CONF-NO FIRST-LOCAL NO-OF-MEMBERS &rest DATA.
Returns a conf-no-list."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 (if (lyskom-have-call 101)
                     'lyskom-parse-member-list
                   'lyskom-parse-member-list-old))
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 
                         (if (lyskom-have-call 101) 101 48)
                         conf-no
                         first-local no-of-members))))


(defun initiate-get-pers-stat (kom-queue handler pers-no &rest data)
  "Get status for person PERS-NO.
Args: KOM-QUEUE HANDLER PERS-NO &rest DATA."
  (lyskom-server-call
    (let ((pers-stat (cache-get-pers-stat pers-no)))
      (cond
       ((null pers-stat)                ;Cached info?
        (lyskom-call kom-queue		;No, ask the server.
                     lyskom-ref-no
                     handler data
                     'lyskom-parse-pers-stat pers-no)
                                        ;(princ pers-no (get-buffer-create "pers-stat")) +++
                                        ;(terpri (get-buffer-create "pers-stat"))
        (lyskom-send-packet kom-queue (lyskom-format-objects 49 pers-no)))
       (t
                                        ;Cached info. 
        (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) pers-stat handler data)
        (lyskom-check-call kom-queue))))))

(defun initiate-get-conf-stat (kom-queue handler conf-no &rest data)
  "Get conf-stat from LysKOM server.
Args: KOM-QUEUE HANDLER CONF-NO &rest DATA."
  (lyskom-server-call
   (let ((conf-stat (cache-get-conf-stat conf-no)))
     (cond
      ((zerop conf-no)			;No real user.
       (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) nil handler data)
       (lyskom-check-call kom-queue))
      ((null conf-stat)			;Cached info?
       (lyskom-call kom-queue		;No, ask the server.
                    lyskom-ref-no
                    handler data
                    (if (lyskom-have-call 91)
                        'lyskom-parse-conf-stat
                      'lyskom-parse-conf-stat-old)
                    conf-no)
       ;;(princ conf-no (get-buffer-create "conf-stat")) +++
       ;;(terpri (get-buffer-create "conf-stat"))
       (lyskom-send-packet kom-queue 
                           (lyskom-format-objects
                            (if (lyskom-have-call 91) 91 50)
                            conf-no)))
      (t
                                        ;Cached info. 
       (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) conf-stat handler data)
       (lyskom-check-call kom-queue))))))


;; who-is-on is obsoleted by who-is-on-dynamic (83) i protocol version 9
(defun initiate-who-is-on (kom-queue handler &rest data)
  "Ask server who is on.
Args: KOM-QUEUE HANDLER &rest DATA"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-who-info-list)
    (lyskom-send-packet kom-queue (lyskom-format-objects 51))))


(defun initiate-get-unread-confs (kom-queue handler pers-no &rest data)
  "Return a list of confs that may have unread texts.
Args: KOM-QUEUE HANDLER PERS-NO &rest DATA.
PERS-NO is the number of the person whos confs we are checking."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-conf-no-list)
    (lyskom-send-packet kom-queue (lyskom-format-objects 52 pers-no))))


(defun initiate-send-message (kom-queue handler recipient message &rest data)
  "Send a message to one or all logged in users.
Args: KOM-QUEUE HANDLER RECIPIENT MESSAGE &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 53 recipient message))))


(defun initiate-get-session-info (kom-queue handler session-no &rest data)
  "Ask server for info about a session.
Args: KOM-QUEUE HANDLER SESSION-NO &rest DATA"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-session-info)
    (lyskom-send-packet kom-queue (lyskom-format-objects 54 session-no))))


(defun initiate-disconnect (kom-queue handler session-no &rest data)
  "Disconnect a session.
Args: KOM-QUEUE HANDLER SESSION &rest DATA"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 55 session-no))))


(defun initiate-who-am-i (kom-queue handler &rest data)
  "Ask the server which connection we are using.
Args: KOM-QUEUE HANDLER &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-num)
    (lyskom-send-packet kom-queue (lyskom-format-objects 56))))

(defun initiate-set-user-area (kom-queue handler pers-no text-no &rest data)
  "Set user-area of a person.
Args: KOM-QUEUE HANDLER PERS-NO TEXT-NO &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 57 pers-no text-no))))


;; WARNING: If you start using this you have to figure out a way to
;; convert local time (before) to UTC. Since this doesn't work at the
;; moment, I have commented this functoun into oblivion.
;;
;; (defun initiate-get-last-text (kom-queue handler before &rest data)
;;   "Get text created before BEFORE.
;; Args: KOM-QUEUE HANDLER BEFORE &rest DATA"
;;   (lyskom-server-call
;;     (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
;;     (lyskom-send-packet kom-queue (lyskom-format-objects 58 before))))


(defun initiate-create-anonymous-text (kom-queue 
                                       handler
                                       message 
                                       misc-list
                                       aux-items
                                       &rest data)
  "Create a new anonymous text.
Args: KOM-QUEUE HANDLER MESSAGE MISC-LIST AUX-ITEMS &rest DATA.
MESSAGE is a string. MISC-LIST should be created by lyskom-create-misc-list."
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
   (if (lyskom-have-call 87)
       (lyskom-send-packet kom-queue
                           (lyskom-format-objects 87
                                                  (cons 'STRING message)
                                                  misc-list
                                                  (cons 'LIST aux-items)))
     (lyskom-send-packet kom-queue
                         (lyskom-format-objects 59 
                                                (cons 'STRING message)
                                                misc-list)))))


(defun initiate-find-next-text-no (kom-queue handler text-no &rest data)
  "Find the text following the text TEXT-NO"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
    (lyskom-send-packet kom-queue (lyskom-format-objects 60 text-no))))

(defun initiate-find-previous-text-no (kom-queue handler text-no &rest data)
  "Find the text preceding the text TEXT-NO"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
    (lyskom-send-packet kom-queue (lyskom-format-objects 61 text-no))))

;; Call 62 is above

(defun initiate-who-is-on-ident (kom-queue handler &rest data)
  "Who is logged on. Obsolete."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 
                 'lyskom-parse-who-info-ident-list)
    (lyskom-send-packet kom-queue (lyskom-format-objects 63))))

(defun initiate-get-session-info-ident (kom-queue handler 
                                                  session-no &rest data)
  "Get session info. Obsolete."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-session-info-ident)
    (lyskom-send-packet kom-queue (lyskom-format-objects 64 session-no))))

(defun initiate-re-lookup-person (kom-queue handler regexp &rest data)
  "Look up person based on regexp. Obsolete."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 
                 'lyskom-parse-number-array)
    (lyskom-send-packet kom-queue (lyskom-format-objects 65 regexp))))

(defun initiate-re-lookup-conf (kom-queue handler regexp &rest data)
  "Look up conference based on regexp. Obsolete."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 
                 'lyskom-parse-number-array)
    (lyskom-send-packet kom-queue (lyskom-format-objects 66 regexp))))

(defun initiate-lookup-person (kom-queue handler regexp &rest data)
  "Look up person based on abbreviated name. Obsolete."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 
                 'lyskom-parse-number-array)
    (lyskom-send-packet kom-queue (lyskom-format-objects 67 regexp))))

(defun initiate-lookup-conf (kom-queue handler regexp &rest data)
  "Look up conference based on abbreviated name. Obsolete."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 
                 'lyskom-parse-number-array)
    (lyskom-send-packet kom-queue (lyskom-format-objects 68 regexp))))

(defun initiate-set-client-version (kom-queue handler name version &rest data)
  "Tell the server to set the client name and version of this session."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 69 name version))))


(defun initiate-get-client-name (kom-queue handler session &rest data)
  "Tell the server to set the highest unread article in conference CONF-NO
to TEXT-NO
Args: KOM-QUEUE HANDLER CONF-NO TEXT-NO &rest DATA"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-string)
    (lyskom-send-packet kom-queue (lyskom-format-objects 70 session))))


(defun initiate-get-client-version (kom-queue handler session &rest data)
  "Tell the server to set the highest unread article in conference CONF-NO
to TEXT-NO
Args: KOM-QUEUE HANDLER CONF-NO TEXT-NO &rest DATA"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-string)
    (lyskom-send-packet kom-queue (lyskom-format-objects 71 session))))

(defun initiate-re-z-lookup (kom-queue handler regexp want-persons want-confs
				       &rest data)
  "Perform a regexp lookup.
Args: KOM-QUEUE HANDLER REGEXP WANT-PERSONS WANT-CONFS &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data
                 'lyskom-parse-conf-z-info-list)
    (lyskom-send-packet kom-queue (lyskom-format-objects 74 regexp want-persons
                                                         want-confs))))

(defun initiate-get-version-info (kom-queue handler &rest data)
  "Perform a get-version-info vall.
Args: KOM-QUEUE HANDLER &rest DATA"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-version-info)
    (lyskom-send-packet kom-queue (lyskom-format-objects 75))))


(defun initiate-lookup-z-name (kom-queue handler name want-persons want-confs
                                         &rest data)
  "Perform a z-lookup.
Args: KOM-QUEUE HANDLER NAME WANT-PERSONS WANT-CONFS &rest DATA"
  (lyskom-server-call
    (if (lyskom-have-call 76)
        (progn
          (lyskom-call kom-queue lyskom-ref-no handler data 
                       'lyskom-parse-conf-z-info-list)
          (lyskom-send-packet kom-queue (lyskom-format-objects 76 name
                                                               want-persons
                                                               want-confs)))
      (let ((ref-no lyskom-ref-no))
        (lyskom-fake-call kom-queue ref-no handler data)
        (++ lyskom-ref-no)
        (initiate-lookup-name 'compat
                              'initiate-compat-lookup-z-name-2
                              name
                              kom-queue
                              ref-no
                              want-persons
                              want-confs)))))

(defun initiate-compat-lookup-z-name-2 (result kom-queue
                                               ref-no
                                               want-persons
                                               want-confs)
  (lyskom-server-call
    (if (null result)
        (lyskom-complete-call kom-queue ref-no nil))

    (let ((conf-nos (listify-vector (conf-list->conf-nos result)))
          (conf-types (listify-vector (conf-list->conf-types result))))
      (lyskom-collect 'follow)
      (while conf-nos
        (if (or (and want-persons
                     (conf-type->letterbox (car conf-types)))
                (and want-confs
                     (not (conf-type->letterbox (car conf-types)))))
            (initiate-get-conf-stat 'follow nil (car conf-nos)))
        (setq conf-nos (cdr conf-nos))
        (setq conf-types (cdr conf-types)))
      (lyskom-list-use 'follow
                       'initiate-compat-lookup-z-name-3
                       kom-queue
                       ref-no))))



(defun initiate-compat-lookup-z-name-3 (conf-list kom-queue 
                                                  ref-no)
  (lyskom-server-call
    (lyskom-complete-call kom-queue ref-no
                          (lyskom-create-conf-z-info-list
                           (mapcar (function
                                    (lambda (conf-stat)
                                      (lyskom-create-conf-z-info
                                       (conf-stat->name conf-stat)
                                       (conf-stat->conf-type conf-stat)
                                       (conf-stat->conf-no conf-stat))))
                                   conf-list)))))
    

(defun initiate-set-last-read (kom-queue handler conf-no text-no &rest data)
  "Tell the server to set the highest unread article in conference CONF-NO
to TEXT-NO
Args: KOM-QUEUE HANDLER CONF-NO TEXT-NO &rest DATA"
  (lyskom-server-call
    (if (lyskom-have-call 77)
        (progn
          (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
          (lyskom-send-packet kom-queue (lyskom-format-objects 77
                                                               conf-no text-no)))
      (initiate-get-conf-stat kom-queue 
                              'initiate-set-last-read-2 
                              conf-no
                              kom-queue
                              handler 
                              conf-no
                              text-no
                              data))))

(defun initiate-set-last-read-2 (conf-stat 
                                 kom-queue
                                 handler
                                 conf-no
                                 text-no
                                 data)
  (lyskom-server-call
    (let ((no-of-unread (- (1- (+ (conf-stat->first-local-no conf-stat)
                                  (conf-stat->no-of-texts conf-stat)))
                           text-no)))
      (if (< no-of-unread 0)
          (setq no-of-unread 0))

      (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
      (lyskom-send-packet kom-queue
                          (lyskom-format-objects 40 conf-no no-of-unread)))))

(defun initiate-get-uconf-stat (kom-queue handler conf-no &rest data)
  "Get an uconf-stat from LysKOM server.
Args: KOM-QUEUE HANDLER CONF-NO &rest DATA."
  (lyskom-server-call
    (let ((conf-stat (cache-get-uconf-stat conf-no)))
      (cond ((zerop conf-no)
             (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) nil handler data)
             (lyskom-check-call kom-queue))
            ((null conf-stat)
             (lyskom-call kom-queue
                          lyskom-ref-no
                          handler data
                          'lyskom-parse-uconf-stat conf-no)
             (lyskom-send-packet kom-queue (lyskom-format-objects 78 conf-no)))
            (t
             (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) conf-stat handler data)
             (lyskom-check-call kom-queue))))))

(defun initiate-set-info (kom-queue handler 
                                    conf-pres-conf
                                    pers-pres-conf
                                    motd-conf
                                    kom-news-conf
                                    motd-of-lyskom &rest data)
  "Set server info."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 79
                                                         0
                                                         conf-pres-conf
                                                         pers-pres-conf
                                                         motd-conf
                                                         kom-news-conf
                                                         motd-of-lyskom))))
                                                         

(defun initiate-accept-async (kom-queue handler list &rest data)
  "Request asynchronous messages in LIST
Args: KOM-QUEUE HANDLER LIST &rest DATA"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 80
                                                         (cons 'LIST
                                                               list)))))

(defun initiate-query-async (kom-queue handler &rest data)
  "Request information on which async messages are being sent.
Args: KOM-QUEUE HANDLER &rest DATA"
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 
                 'lyskom-parse-number-array)
    (lyskom-send-packet kom-queue (lyskom-format-objects 81))))
                                 


(defun initiate-user-active (kom-queue handler &rest data)
  "Notify the server that the user is active
Args: KOM-QUEUE HANDLER &rest DATA."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 82))))


(defun initiate-who-is-on-dynamic (kom-queue handler want-visible
					     want-invisible active-last
					     &rest data)
  "Ask server who is on.
Args: KOM-QUEUE HANDLER WANT-VISIBLE WANT-INVISIBLE ACTIVE_LAST &rest DATA"
  (lyskom-server-call
  (lyskom-call kom-queue lyskom-ref-no handler data
	       'lyskom-parse-dynamic-session-info-list)
  (lyskom-send-packet kom-queue (lyskom-format-objects
				 83 want-visible want-invisible active-last))))


(defun initiate-get-static-session-info (kom-queue handler session-no
						   &rest data)
  "Ask server for info about a session.
Args: KOM-QUEUE HANDLER SESSION-NO &rest DATA"
  (lyskom-server-call
   (let ((info (cache-get-static-session-info session-no)))
     (cond
      ((null info)			; Not cached
       (lyskom-call kom-queue lyskom-ref-no handler data
                    'lyskom-parse-static-session-info session-no)
       (lyskom-send-packet kom-queue (lyskom-format-objects
                                      84 session-no)))
      (t                                ; Cached
       (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) info handler data)
       (lyskom-check-call kom-queue)))))) ;This might call the handler.


(defun initiate-get-collate-table (kom-queue handler &rest data)
  "Get the collate table from the server."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-string)
    (lyskom-send-packet kom-queue (lyskom-format-objects 85))))

(defun initiate-modify-text-info (kom-queue handler 
                                            text-no
                                            delete-items
                                            add-items
                                            &rest data)
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
   (lyskom-send-packet kom-queue
                       (lyskom-format-objects 92
                                              text-no
                                              (cons 'LIST delete-items)
                                              (cons 'LIST add-items)))))

(defun initiate-modify-conf-info (kom-queue handler
                                            conf-no
                                            delete-items
                                            add-items
                                            &rest data)
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
   (lyskom-send-packet kom-queue 
                       (lyskom-format-objects 93
                                              conf-no
                                              (cons 'LIST delete-items)
                                              (cons 'LIST add-items)))))

(defun initiate-modify-server-info (kom-queue handler 
                                              delete-items
                                              add-items
                                              &rest data)
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
   (lyskom-send-packet kom-queue
                       (lyskom-format-objects 95
                                              (cons 'LIST delete-items)
                                              (cons 'LIST add-items)))))

(defun initiate-query-predefined-aux-items (kom-queue handler &rest data)
  "Send query-predefined-aux-items to the server"
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data
                'lyskom-parse-number-array)
   (lyskom-send-packet kom-queue (lyskom-format-objects 96))))

(defun initiate-set-expire (kom-queue handler conf-no expire &rest data)
  "Send set-expire to server."
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
   (lyskom-send-packet kom-queue (lyskom-format-objects 97 conf-no expire))
   (cache-del-conf-stat conf-no)))

(defun initiate-set-membership-type (kom-queue handler pers-no conf-no type &rest data)
  "Send set-membership-type to the server."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue 
                        (lyskom-format-objects 102 pers-no conf-no type))))

(defun initiate-local-to-global (kom-queue handler 
                                           conf-no
                                           first-local-no
                                           no-of-texts &rest data)
  "Send local-to-global to server."
  (lyskom-server-call
    (cond ((lyskom-have-call 103)
           (lyskom-call kom-queue lyskom-ref-no handler data
                        'lyskom-parse-text-mapping no-of-texts)
           (lyskom-send-packet kom-queue
                               (lyskom-format-objects 103 
                                                      conf-no
                                                      first-local-no
                                                      no-of-texts)))
          (t (lyskom-call kom-queue lyskom-ref-no 
                          'lyskom-l2g-fake-callback
                          (cons handler data)
                          'lyskom-parse-map)
                 (lyskom-send-packet kom-queue 
                                     (lyskom-format-objects 34 conf-no
                                                            first-local-no
                                                            no-of-texts))))))

(defun lyskom-l2g-fake-callback (map handler &rest data)
  (let ((mapping 
         (and map (lyskom-create-text-mapping (map->first-local map)
                                              (+ (map->first-local map)
                                                 (length (map->text-nos map)))
                                              (length (map->text-nos map))
                                              t
                                              'dense
                                              map))))
    (apply handler mapping data)))

(defun initiate-map-created-texts (kom-queue handler
                                             author
                                             first-local-no
                                             no-of-texts
                                             &rest data)
  "Send map-created-texts to the server."
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 
                 'lyskom-parse-text-mappinng no-of-texts)
    (lyskom-send-packet kom-queue
                        (lyskom-format-objects 104
                                               author
                                               first-local-no
                                               no-of-texts))))



(defun initiate-set-keep-commented (kom-queue handler conf-no keep &rest data)
  (lyskom-server-call
   (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
   (lyskom-send-packet kom-queue (lyskom-format-objects 105 conf-no keep))
   (cache-del-conf-stat conf-no)))

(defun initiate-set-pers-flags (kom-queue handler pers-no flags &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 106 pers-no flags))
    (cache-del-pers-stat pers-no)))

(defun initiate-mark-as-unread (kom-queue handler conf-no text &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 109 conf-no text))))

(defun initiate-get-stats-description (kom-queue handler &rest data)
  (lyskom-server-call
    (if lyskom-stats-description
        (progn (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) lyskom-stats-description
                                handler data)
          (lyskom-check-call kom-queue))
      (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-stats-description)
      (lyskom-send-packet kom-queue (lyskom-format-objects 111)))))

(defun initiate-get-stats (kom-queue handler what &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-stats-array)
    (lyskom-send-packet kom-queue (lyskom-format-objects 112 what))))

(defun initiate-get-boottime-info (kom-queue handler &rest data)
  (lyskom-server-call
    (if lyskom-static-server-info
        (progn (lyskom-call-add kom-queue 'PARSED (lyskom-ref-no) lyskom-static-server-info
                                handler data)
          (lyskom-check-call kom-queue))
      (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-static-server-info)
      (lyskom-send-packet kom-queue (lyskom-format-objects 113)))))

(defun initiate-first-unused-conf-no (kom-queue handler &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
    (lyskom-send-packet kom-queue (lyskom-format-objects 114))))

(defun initiate-first-unused-text-no (kom-queue handler &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
    (lyskom-send-packet kom-queue (lyskom-format-objects 115))))

(defun initiate-find-next-conf-no (kom-queue handler conf-no &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
    (lyskom-send-packet kom-queue (lyskom-format-objects 116 conf-no))))

(defun initiate-find-previous-conf-no (kom-queue handler conf-no &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-num)
    (lyskom-send-packet kom-queue (lyskom-format-objects 117 conf-no))))

(defun initiate-get-scheduling (kom-queue handler session-no &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-scheduling-info)
    (lyskom-send-packet kom-queue (lyskom-format-objects 118 (or session-no 0)))))

(defun initiate-set-scheduling (kom-queue handler session-no priority weight &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 119 (or session-no 0) priority weight))))

(defun initiate-set-connection-time-format (kom-queue handler use-utc &rest data)
  (lyskom-server-call
    (lyskom-call kom-queue lyskom-ref-no handler data 'lyskom-parse-void)
    (lyskom-send-packet kom-queue (lyskom-format-objects 120 use-utc))))


;;; ================================================================


;; Blocking reading from server:

(defvar lyskom-blocking-return nil
  "Return from blocking-do.")

(defun blocking-return (retval)
  "Sets blocking variable."
  (setq lyskom-blocking-return retval
	lyskom-ok-to-send-new-calls nil))

(defun blocking-do (command &rest data)
  "Does the COMMAND agains the lyskom-server and returns the result.
COMMAND is one lyskom-command \(like the initiate-* but the initiate- is
stripped. DATA is the args to command.
The cache is consulted when command is get-conf-stat, get-pers-stat
or get-text-stat."
  ;; Here we could check if lyskom-blocking-return is non-nil, in
  ;; which case there is a bug in the code

  (save-current-buffer
    (set-buffer (or lyskom-buffer (process-buffer lyskom-proc)))
    ;; If this happens, we're in trouble
    (if lyskom-is-parsing (lyskom-really-serious-bug))

    (let ((lyskom-blocking-return 'not-yet-gotten))
      ;; This should not be necessary, but for robustness sake...
      ;; There are occasions when it is needed.
      (setq lyskom-ok-to-send-new-calls t)

      (apply (intern-soft (concat "initiate-"
				  (symbol-name command)))
	     'blocking 'blocking-return
	     data)
      (unwind-protect
	  (while (and (eq lyskom-blocking-return 'not-yet-gotten)
		      (memq (process-status lyskom-proc) '(open run))
		      ;; The following test should probably be removed
		      (not lyskom-quit-flag))
	    (lyskom-accept-process-output))
	
	;; OK to continue with prefetch and stuff again
	(setq lyskom-ok-to-send-new-calls t)
	(lyskom-check-output-queues))
      
      (if (or lyskom-quit-flag quit-flag)
          (signal 'quit nil))
      (setq lyskom-quit-flag nil)
      lyskom-blocking-return)))



(defun lyskom-wait-queue (queue)
  "Waits until all data on QUEUE has been processed"
  (save-current-buffer
    (set-buffer (or lyskom-buffer
                    (process-buffer lyskom-proc)))
    (let ((collector (make-collector)))
      (lyskom-run queue (lambda (c) (set-collector->value c t)) collector)
      (unwind-protect
	  (while (and (null (collector->value collector))
		      (not lyskom-quit-flag))
	    (lyskom-accept-process-output))
	(setq lyskom-ok-to-send-new-calls t)
	(lyskom-check-output-queues))
      (if (or lyskom-quit-flag quit-flag)
	  (progn
	    (lyskom-insert-before-prompt (lyskom-get-string 'interrupted))
	    (signal 'quit nil)))
      (setq lyskom-quit-flag nil)
      (collector->value collector))))


(defvar lyskom-multiple-blocking-return nil
  "Return from blocking-do-multiple")

(defun lyskom-blocking-do-multiple (call-list)
  (save-current-buffer
    (set-buffer (or lyskom-buffer (process-buffer lyskom-proc)))
    ;; If this happens, we're in trouble
    (if lyskom-is-parsing (lyskom-really-serious-bug))
    
    (let ((lyskom-multiple-blocking-return 'not-yet-gotten))
      (setq lyskom-ok-to-send-new-calls t)
      (lyskom-collect 'blocking)
      (while call-list
	(apply (intern-soft (concat "initiate-"
				    (symbol-name (car (car call-list)))))
	       'blocking nil
	       (cdr (car call-list)))
	(setq call-list (cdr call-list)))
      (lyskom-use 'blocking 'lyskom-blocking-do-multiple-1)
      (unwind-protect
	  (while (and (eq lyskom-multiple-blocking-return 'not-yet-gotten)
		      (memq (process-status lyskom-proc) '(open run))
		      (not lyskom-quit-flag))
	    (lyskom-accept-process-output))
	;; OK to continue with prefetch and stuff again
	(setq lyskom-ok-to-send-new-calls t)
	(lyskom-check-output-queues))
      (if lyskom-quit-flag
	  (progn
	    (setq lyskom-quit-flag nil)
	    (lyskom-insert-before-prompt (lyskom-get-string 'interrupted))
	    (signal 'quit nil)))
      lyskom-multiple-blocking-return)))

(defun lyskom-blocking-do-multiple-1 (&rest data)
  (setq lyskom-multiple-blocking-return data
	lyskom-ok-to-send-new-calls nil))

(defun lyskom-cancel-call (queue-name ref-nos)
  "Attempt to cancel calls in queue QUEUE-NAME with ref-no in REF-NOS. 
There is no guarantee that the call will be canceled. In particular, if
the call is not on QUEUE-NAME or has been sent to the server, it will
probably not be canceled."
  (let ((found nil))

    ;; Delete the call from the pending queue

    (let* ((queue (cdr (assq queue-name lyskom-call-data)))
           (calls (and queue (lyskom-queue->all-entries (kom-queue->pending queue)))))
      (when queue
        (lyskom-queue-make-empty (kom-queue->pending queue))
        (lyskom-traverse el calls
          (if (memq (elt el 1) ref-nos)
              (setq found t)
            (lyskom-queue-enter (kom-queue->pending queue) el)))))
    
    ;; Delete the call from the output queue

    (let* ((queue (aref lyskom-output-queues (lyskom-queue-priority queue-name))))
      (when (and queue found)
        (let ((calls (lyskom-queue->all-entries queue)))
          (lyskom-queue-make-empty queue)
          (lyskom-traverse el calls
            (unless (memq (car el) ref-nos)
              (lyskom-queue-enter queue el))))))))

(put 'blocking-do-multiple 'edebug-form-spec '(sexp body))



(eval-and-compile (provide 'lyskom-services))

;;; services.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: parse.el
;;;;
;;;; This file contains functions which parse replies from the
;;;; server. 
;;;;


;;; ================================================================
;;;        Errors that are handled of use while parsing


(put 'lyskom-parse-incomplete 'error-conditions
     '(error lyskom-error lyskom-parse-incomplete))
(put 'lyskom-parse-incomplete 'error-message
     "LysKOM internal error: Parse incomplete.")


;;; ================================================================
;;;                  Low-level parsing.


(defun lyskom-string-skip-whitespace (string)
  "Return STRING omitting any leading whitespace."
  (let ((start (string-match "[^ \t\n\r]" string)))
    (cond ((null start) "")
	  (t (substring string start)))))

(defun lyskom-parse-skip-rest-of-token ()
  "Skip to the next whitespace"
  (let ((c (lyskom-parse-char)))
    (while (not (or (= c ?\ ) (= c ?\n)))
      (setq c (lyskom-parse-char)))))

(defun lyskom-parse-nonwhite-char ()
  "Get next character, skipping whitespace, from lyskom-unparsed-buffer and
increase lyskom-parse-pos. Signal lyskom-parse-incomplete if
the buffer lyskom-unparsed-buffer is exhausted."
  (let ((char (lyskom-parse-char)))
    (while (or (= char ?\ )
               (= char ?\n))
      (setq char (lyskom-parse-char)))
    char))

  
(defun lyskom-parse-char ()
  "Get next character from lyskom-unparsed-buffer and increase 
lyskom-parse-pos. Signal lyskom-parse-incomplete if the buffer 
lyskom-unparsed-buffer is exhausted."
  (cond
   ((< lyskom-parse-pos (point-max))
    (prog1 (char-after lyskom-parse-pos)
      (++ lyskom-parse-pos)))
   (t (signal 'lyskom-parse-incomplete nil))))


(defun lyskom-expect-char (char)
  "Read past next non-white character, which must be equal to CHAR.
Return nil, or signal lyskom-protocol-error if the
first non-white character was not equal to CHAR."
  (if (/= char (lyskom-parse-nonwhite-char))
      (lyskom-protocol-error 'lyskom-expect-char
                             "Expecting %S but got %S"
                             char (char-after (1- lyskom-parse-pos)))
    nil))


(defun lyskom-char-p (char)
  "Check if next token is CHAR (a character)."
  (let* ((lyskom-parse-pos lyskom-parse-pos)
         (c (lyskom-parse-nonwhite-char)))
    (eq char c)))

(defun lyskom-string-to-parse ()
  "Return unparsed data as a string."
  (lyskom-string-skip-whitespace     
   (buffer-substring lyskom-parse-pos (point-max))))


(defun lyskom-parse-num ()
  "Parse the next token as a number.
Signal lyskom-parse-incomplete if the number is not followed by whitespace.
Signal lyskom-protocol-error if the next token is not a number."
  (goto-char lyskom-parse-pos)

  (cond
   ((looking-at "[ \n]*[0-9]+")
    (if (char-after (match-end 0))
        (progn (setq lyskom-parse-pos (goto-char (match-end 0)))
               (lyskom-string-to-number (match-string 0)))
      (signal 'lyskom-parse-incomplete nil)))
   ((looking-at "[ \n]*\\'") 
    (goto-char (point-max))
    (signal 'lyskom-parse-incomplete nil))
   (t (lyskom-protocol-error 'lyskom-parse-num
                             "Expected number, got %S"
                             (lyskom-string-to-parse))))
)

(defun lyskom-parse-float ()
  "Parse the next token as a float.
Signal lyskom-parse-incomplete if the number is not followed by whitespace.
Signal lyskom-protocol-error if the next token is not a number."
  (goto-char lyskom-parse-pos)
  (cond ((looking-at "[ \n]*[-+]?[0-9]*\\(\\.\\([0-9]*\\([eE]\\([-+]?[0-9]*\\)?\\)?\\)?\\)?")
         (if (char-after (match-end 0))
             (progn (setq lyskom-parse-pos (goto-char (match-end 0)))
                    (string-to-number (match-string 0)))
           (signal 'lyskom-parse-incomplete nil)))
        ((looking-at "[ \n]*\\'")
         (goto-char (point-max))
         (signal 'lyskom-parse-incomplete nil))
        (t (lyskom-protocol-error 'lyskom-parse-num
                                  "Expected float, got %S"
                                  (lyskom-string-to-parse)))))



(defun lyskom-parse-string ()
  "Parse the next token as a string in the server coding system.
Signal lyskom-parse-incomplete if the string is not complete.
Signal lyskom-protocol-error if the next token is not a string."
  (lyskom-decode-coding-string (lyskom-parse-raw-string)
                               lyskom-server-coding-system))

(defun lyskom-parse-raw-string ()
  "Parse next token as a raw string.
Signal lyskom-parse-incomplete if the string is not complete.
Signal lyskom-protocol-error if the next token is not a string."
  ;; Kludge to deal with leading spaces.
  (lyskom-parse-nonwhite-char)
  (setq lyskom-parse-pos (1- lyskom-parse-pos))
  ;; End kludge.
  (goto-char lyskom-parse-pos)
  (cond
   ((looking-at "[0-9]*\\(\\|H\\)\\'")
    (signal 'lyskom-parse-incomplete nil))
   ((null (looking-at "[0-9]+H"))
    (lyskom-protocol-error 'lyskom-parse-string
                           "Expected hollerith, got %S" 
                           (lyskom-string-to-parse)))   ;Not a legal string.
   (t
    (let* ((num (match-string 0))
           (end (match-end 0))
           (len (lyskom-string-to-number num)))
      (setq lyskom-parse-pos end)
      (cond
       ((< (point-max) (+ lyskom-parse-pos len))
        (lyskom-setq-default lyskom-string-bytes-missing
                             (- (+ lyskom-parse-pos len)
                                (point-max)))
        (signal 'lyskom-parse-incomplete nil))
       (t
        (prog1 (buffer-substring lyskom-parse-pos 
                                 (+ lyskom-parse-pos len))
          (lyskom-setq-default lyskom-string-bytes-missing 0)
          (setq lyskom-parse-pos (+ lyskom-parse-pos len)))))))))


;;(defun lyskom-parse-raw-string ()
;;  "Parse next token as a raw string.
;;Signal lyskom-parse-incomplete if the string is not complete.
;;Signal lyskom-protocol-error if the next token is not a string."
;;  ;; Kludge to deal with leading spaces.
;;  (lyskom-parse-nonwhite-char)
;;  (setq lyskom-parse-pos (1- lyskom-parse-pos))
;;  ;; End kludge.
;;  (let ((to-parse (lyskom-string-to-parse)))
;;    (cond
;;     ((string-match "\\`[0-9]*\\(\\|H\\)\\'" to-parse)
;;      (signal 'lyskom-parse-incomplete nil))
;;     ((null (string-match "\\`[0-9]+H" to-parse))
;;      (lyskom-protocol-error 'lyskom-parse-string
;;                             "Expected hollerith, got %S" 
;;                             to-parse)) ;Not a legal string.
;;     (t
;;      (let ((end (match-end 0))
;;            (len (lyskom-string-to-number to-parse)))
;;        (setq lyskom-parse-pos (+ lyskom-parse-pos end))
;;        (cond
;;         ((< (point-max) (+ lyskom-parse-pos len))
;;          (lyskom-setq-default lyskom-string-bytes-missing
;;                (- (+ lyskom-parse-pos len)
;;                   (point-max)))
;;          (signal 'lyskom-parse-incomplete nil))
;;         (t
;;          (prog1 (buffer-substring lyskom-parse-pos 
;;                                   (+ lyskom-parse-pos len))
;;            (lyskom-setq-default lyskom-string-bytes-missing 0)
;;            (setq lyskom-parse-pos (+ lyskom-parse-pos len))))))))))

(defun lyskom-parse-coding ()
  "Parse next token as a raw string.
Signal lyskom-parse-incomplete if the string is not complete.
Signal lyskom-protocol-error if the next token is not a string."
  ;; Kludge to deal with leading spaces.
  (lyskom-parse-nonwhite-char)
  (setq lyskom-parse-pos (1- lyskom-parse-pos))
  ;; End kludge.
  (let ((to-parse (lyskom-string-to-parse)))
    (cond
     ((string-match "\\`[0-9]*\\(\\|C\\)\\'" to-parse)
      (signal 'lyskom-parse-incomplete nil))
     ((null (string-match "\\`[0-9]+C" to-parse))
      (lyskom-protocol-error 'lyskom-parse-string
                             "Expected hollerith, got %S" 
                             to-parse)) ;Not a legal string.
     (t
      (let ((end (match-end 0))
	    (len (lyskom-string-to-number to-parse)))
	(setq lyskom-parse-pos (+ lyskom-parse-pos end))
	(cond
	 ((< (point-max) (+ lyskom-parse-pos len))
	  (lyskom-setq-default lyskom-string-bytes-missing
		(- (+ lyskom-parse-pos len)
		   (point-max)))
	  (signal 'lyskom-parse-incomplete nil))
	 (t
	  (prog1 (buffer-substring lyskom-parse-pos 
				   (+ lyskom-parse-pos len))
            (lyskom-setq-default lyskom-string-bytes-missing 0)
	    (setq lyskom-parse-pos (+ lyskom-parse-pos len))))))))))


(defun lyskom-parse-1-or-0 ()
  "Parse next nonwhite char and return t if it was 1, nil if it was 0.
Signal lyskom-protocol-error if it was neither 1 nor 0.
Signal lyskom-parse-incomplete if there is no nonwhite char to parse."
  (let ((char (lyskom-parse-nonwhite-char)))
    (cond
     ((= char ?0) nil)
     ((= char ?1) t)
     (t (lyskom-protocol-error 'lyskom-parse-1-or-0
                               "Expected boolean, got %S" char)))))


(defun lyskom-parse-bitstring (default)
  "Parse a generic bit string"
  (let ((result nil)
        (char (lyskom-parse-nonwhite-char))
	(continue t))
    (while (and continue default)
      (cond ((eq char ?0) (setq result (cons nil result)
			       default (cdr default)
			       char (lyskom-parse-char)))

            ((eq char ?1) (setq result (cons t result)
			       default (cdr default)
			       char (lyskom-parse-char)))
            ((or (= char ?\ )
                 (= char ?\n))
	     ;; This occurs when the received string is shorter than
	     ;; expected.
             (setq continue nil))

            (t (lyskom-protocol-error 'lyskom-parse-bitstring
                                      "Expected bool or space, got %S"
                                      char))))
    (if (not (memq char '(?\  ?\n)))
	;; This occurs when the received string is longer than
	;; expected.
        (progn
          (lyskom-parse-skip-rest-of-token)
          (nreverse result))
      (nconc (nreverse result) (copy-sequence default)))))


(defun lyskom-parse-time ()
  "Parse a time from server. Args: none."
  (lyskom-create-time-from-utc
   (lyskom-parse-num)			;sec
   (lyskom-parse-num)			;min
   (lyskom-parse-num)			;hour
   (lyskom-parse-num)			;mday
   (1+ (lyskom-parse-num))		;mon
   (+ 1900 (lyskom-parse-num))		;year
   (lyskom-parse-num)			;wday
   (1+ (lyskom-parse-num))		;yday
   (lyskom-parse-num)))			;isdst


;;; ================================================================
;;; Skip tokens. (Used e g to skip unknown asynchronous messages.)


(defun lyskom-skip-tokens (to-skip)
  "Skip next TO-SKIP tokens"
  (while (not (zerop to-skip))
    (lyskom-skip-one-token)
    (-- to-skip)))


(defun lyskom-skip-one-token ()
  (let ((to-parse (lyskom-string-to-parse)))
    (cond
     ((string-match "\\`{" to-parse)	;Array/list?
        (lyskom-parse-nonwhite-char)
        (lyskom-skip-array))
     ((string-match "\\`*" to-parse)	;Empty array/list?
        (lyskom-parse-nonwhite-char))	;Simply skip it.
     ((string-match "\\`[0-9]+H" to-parse) ;Hollerith string?
        (lyskom-parse-string))
     ((string-match "\\`[0-9]+[ \t\n\r]" to-parse) ;Number?
        (lyskom-parse-num))
     ((string-match "\\`[0-9]\\'" to-parse)	;Incomplete number?
        (signal 'lyskom-parse-incomplete nil))
     (t (lyskom-protocol-error 'lyskom-skip-one-token
                               "Unrecognized token")))))


(defun lyskom-skip-array ()
  (let ((to-parse (lyskom-string-to-parse)))
    (cond
     ((string-match "\\`}" to-parse)
      (lyskom-parse-nonwhite-char))
     (t (lyskom-skip-one-token)
	(lyskom-skip-array)))))
  

;;; ================================================================
;;; Medium level parsing. Parse arrays, misc-info-lists
;;; and other complex LysKOM types.


(defun lyskom-parse-vector (len parser)
  "Parse a vector with LEN elements.
Each element is parsed by PARSER, a function that takes no arguments."
  (cond
   ((zerop len) (if (lyskom-char-p ?*)
                    (lyskom-expect-char ?*)
                  (lyskom-expect-char ?\{)
                  (lyskom-expect-char ?\})))
   ((lyskom-char-p ?*) (lyskom-expect-char ?*))
   (t (lyskom-expect-char ?{)
      (prog1
          (let ((res nil))
            (while (> len 0)
              (setq res (cons (funcall parser) res) len (1- len)))
            (apply 'vector (nreverse res)))
;        (lyskom-fill-vector (make-vector len nil) parser)
	(lyskom-expect-char ?})))))

(defun lyskom-parse-list (len parser)
  "Parse a vector with LEN elements and return it as a list.
Each element is parsed by PARSER, a function that takes no arguments."
  (cond
   ((zerop len) (if (lyskom-char-p ?*)
                    (lyskom-expect-char ?*)
                  (lyskom-expect-char ?\{)
                  (lyskom-expect-char ?\})) nil)
   ((lyskom-char-p ?*) (lyskom-expect-char ?*) nil)
   (t (lyskom-expect-char ?{)
      (let ((result nil))
        (while (> len 0)
          (setq result (cons (funcall parser) result))
          (setq len (1- len)))
        (prog1
            (nreverse result)
          (lyskom-expect-char ?}))))))


(defun lyskom-fill-vector (vector parser)
  "Fill a vector. Args: VECTOR PARSER.
Fills in all elements in VECTOR. PARSER is called for each element and the
result is assigned to the element."
  (let ((index 0)
	(len (length vector)))
    (while (< index len)
      (aset vector index (funcall parser))
      (setq index (1+ index))))
  vector)


(defun lyskom-parse-conf-type ()
  "Parse a conf-type. No args."
  (apply 'lyskom-create-conf-type (lyskom-parse-bitstring 
                                   '(nil nil nil nil t nil nil nil))))
          
(defun lyskom-parse-privs ()
  "Parse privileges. No args."
  (apply 'lyskom-create-privs
         (lyskom-parse-bitstring
          '(nil nil nil t t t nil nil nil nil nil nil nil nil nil nil))))


(defun lyskom-parse-flags ()
  "Parse Personal_flags. No args."
  (apply 'lyskom-create-flags
         (lyskom-parse-bitstring
          '(nil nil nil nil nil nil nil nil))))

	
(defun lyskom-parse-misc-info-list ()
  "Parse a misc-info-list."
  (let ((n (lyskom-parse-num))		;Number of misc-items to parse.
	(char (lyskom-parse-nonwhite-char)))
    (cond
     ((= char ?*)			;Empty list.
      nil)
     ((= char ?{)			;Start of list.
      (prog1
	  (lyskom-parse-misc-info-list-sub n)
	(lyskom-expect-char ?})))
     (t (lyskom-protocol-error 'lyskom-parse-misc-info-list
                               "Expected * or {, got %S" char)))))


(defun lyskom-parse-misc-info-list-sub (n)
  "Parse a misc-info list with N items."
  (let* ((result (list 'dummy))
	 (last result)
	 (next-key (lyskom-parse-num))
	 (res))
    (while (> n 0)
      (cond
       ((eq next-key 0)			;recpt
	(setq res (lyskom-parse-misc-recipient 'RECPT last n)))
       ((eq next-key 1)			;cc-recpt
	(setq res (lyskom-parse-misc-recipient 'CC-RECPT last n)))
       ((eq next-key 2)			;comm-to
	(setq res (lyskom-parse-misc-comm-to last n)))
       ((eq next-key 3)			;comm-in
	(setq res (lyskom-parse-misc-comm-in last n)))
       ((eq next-key 4)			;footn-to
	(setq res (lyskom-parse-misc-footn-to last n)))
       ((eq next-key 5)			;footn-in
	(setq res (lyskom-parse-misc-footn-in last n)))
       ((eq next-key 15)                ;bcc-recpt
        (setq res (lyskom-parse-misc-recipient 'BCC-RECPT last n)))
       (t (lyskom-protocol-error 'lyskom-parse-misc-info-list-sub
                                 "Unknown misc-info type %S" next-key)))
      (setq n (car res))
      (setq next-key (cdr res))
      (setq last (cdr last)))
    (cdr result)))			;Don't return the dummy element.
      
	      
(defun lyskom-parse-misc-recipient (type last n)
  "Parse a recipient. Args: TYPE LAST N.
TYPE is either RECPT, CC-RECPT or BCC-RECPT.
LAST is a pointer to the last element on a misc-info-list.
N is number of misc-items left to parse.
Returns (cons n next-key)."
  (setcdr last (cons (lyskom-create-empty-misc-info) nil))
  (let ((info (car (cdr last)))
	(next-key nil))
      (set-misc-info->type info type)
    (set-misc-info->recipient-no info (lyskom-parse-num))
    (setq n (1- n))
    ;; A loc-no should follow.
    (if (/= 6 (lyskom-next-num n nil))
        (lyskom-protocol-error 'lyskom-parse-misc-recipient
                               "Expected 6, got something else"))
    (set-misc-info->local-no info (lyskom-parse-num))
    (setq n (1- n))
    ;; A rec-time might follow.
    (if (= 7 (setq next-key (lyskom-next-num n nil)))
	(progn
	  (set-misc-info->rec-time info (lyskom-parse-time))
	  (setq n (1- n))
	  (setq next-key nil)))
    ;; A sent-by might follow.
    (if (= 8 (setq next-key (lyskom-next-num n next-key)))
	(progn
	  (set-misc-info->sender info (lyskom-parse-num))
	  (setq n (1- n))
	  (setq next-key nil)))
    ;; A sent-at might follow.
    (if (= 9 (setq next-key (lyskom-next-num n next-key)))
	(progn
	  (set-misc-info->sent-at info (lyskom-parse-time))
	  (setq n (1- n))
	  (setq next-key nil)))
    ;; Return n and next-key.
    (cons n (lyskom-next-num n next-key))))


(defun lyskom-parse-misc-comm-to (last n)
  "Parse a comm-to. Args: LAST N.
LAST is a pointer to the last element on a misc-info-list.
N is number of misc-items left to parse.
Returns (cons n next-key)."
  (setcdr last (cons (lyskom-create-empty-misc-info) nil))
  (let ((info (car (cdr last)))
	(next-key nil))
    (set-misc-info->type info 'COMM-TO)
    (set-misc-info->comm-to info (lyskom-parse-num))
    (setq n (1- n))
    ;; A sent-by might follow.
    (if (= 8 (setq next-key (lyskom-next-num n nil)))
	(progn
	  (set-misc-info->sender info (lyskom-parse-num))
	  (setq n (1- n))
	  (setq next-key nil)))
    ;; A sent-at might follow.
    (if (= 9 (setq next-key (lyskom-next-num n next-key)))
	(progn
	  (set-misc-info->sent-at info (lyskom-parse-time))
	  (setq n (1- n))
	  (setq next-key nil)))
    ;; Return n and next-key.
    (cons n (lyskom-next-num n next-key))))


(defun lyskom-parse-misc-footn-to (last n)
  "Parse a footn-to. Args: LAST N.
LAST is a pointer to the last element on a misc-info-list.
N is number of misc-items left to parse.
Returns (cons n next-key)."
  (setcdr last (cons (lyskom-create-empty-misc-info) nil))
  (let ((info (car (cdr last)))
	(next-key nil))
    (set-misc-info->type info 'FOOTN-TO)
    (set-misc-info->footn-to info (lyskom-parse-num))
    (setq n (1- n))
    ;; A sent-at might follow.
    (if (= 9 (setq next-key (lyskom-next-num n nil)))
	(progn
	  (set-misc-info->sent-at info (lyskom-parse-time))
	  (setq n (1- n))
	  (setq next-key nil)))
    ;; Return n and next-key.
    (cons n (lyskom-next-num n next-key))))


(defun lyskom-parse-misc-comm-in (last n)
  "Parse a comm-in. Args: LAST N.
LAST is a pointer to the last element on a misc-info-list.
N is number of misc-items left to parse.
Returns (cons n next-key)."
  (setcdr last (cons (lyskom-create-empty-misc-info) nil))
  (let ((info (car (cdr last))))
    (set-misc-info->type info 'COMM-IN)
    (set-misc-info->comm-in info (lyskom-parse-num))
    (setq n (1- n))
    ;; Return n and next-key.
    (cons n (lyskom-next-num n nil))))


(defun lyskom-parse-misc-footn-in (last n)
  "Parse a footn-in. Args: LAST N.
LAST is a pointer to the last element on a misc-info-list.
N is number of misc-items left to parse.
Returns (cons n next-key)."
  (setcdr last (cons (lyskom-create-empty-misc-info) nil))
  (let ((info (car (cdr last))))
    (set-misc-info->type info 'FOOTN-IN)
    (set-misc-info->footn-in info (lyskom-parse-num))
    (setq n (1- n))
    ;; Return n and next-key.
    (cons n (lyskom-next-num n nil))))

    
(defun lyskom-next-num (items-to-parse pre-fetched)
  "Parse next number if PRE-FETCHED is nil and ITEMS-TO-PARSE is greater
than 0. Args: ITEMS-TO-PARSE PRE-FETCHED. Returns -1 if ITEMS-TO-PARSE is
0."
  (cond
   ((zerop items-to-parse) -1)
   (pre-fetched)
   (t (lyskom-parse-num))))


;;; ================================================================
;;;         High level parsing. Parsing of entire datatypes.


(defun lyskom-parse-number-array ()
  "Parse an array of integers."
  (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-num))


(defun lyskom-parse-membership-type ()
  "Parse a membership type"
  (let ((type
         (apply 'lyskom-create-membership-type
                (lyskom-parse-bitstring '(nil nil nil nil nil nil nil nil)))))
    (set-membership-type->message-flag
     type
     (eq (membership-type->passive type)
         (membership-type->message-flag type)))
    type))

(defun lyskom-parse-member-old ()
  "Parse an old-style member record."
  (lyskom-create-member (lyskom-parse-num) 
                        0
                        (lyskom-current-client-time)
                        (lyskom-create-membership-type
                         nil nil nil nil nil nil nil nil)))

(defun lyskom-parse-member ()
  "Parse a member record"
  (lyskom-create-member
   (lyskom-parse-num)                   ;conf-no
   (lyskom-parse-num)                   ;added-by
   (lyskom-parse-time)                  ;added-at
   (lyskom-parse-membership-type)       ;type
   ))

(defun lyskom-parse-member-list ()
  "Parse a list of members"
  (lyskom-create-member-list
   (lyskom-parse-vector (lyskom-parse-num)
                        'lyskom-parse-member)))

(defun lyskom-convert-read-ranges-to-map (read-ranges)
  (let ((result nil)
        a b)
    (while read-ranges
      (setq a (car (car read-ranges))
            b (cdr (car read-ranges))
            read-ranges (cdr read-ranges))
      (while (<= a b) (setq result (cons a result) a (1+ a))))
    (and result (apply 'vector (nreverse result)))))

(defun lyskom-parse-membership-11 ()
  "Parse a membership, converting read-ranges to the old style."
  (let ((read-ranges nil))
    (lyskom-create-membership
     (lyskom-parse-num)                 ;position
     (lyskom-parse-time)                ;last-time-read
     (lyskom-parse-num)			;conf-no
     (lyskom-parse-num)			;priority
     (progn (setq read-ranges 
                  (listify-vector
                   (lyskom-parse-vector
                    (lyskom-parse-num) 'lyskom-parse-read-range)))
            (if (eq (car (car read-ranges)) 1)
                (prog1 (cdr (elt read-ranges 0))
                       (setq read-ranges (cdr read-ranges)))
              0))
     (lyskom-convert-read-ranges-to-map read-ranges)
     (lyskom-parse-num)                 ;added-by
     (lyskom-parse-time)                ;added-at
     (lyskom-parse-membership-type))))

(defun lyskom-parse-membership-10 ()
  "Parse a membership."
    (lyskom-create-membership
     (lyskom-parse-num)                 ;position
     (lyskom-parse-time)                ;last-time-read
     (lyskom-parse-num)			;conf-no
     (lyskom-parse-num)			;priority
     (lyskom-parse-num)			;last-text-read
     (lyskom-parse-vector               ;read-texts
      (lyskom-parse-num) 'lyskom-parse-num)
     (lyskom-parse-num)                 ;added-by
     (lyskom-parse-time)                ;added-at
     (lyskom-parse-membership-type)))

(defun lyskom-parse-membership-old ()
  "Parse a membership."
  (lyskom-create-membership
   nil
   (lyskom-parse-time)			;last-time-read
   (lyskom-parse-num)			;conf-no
   (lyskom-parse-num)			;priority
   (lyskom-parse-num)			;last-text-read
   (lyskom-parse-vector			;read-texts
    (lyskom-parse-num) 'lyskom-parse-num)
   0
   (lyskom-current-client-time)
   (lyskom-create-membership-type nil nil nil nil nil nil nil nil)))


(defun lyskom-parse-version-info ()
  "Parse info about the server and protocol."
  (lyskom-create-version-info
   (lyskom-parse-num)
   (lyskom-parse-string)
   (lyskom-parse-string)))

(defun lyskom-parse-server-info ()
  "Parse info about the server."
  (lyskom-create-server-info
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-aux-item-list)))

(defun lyskom-parse-server-info-old ()
  "Parse info about the server."
  (lyskom-create-server-info
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-num)
   (lyskom-parse-num)))


(defun lyskom-parse-map ()
  "Parse a text-list (also known as map)."
  (lyskom-create-map
   (lyskom-parse-num)			;first-local
   (lyskom-parse-vector			;text-nos
    (lyskom-parse-num) 'lyskom-parse-num)))

(defun lyskom-parse-who-info ()
  "Parse a who-info."
  (lyskom-create-who-info
   (lyskom-parse-num)			;pers-no
   (lyskom-parse-num)			;working-conf
   (lyskom-parse-num)			;connection
   (lyskom-parse-string)		;doing-what
   (lyskom-parse-string)))		;userid@host

(defun lyskom-parse-who-info-ident ()
  "Parse a who-info-ident"
  (lyskom-create-who-info
   (lyskom-parse-num)			;pers-no
   (lyskom-parse-num)			;working-conf
   (lyskom-parse-num)			;connection
   (lyskom-parse-string)		;doing-what
   (lyskom-parse-string)		;userid@host
   (lyskom-parse-string)                ;hostname
   (lyskom-parse-string)                ;ident-user
   ))


(defun lyskom-parse-session-info ()
  "Parse a session-info."
  (lyskom-create-session-info
   (lyskom-parse-num)			;pers-no
   (lyskom-parse-num)			;working-conf
   (lyskom-parse-num)			;connection
   (lyskom-parse-string)		;doing
   (lyskom-parse-string)		;userid@host
   nil                                  ;username
   nil                                  ;ident-user
   (lyskom-parse-num)			;idletime
   (lyskom-parse-time)))		;connect-time

(defun lyskom-parse-session-info-ident ()
  "Parse a session-info."
  (lyskom-create-session-info
   (lyskom-parse-num)			;pers-no
   (lyskom-parse-num)			;working-conf
   (lyskom-parse-num)			;connection
   (lyskom-parse-string)		;doing
   (lyskom-parse-string)		;userid@host
   (lyskom-parse-string)                ;username
   (lyskom-parse-string)                ;ident-user
   (lyskom-parse-num)			;idletime
   (lyskom-parse-time)))                ;connect-time

;; prot-A.txt says that this should allow more or less flags than
;; specified, but I can't figure out how. /davidk

(defun lyskom-parse-session-flags ()
  "Parse session-flags."
  (apply 'lyskom-create-session-flags
         (lyskom-parse-bitstring
          '(nil nil nil nil nil nil nil nil))))

	
(defun lyskom-parse-dynamic-session-info ()
  "Parse a dynamic-session-info."
  (lyskom-create-dynamic-session-info
   (lyskom-parse-num)			;session-no
   (lyskom-parse-num)			;pers-no
   (lyskom-parse-num)			;working-conf
   (lyskom-parse-num)			;idle-time
   (lyskom-parse-session-flags)		;session-flags
   (lyskom-parse-string)))		;doing


;;; High level parsing. Parsing of complete replies.


(defun lyskom-parse-void ()
  "Parse result from functions that only return an OK/FAILURE."
  t)					;Needn't do anything.
			  

(defun lyskom-parse-conf-no-list ()
  "Parse result from functions that return conf-no-list"
  (lyskom-create-conf-no-list
   (lyskom-parse-vector (lyskom-parse-num)
                        'lyskom-parse-num)))


(defun lyskom-parse-conf-list ()
  "Parse result from functions that return a conf-list."
  (let* ((list-len (lyskom-parse-num)))
    (lyskom-create-conf-list
     (lyskom-parse-vector list-len 'lyskom-parse-num)
     (lyskom-parse-vector list-len 'lyskom-parse-conf-type))))


(defun lyskom-parse-member-list-old ()
  "Parse result from functions that return a conf-no-list."
  (lyskom-create-member-list
   (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-member-old)))


(defun lyskom-parse-mark-list ()
  "Parser result from functions that returns a mark-list."
  (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-mark))


(defun lyskom-parse-mark ()
  "Parse a marked text."
  (lyskom-create-mark
   (lyskom-parse-num)			;Text-no
   (lyskom-parse-num)))			;Mark-type

(defun lyskom-parse-aux-item-list ()
  "Parse an aux-item list"
  (listify-vector
   (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-aux-item)))

(defun lyskom-parse-aux-item ()
  "Parse an aux-item"
  (lyskom-aux-item-after-parse
   (lyskom-create-aux-item (lyskom-parse-num)    ; aux-no
                           (lyskom-parse-num)    ; tag
                           (lyskom-parse-num)    ; creator
                           (lyskom-parse-time)   ; sent-at
                           (lyskom-parse-aux-item-flags)
                           (lyskom-parse-num)    ; inherit-limit
                           (lyskom-parse-raw-string) ; data
                           )))

(defun lyskom-parse-aux-item-flags ()
  "Parse aux-item flags"
  (apply 'lyskom-create-aux-item-flags
         (lyskom-parse-bitstring
          '(nil nil nil nil nil nil nil nil))))
  

;;;================================================================
;;; Parsing of datatypes with cache


(defun lyskom-parse-static-session-info (session)
  "Parse a static-session-info and add it to the cache."
  (let ((info (lyskom-create-static-session-info
	       (lyskom-parse-string)	;username
	       (lyskom-parse-string)	;hostname
	       (lyskom-parse-string)	;ident-user
	       (lyskom-parse-time))))	;connection-time
    (lyskom-save-excursion
    	(set-buffer lyskom-buffer)
    	(cache-add-static-session-info session info))
    info))
    

(defun lyskom-parse-conf-stat-old (conf-no)
  "Parse a conf-stat, add add it in the cache.
Retuns the conf-stat. Args: CONF-NO."
  (let
      ((conf-stat (lyskom-create-conf-stat
		   conf-no		;conf-no (supplied by
					; initiate-get-conf-stat)
		   (lyskom-parse-string) ;name
		   (lyskom-parse-conf-type) ;conf-type
		   (lyskom-parse-time)	;creation-time
		   (lyskom-parse-time)	;last-written
		   (lyskom-parse-num)	;creator
		   (lyskom-parse-num)	;presentation
		   (lyskom-parse-num)	;supervisor
		   (lyskom-parse-num)	;permitted-submitters
		   (lyskom-parse-num)	;super-conf
		   (lyskom-parse-num)	;msg-of-day
		   (lyskom-parse-num)	;garb-nice
                   77                   ;fake keep-commented
		   (lyskom-parse-num)	;no-of-members
		   (lyskom-parse-num)	;first-local-no
		   (lyskom-parse-num)))) ;no-of-texts
    
    (lyskom-save-excursion
     (set-buffer lyskom-buffer)
     (cache-add-conf-stat conf-stat))
    conf-stat))

(defun lyskom-parse-conf-stat (conf-no)
  "Parse a conf-stat, add add it in the cache.
Retuns the conf-stat. Args: CONF-NO."
  (let
      ((conf-stat (lyskom-create-conf-stat
		   conf-no		;conf-no (supplied by
					; initiate-get-conf-stat)
		   (lyskom-parse-string) ;name
		   (lyskom-parse-conf-type) ;conf-type
		   (lyskom-parse-time)	;creation-time
		   (lyskom-parse-time)	;last-written
		   (lyskom-parse-num)	;creator
		   (lyskom-parse-num)	;presentation
		   (lyskom-parse-num)	;supervisor
		   (lyskom-parse-num)	;permitted-submitters
		   (lyskom-parse-num)	;super-conf
		   (lyskom-parse-num)	;msg-of-day
		   (lyskom-parse-num)	;garb-nice
                   (lyskom-parse-num)   ;keep-commented
		   (lyskom-parse-num)	;no-of-members
		   (lyskom-parse-num)	;first-local-no
		   (lyskom-parse-num)   ;no-of-texts
                   (lyskom-parse-num)   ;expire
                   (lyskom-parse-aux-item-list)
                   )))
    
    (lyskom-save-excursion
     (set-buffer lyskom-buffer)
     (cache-add-conf-stat conf-stat))
    conf-stat))



(defun lyskom-parse-uconf-stat (conf-no)
  "Parse a uconf-stat, and add it to the cache.
Returns the conf-stat. Args CONF-NO."
  (let ((conf-stat (lyskom-create-uconf-stat conf-no
					     (lyskom-parse-string)
					     (lyskom-parse-conf-type)
					     (lyskom-parse-num)
					     (lyskom-parse-num))))
    (lyskom-save-excursion
     (set-buffer lyskom-buffer)
     (cache-add-uconf-stat conf-stat))
    conf-stat))


(defun lyskom-parse-pers-stat (pers-no)
  "Parse a pers-stat, add add it in the cache.
Retuns the pers-stat. Args: PERS-NO."
  (let
      ((pers-stat
	(lyskom-create-pers-stat
	 pers-no			;pers-no
	 (lyskom-parse-string)		;username
	 (lyskom-parse-privs)		;privileges
	 (lyskom-parse-flags)		;flags
	 (lyskom-parse-time)		;last-login
	 (lyskom-parse-num)		;user-area
	 (lyskom-parse-num)		;total-time-present
	 (lyskom-parse-num)		;sessions
	 (lyskom-parse-num)		;created-lines
	 (lyskom-parse-num)		;created-bytes
	 (lyskom-parse-num)		;read-texts
	 (lyskom-parse-num)		;no-of-text-fetches
	 (lyskom-parse-num)		;created-persons
	 (lyskom-parse-num)		;created-confs
	 (lyskom-parse-num)		;first-created-text
	 (lyskom-parse-num)		;no-of-created-texts
	 (lyskom-parse-num)		;no-of-marks
	 (lyskom-parse-num))))		;no-of-confs
    (lyskom-save-excursion
     (set-buffer lyskom-buffer)
     (cache-add-pers-stat pers-stat))
    pers-stat))


(defun lyskom-parse-text-stat-old (text-no)
  "Parse a text-stat and add it in the cache. 
Args: TEXT-NO. Value: text-stat."
  (let
      ((text-stat (lyskom-create-text-stat
		   text-no
		   (lyskom-parse-time)	;creation-time
		   (lyskom-parse-num)	;author
		   (lyskom-parse-num)	;no-of-lines
		   (lyskom-parse-num)	;no-of-chars
		   (lyskom-parse-num)	;no-of-marks
		   (lyskom-parse-misc-info-list)))) ;misc-info-list
    (lyskom-save-excursion
     (set-buffer lyskom-buffer)
     (cache-add-text-stat text-stat))
    text-stat))

(defun lyskom-parse-text-stat (text-no)
  "Parse a text-stat and add it in the cache. 
Args: TEXT-NO. Value: text-stat."
  (let
      ((text-stat (lyskom-create-text-stat
		   text-no
		   (lyskom-parse-time)	;creation-time
		   (lyskom-parse-num)	;author
		   (lyskom-parse-num)	;no-of-lines
		   (lyskom-parse-num)	;no-of-chars
		   (lyskom-parse-num)	;no-of-marks
		   (lyskom-parse-misc-info-list)  ;misc-info-list
                   (lyskom-parse-aux-item-list)
                   )))
    (lyskom-save-excursion
     (set-buffer lyskom-buffer)
     (cache-add-text-stat text-stat))
    text-stat))


(defun lyskom-parse-text (text-no)
  "Parse a text and add it to the cache.
  Args: TEXT-NO. Result: text-stat."
  (let ((text (lyskom-create-text
	       text-no
	       (lyskom-parse-raw-string))))		;The text.
    (lyskom-save-excursion
     (set-buffer lyskom-buffer)
     (cache-add-text text))
    text))


(defun lyskom-parse-conf-z-info-list ()
  "Parse result from functions that return a conf-z-info-list."
  (let* ((list-len (lyskom-parse-num)))
    (lyskom-create-conf-z-info-list
     (lyskom-parse-vector list-len 'lyskom-parse-conf-z-info))))

(defun lyskom-parse-conf-z-info ()
  "Parse a conf-z-info."
  (lyskom-create-conf-z-info
   (lyskom-parse-string)		;name
   (lyskom-parse-conf-type)		;conf-type
   (lyskom-parse-num)))			;conf-no


(defun lyskom-parse-text-mapping (existing)
  "Parse a Text-Mapping"
  (let ((block-type nil))
    (lyskom-create-text-mapping
     (lyskom-parse-num)
     (lyskom-parse-num)
     existing
     (lyskom-parse-1-or-0)
     (let ((val (lyskom-parse-num)))
       (cond ((= val 0) (setq block-type 'sparse))
             ((= val 1) (setq block-type 'dense)))
       block-type)
     (lyskom-parse-local-to-global-block block-type))))

(defun lyskom-parse-local-to-global-block (block-type)
  "Parse a Local-To-Global-Block"
  (cond ((eq block-type 'sparse)
         (let ((len (lyskom-parse-num)))
           (lyskom-parse-list len 'lyskom-parse-text-number-pair)))
        ((eq block-type 'dense)
         (lyskom-parse-map))))

(defun lyskom-parse-text-number-pair ()
  "Parse a Text-Number-Pair"
  (lyskom-create-text-pair (lyskom-parse-num) (lyskom-parse-num)))

(defun lyskom-parse-read-range ()
  "Parse a Read-Range"
  (lyskom-create-text-pair (lyskom-parse-num) (lyskom-parse-num)))

(defun lyskom-parse-stats-description ()
  "Parse a Stats-Description"
  (let ((tmp (lyskom-create-stats-description
              (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-string)
              (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-num))))
    (lyskom-save-excursion
      (set-buffer lyskom-buffer)
      (setq lyskom-stats-description tmp))))

(defun lyskom-parse-stats ()
  "Parse a Stats"
  (lyskom-create-stats (lyskom-parse-float)
                       (lyskom-parse-float)
                       (lyskom-parse-float)))

(defun lyskom-parse-stats-array ()
  "Parse an array of Stats"
  (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-stats))

(defun lyskom-parse-static-server-info ()
  "Parse a static-server-info"
  (let ((tmp (lyskom-create-static-server-info (lyskom-parse-time)
                                               (lyskom-parse-time)
                                               (lyskom-parse-string)
                                               (lyskom-parse-num)
                                               (lyskom-parse-num)
                                               (lyskom-parse-num)
                                               (lyskom-parse-num)
                                               (lyskom-parse-num))))
    (lyskom-save-excursion
      (set-buffer lyskom-buffer)
      (setq lyskom-static-server-info tmp))))

(defun lyskom-parse-scheduling-info ()
  "Parse a scheduling-info"
  (lyskom-create-scheduling-info (lyskom-parse-num)
                                 (lyskom-parse-num)))


;;; ================================================================
;;;          Parsing of complex datatypes without cache.


(defun lyskom-parse-membership-list-11 ()
  "Parse a membership-list. Returns a vector."
   (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-membership-11))

(defun lyskom-parse-membership-list-10 ()
  "Parse a membership-list. Returns a vector."
   (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-membership-10))

(defun lyskom-parse-membership-list-old ()
  "Parse a membership-list. Returns a vector."
   (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-membership-old))


(defun lyskom-parse-who-info-list ()
  "Parse a who-info-list. Returns a vector."
  (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-who-info))
  
(defun lyskom-parse-who-info-ident-list ()
  "Parse a who-info-ident-list. Returns a vector."
  (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-who-info-ident))
  

(defun lyskom-parse-dynamic-session-info-list ()
  "Parse a who-info-list. Returns a vector."
  (lyskom-parse-vector (lyskom-parse-num) 'lyskom-parse-dynamic-session-info))
  

(defun lyskom-init-parse (buffer)
  "Does all initialization of the parsing routines.
i.e creates the buffer, sets all markers and pointers."
  (setq lyskom-is-parsing nil)
  (setq lyskom-unparsed-buffer 
	(lyskom-generate-new-buffer 
	 (concat (if lyskom-debug-communications-to-buffer "" " ")
		 (buffer-name)
		 "-replies")))
  (lyskom-set-buffer-parent lyskom-unparsed-buffer lyskom-buffer)
  (lyskom-update-inherited-variables lyskom-unparsed-buffer)
  (save-current-buffer (set-buffer lyskom-unparsed-buffer)
                       (lyskom-set-buffer-multibyte nil))
  (setq lyskom-unparsed-marker 
	(lyskom-save-excursion
	 (let ((proc lyskom-proc))
	   (set-buffer lyskom-unparsed-buffer)
 	   (make-local-variable 'lyskom-proc)
	   (make-local-variable 'lyskom-string-bytes-missing)
	   (setq lyskom-proc proc)
	   (make-local-variable 'lyskom-buffer)
	   (setq lyskom-buffer buffer)
           (lyskom-setq-default lyskom-string-bytes-missing 0)
	   (goto-char (point-max))
	   (point-marker)))))


;;; ================================================================
;;;        Functions that call the other parsing functions. 


(defun lyskom-parse-success (ref-no buffer)
  "Parse the results of a successful call and call the handler."
  (lyskom-save-excursion
   (set-buffer buffer)
   (let* ((kom-queue (cdr (assq ref-no lyskom-pending-calls)))
	  (call-info (lyskom-locate-ref-no kom-queue ref-no)))
     (set-buffer lyskom-unparsed-buffer)
     (if call-info
	 (lyskom-apply-parser call-info))
     (set-buffer buffer)
     (lyskom-decrease-pending-calls)
     (setq lyskom-pending-calls
	   (lyskom-assoc-dremove ref-no lyskom-pending-calls))
     (lyskom-check-call kom-queue))))


(defun lyskom-locate-ref-no (kom-queue ref-no)
  (let ((pending (lyskom-queue->all-entries
		  (kom-queue->pending
		   (cdr (assoc kom-queue lyskom-call-data)))))
	(result nil))
    (while (and (null result)
		(not (null pending)))
      (cond
       ((and (eq (car (car pending)) 'CALL)
	     (eq (car (cdr (car pending))) ref-no))
	(setq result (car pending)))
       (t (setq pending (cdr pending)))))
    result))

     
(defun lyskom-assoc-dremove (elt list)
  "Args: ELT LIST. Return a copy of LIST, but exclude any elements whose
car is equal to ELT."
  (let* ((head (cons nil list))
	 (tail head))
    (while (not (null (cdr tail)))
      (cond
       ((equal elt (car (car (cdr tail))))
	(setcdr tail (cdr (cdr tail))))
       (t
	(setq tail (cdr tail)))))
    (cdr head)))


(defun lyskom-apply-parser (call-info)
  "Try to parse a reply from the server.
CALL-INFO looks like this: (See lyskom-call-data). 
	('CALL REF-NO PARSER PARSER-DATA HANDLER HANDLER-DATA)
PARSER is called, and if it succeeds
CALL-INFO is destructively changed to
	('PARSED RESULT HANDLER HANDLER-DATA)"
  (let ((result
	 (apply (car (cdr (cdr call-info)))	     ;Parser
		(car (cdr (cdr (cdr call-info))))))) ;Parser-data
    ;; If some part of the reply has not yet arrived
    ;; the parser will signal lyskom-parse-incomplete
    ;; and execution will not continue here.
    ;; The parse is complete. Change the call-info.
    (lyskom-tr-call-to-parsed call-info result)))


(defun lyskom-tr-call-to-parsed (call-info result)
  "Transform a CALL to a PARSED."
  (setcar call-info 'PARSED)
  (setcar (cdr (cdr call-info)) result)
  (setcdr (cdr (cdr call-info)) (cdr (cdr (cdr (cdr call-info))))))


(defun lyskom-parse-error (ref-no buffer)
  "Parse the result of an unsuccessful call and call the handler."
  (lyskom-save-excursion
    (set-buffer buffer)
    (let* ((kom-queue (cdr (assq ref-no lyskom-pending-calls)))
	   (call-info (lyskom-locate-ref-no kom-queue ref-no))
	   errno err-stat)
      (set-buffer lyskom-unparsed-buffer)
      (setq errno (lyskom-parse-num))
      (setq err-stat (lyskom-parse-num))		;Skip ref_no.
      (set-buffer buffer)
      (setq lyskom-errno errno)
      (setq lyskom-err-stat err-stat)
      (setq lyskom-pending-calls
	    (lyskom-assoc-dremove ref-no lyskom-pending-calls))
      (lyskom-decrease-pending-calls)
      (when call-info
	  (lyskom-tr-call-to-parsed call-info nil))
      (lyskom-check-call kom-queue))))


(defun lyskom-parse-unparsed ()
  "Parse all complete replies in lyskom-unparsed-buffer.
All parsing is to take place in this buffer but calling the functions:
lyskom-parse-success, lyskom-parse-error and lyskom-parse-async calls 
functions and variables that are connected with the lyskom-buffer."
  (let ((lyskom-buffer (current-buffer))
	(match-data (match-data)))
    ;; Was the server saving?
    (if lyskom-is-saving
	(progn
	  (setq mode-line-process (lyskom-get-string
				   (if lyskom-executing-command
				       'mode-line-working
				     'mode-line-waiting))
		lyskom-is-saving nil)
	  ;; I guess the following two lines could be replaced by
	  ;; force-mode-line-update in a modern emacs.
	  (set-buffer-modified-p (buffer-modified-p))
	  (sit-for 0)

	  ;; Removed check for kom-presence-messages
	  (if (and (not (lyskom-is-in-minibuffer)))
	      (message ""))))
    (if lyskom-parser-recovering
        (lyskom-save-excursion
          (set-buffer lyskom-unparsed-buffer)
          (goto-char (point-min))
          (if (re-search-forward "^[:%=]" nil t)
              (progn (delete-region (point-min) (match-beginning 0))
                     (lyskom-set-default 'lyskom-parser-recovering nil))
            (when (> (point-max) (point-min))
              (delete-region (point-min) (1- (point-max))))))
      (lyskom-save-excursion
        (set-buffer lyskom-unparsed-buffer)
        (while (not (zerop (1- (point-max)))) ;Parse while replies.
          (let* ((lyskom-parse-pos 1)
                 (key (lyskom-parse-nonwhite-char))
                 (endpos nil))
            (unwind-protect
                (condition-case err
                    (let ((inhibit-quit t)) ; Used to be nil, but that can
					; cause hard-to-repair
					; problems
                      (cond
                       ((= key ?=)      ;The call succeeded.
                        (lyskom-parse-success (lyskom-parse-num) lyskom-buffer))
                       ((= key ?%)      ;The call was not successful.
                        (lyskom-parse-error (lyskom-parse-num) lyskom-buffer))
                       ((= key ?:)      ;An asynchronous message.
                        (lyskom-parse-async (lyskom-parse-num) lyskom-buffer))
                       (t
                        (lyskom-protocol-error 'lyskom-parse-unparsed
                                               "Expected =, %% or :, got %S"
                                               (lyskom-string-to-parse))))
                      (setq endpos lyskom-parse-pos))
                  ;; One reply is now parsed.
                  (lyskom-protocol-error
                   (setq endpos (min (point-max) (1+ lyskom-parse-pos)))
                   (signal 'lyskom-protocol-error err))
                  (lyskom-parse-incomplete (setq endpos (point-min))
                                           (signal (car err) (cdr err))))
              (delete-region (point-min) (or endpos lyskom-parse-pos)))
            (goto-char (point-min))
            (if (looking-at "[ \n]+")
                (delete-region (match-beginning 0) (match-end 0)))
            ))))
    (store-match-data match-data)))

(defun lyskom-protocol-error (function format-string &rest args)
  (when lyskom-debug-communications-to-buffer
    (setq lyskom-debug-communications-limit nil)
    (lyskom-debug-insert lyskom-proc
                         (format " Protocol error in %S: " function)
                         (apply 'format format-string args))
    (lyskom-debug-insert lyskom-proc
                         " Backtrace:"
                         "")
    (let ((standard-output
           (get-buffer-create lyskom-debug-communications-to-buffer-buffer)))
      (backtrace))
    (lyskom-debug-insert lyskom-proc
                         (format " Current string: ")
                         (buffer-substring lyskom-parse-pos (point-max))))

  (lyskom-save-backtrace (lyskom-string-to-parse))
  (lyskom-parse-recover)
  (signal 'lyskom-protocol-error
          (format "Protocol error in %S: %s"
                  function
                  (apply 'format format-string args))))

(defun lyskom-parse-recover ()
  "Initiate parser recovery."
  (lyskom-save-excursion
    (set-buffer lyskom-buffer)
    (setq lyskom-parser-recovering t)
    (initiate-get-time 'main nil)))

;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: cache.el
;;;;
;;;; This file contains all functions which have to do with
;;;; caching various data types in the LysKOM client.
;;;;
;;;;

;;; ================================================================
;;;			UConf-stat cache

(defun cache-get-uconf-stat (conf-no)
  "Get uconf-stat for conference CONF-NO, or nil if nothing is cached.
If full conf-stat is cached, construct an uconf-stat from that data and
cache it."
  (or (cache-assoc conf-no lyskom-uconf-cache)
      (and (lyskom-have-feature long-conf-types)
           (cache-construct-uconf-stat (cache-get-conf-stat conf-no)))))

(defun cache-construct-uconf-stat (conf)
  "If conf is non-nil, create an uconf-stat from conf and cache it.
Return the new uconf-stat or nil"
  (let ((tmp nil))
    (and conf
         (cache-add-uconf-stat 
          (setq tmp
                (lyskom-create-uconf-stat (conf-stat->conf-no conf)
                                          (conf-stat->name conf)
                                          (conf-stat->conf-type conf)
                                          (+
                                           (conf-stat->first-local-no conf)
                                           (conf-stat->no-of-texts conf))
                                          (conf-stat->garb-nice conf)))))
    tmp))

(defun cache-add-uconf-stat (uconf-stat)
  "Insert a UCONF-STAT in the cache."
  (cache-add (uconf-stat->conf-no uconf-stat)
	     uconf-stat
	     'lyskom-uconf-cache))

(defun cache-del-uconf-stat (conf-no)
  "Delete a conf-stat from the cache. Args: CONF-NO."
  (cache-del conf-no 'lyskom-uconf-cache)
  (cache-del conf-no 'lyskom-conf-cache))


;;; ================================================================
;;;                     Conf-stat cache.


(defun cache-get-conf-stat (conf-no)
  "Get conf-stat for conference CONF-NO, or nil if nothing is cached."
  (cache-assoc conf-no lyskom-conf-cache))

(defun cache-add-conf-stat (conf-stat)
  "Insert a CONF-STAT in the cache."
  (cache-add (conf-stat->conf-no conf-stat) conf-stat 'lyskom-conf-cache))


(defun cache-del-conf-stat (conf-no)
  "Delete a conf-stat from the cache. Args: CONF-NO."
  (cache-del conf-no 'lyskom-conf-cache)
  (cache-del conf-no 'lyskom-uconf-cache))



;;; ================================================================
;;;                       Pers-stat cache.


(defun cache-get-pers-stat (pers-no)
  "Get pers-stat for person PERS-NO, or nil if nothing is cached."
  (cache-assoc pers-no lyskom-pers-cache))

  
(defun cache-add-pers-stat (pers-stat)
  "Insert a PERS-STAT in the cache."
  (cache-add (pers-stat->pers-no pers-stat) pers-stat 'lyskom-pers-cache))


(defun cache-del-pers-stat (pers-no)
  "Delete a pers-stat from the cache. Args: PERS-NO."
  (cache-del pers-no 'lyskom-pers-cache))
  


;;; ================================================================
;;;                       Text-stat cache.


(defun cache-get-text-stat (text-no)
  "Get text-stat for texton TEXT-NO, or nil if nothing is cached."
  (cache-assoc text-no lyskom-text-cache))

  
(defun cache-add-text-stat (text-stat)
  "Insert a TEXT-STAT in the cache."
  (cache-add (text-stat->text-no text-stat) text-stat 'lyskom-text-cache))


(defun cache-del-text-stat (text-no)
  "Delete a text-stat from the cache. Args: TEXT-NO."
  (cache-del text-no 'lyskom-text-cache))


  
;;; ================================================================
;;;                 Text cache (the text strings).


(defun cache-get-text (text-no)
  "Get text for textno TEXT-NO, or nil if nothing is cached."
  (let ((tx (cache-assoc text-no lyskom-text-mass-cache)))
    (cond
     ((lyskom-text-p tx) tx))))

  
(defun cache-add-text (text)
  "Insert a TEXT in the cache."
  (cache-add (text->text-no text) text 'lyskom-text-mass-cache))


(defun cache-del-text (text-no)
  "Delete a text from the cache. Args: TEXT-NO."
  (cache-del text-no 'lyskom-text-mass-cache))



;;; ================================================================
;;;                      Marked texts cache.


(defun cache-get-marked-texts ()
  "Return the a list of marks for all marked texts."
  lyskom-marked-text-cache)


(defun cache-set-marked-texts (mark-array)
  "Sets the marks for all marked texts. A mark-list is an array, but we
want a list in the cache, so this remakes it into a list."
  (setq lyskom-marked-text-cache (listify-vector mark-array)))


(defun cache-text-is-marked (text-no)
  "Return the mark if the text text-no is marked by the current user,
otherwise return nil"
  (let ((marks lyskom-marked-text-cache)
	(mark nil))
    (while (and (not (null marks))
		(null mark))
      (if (equal text-no (mark->text-no (car marks)))
	  (setq mark (car marks))
	(setq marks (cdr marks))))
    mark))


(defun cache-add-marked-text (text-no mark-type)
  "Insert a mark into the cache. If it is already there, replace it."
  (let ((marks lyskom-marked-text-cache)
	(found nil)
	(mark (lyskom-create-mark text-no mark-type)))
    (while (and (not found)
		(not (null marks)))
      (if (and (car marks)
	       (equal text-no (mark->text-no (car marks))))
	  (progn
	    (setcar marks mark)
	    (setq found t))
	(setq marks (cdr marks))))
    (if (not found)
	(setq lyskom-marked-text-cache
	      (cons mark lyskom-marked-text-cache)))))


(defun cache-del-marked-text (text-no)
  "Remove the mark from the cache of marked texts if it is there.
+++BUG: A mark is replaced with a nil and not removed."
  (let ((marks lyskom-marked-text-cache)
	(found nil))
    (while (and (not found)
		(not (null marks)))
      (if (equal text-no (mark->text-no (car marks)))
	  (progn
	    (setcar marks nil)
	    (setq found t))
	(setq marks (cdr marks))))))


;;; ================================================================
;;;                 Static-session-info cache


(defun cache-get-static-session-info (session)
  "Get static-session-info for session SESSION, or nil if nothing is cached."
  (let ((tx (cache-assoc session lyskom-static-session-info-cache)))
    (cond
     ((lyskom-static-session-info-p tx) tx))))

  
(defun cache-add-static-session-info (session info)
  "Insert INFO in the cache."
  (cache-add session info 'lyskom-static-session-info-cache))


;; NOTUSED: cache-del-static-session-info
(defun cache-del-static-session-info (session)
  "Delete a text from the cache. Args: SESSION."
  (cache-del session 'lyskom-static-session-info-cache))


;;; ================================================================
;;;                     Generic cache routines

(defvar lyskom-caches nil
  "A list of all the caches in use.
This is used to clear all caches with `clear-all-caches'") 

(defun cache-create (cache)
  (set cache nil)
  (setq lyskom-caches (cons cache lyskom-caches)))

(defun cache-assoc (key cache)
  "Get data for item with key KEY from CACHE.
CACHE is an assoc-list in this implementation."
  (cdr-safe (assoc key cache)))


(defun cache-add (key data cache)
  "Add DATA to CACHE under the key KEY.
Args: KEY DATA CACHE.
CACHE is a (the only one) quoted variable pointing to the cache (an alist).
The variable might be changed."
  (if (null (symbol-value cache))
      (cache-create cache))
  (let ((oldval (assoc key (symbol-value cache))))
    (cond
     ((null oldval)
      (set cache
	   (cons (cons key data)

		 (symbol-value cache))))
     (t
      (setcdr oldval data)))))


(defun cache-del (key cache)
  "Delete item with key KEY from CACHE.
CACHE is the name of the variable that points to the cache."
  (let ((oldval (assoc key (symbol-value cache))))
    (if oldval
	(setcdr oldval nil))))		;A pair (key . nil) will remain.
					;Fix this bug someday. +++

(defun cache-clear (cache)
  (set cache nil)
  (setq lyskom-caches (delete cache lyskom-caches)))


(defun clear-all-caches ()
  (mapc (function (lambda (cache) (set cache nil)))
	  lyskom-caches)
  (setq lyskom-caches nil))

;;; ================================================================
;;;         lyskom-tell-server (this is also a sort of cache)


(def-kom-var lyskom-what-i-am-doing nil
  "What the client thinks the server thinks the user is doing."
  local)


(defun lyskom-tell-server (string)
  "Tell the server what the user is doing. Args: STRING."
  (unless lyskom-is-anonymous
    (save-excursion
      (when lyskom-buffer
        (set-buffer lyskom-buffer))
      (cond
       ((equal string lyskom-what-i-am-doing))
       (t
        (setq lyskom-what-i-am-doing string)
        (initiate-change-what-i-am-doing 'background nil (or string "")))))))

(provide 'lyskom-cache)
;;;;; -*-coding: raw-text;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: view-mode.el
;;;;
;;;; LysKOM view mode, a simple move for viewing buffers.
;;;;


(defvar lyskom-view-mode-map nil
  "Keymap for LysKOM view mode")

(eval-when-compile (defvar view-mode-map nil))

(lyskom-external-function view-major-mode)

(defun lyskom-view-base-mode ()
  (cond ((fboundp 'view-major-mode) (view-major-mode))
        ((assq 'view-mode minor-mode-alist)
         (let* ((keymap (copy-keymap lyskom-view-mode-map)))
           (make-local-variable 'minor-mode-map-alist)
           (lyskom-set-keymap-parent keymap view-mode-map)
           (setq minor-mode-map-alist (cons (cons 'view-mode keymap)
                                            minor-mode-map-alist))
           (view-mode)))
        (t (view-mode))))


(if lyskom-view-mode-map
    nil
  (setq lyskom-view-mode-map (make-sparse-keymap))
  (define-key lyskom-view-mode-map "*" 'kom-button-press)
  (define-key lyskom-view-mode-map "=" 'kom-menu-button-press)
  (define-key lyskom-view-mode-map "\t" 'kom-next-link)
  (define-key lyskom-view-mode-map [(meta tab)] 'kom-previous-link)
  (define-key lyskom-view-mode-map (kbd (lyskom-keys 'button2up)) 'kom-button-click)
  (define-key lyskom-view-mode-map (kbd (lyskom-keys 'button3)) 'kom-popup-menu)
  (define-key lyskom-view-mode-map "q" 'lyskom-view-mode-quit))

(defun lyskom-view-mode-quit ()
  (interactive)
  (let ((buf (current-buffer)))
    (lyskom-undisplay-buffer buf)
    (kill-buffer buf)))

(define-derived-mode lyskom-view-mode 
  lyskom-view-base-mode
  "LysKOM View"
  "Major mode for viewing buffers")

;;;;; -*-coding: x-ctext;-*-
;;;;;
;;;;; ================================================================
;;;;;            _       _____    ____  _   _______   ________
;;;;;           | |     / /   |  / __ \/ | / /  _/ | / / ____/
;;;;;           | | /| / / /| | / /_/ /  |/ // //  |/ / / __
;;;;;           | |/ |/ / ___ |/ _, _/ /|  // // /|  / /_/ /
;;;;;           |__/|__/_/  |_/_/ |_/_/ |_/___/_/ |_/\____/
;;;;;
;;;;; This file is coded using x-ctext, since it contains both
;;;;; iso-8859-1 and iso-8859-2. If you open the file using any
;;;;; other coding, you'll probably screw it up.
;;;;;
;;;;; Look for the little x at the bottom-left of your Emacs frame!
;;;;;
;;;;; ================================================================
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: names.el
;;;;
;;;; This file contains code to deal with namedays
;;;;

(def-komtype nameday-data
  ((code            :read-only t)
   (name            :read-only t)
   (description     :read-only t)
   (data            :read-only t)))

(defvar lyskom-namedays nil)

(defun lyskom-define-namedays (code name description data)
  "Define a new collection of namedays.
CODE is the unique code for the nameday collection.
NAME is a short name for the collection, in the language of the collection
DESCRIPTION is a longer description of the collection, in the langauge of
the collection.
DATA is nameday data"
  (setq lyskom-namedays
        (cons (cons code (lyskom-create-nameday-data
                          code name description data))
              lyskom-namedays)))


(defun lyskom-nameday (&optional now)
  (cond ((eq kom-show-namedays t) (lyskom-nameday-2 lyskom-language now))
        ((symbolp kom-show-namedays) (lyskom-nameday-2 kom-show-namedays now))
        ((listp kom-show-namedays)
         (mapconcat (lambda (x) (lyskom-nameday-2 x now t))
                    kom-show-namedays  "\n"))
        (t nil)))

(defun lyskom-nameday-2 (sym now &optional show-list-name)
  (let* ((time (or now (lyskom-current-server-time)))
         (data (cdr (assq sym lyskom-namedays)))
         (alist (and data (nameday-data->data data)))
         (mlist (cdr (assq (time->mon time) alist)))
         (dlist (cdr (assq (time->mday time) mlist))))
    (if data
        (cond ((null dlist) 
               (lyskom-format 'no-nameday nil (and show-list-name (nameday-data->name data))))
              ((eq 1 (length dlist))
               (lyskom-format 'has-nameday-1 
                              (car dlist)
                              (and show-list-name (nameday-data->name data))))
              ((eq 2 (length dlist))
               (lyskom-format 'has-nameday-2
                              (elt dlist 0)
                              (elt dlist 1)
                              (and show-list-name (nameday-data->name data))))
              (t (lyskom-format 'has-nameday-2
                                (mapconcat 'identity (lyskom-butlast dlist 1) ", ")
                                (elt dlist (1- (length dlist)))
                                (and show-list-name (nameday-data->name data)))))
      "")))



(lyskom-define-namedays 'sv 
                        "Sverige"
                        "\
Namnsdagar enligt svenska språknämndens nya namnlängd (2001)."
                        '((1 . ((1 . ())
                                (2 . ("Svea"))
                                (3 . ("Alfred" "Alfrida"))
                                (4 . ("Rut"))
                                (5 . ("Hanna" "Hannele"))
                                (6 . ("Kasper" "Melker" "Baltsar"))
                                (7 . ("August" "Augusta"))
                                (8 . ("Erland"))
                                (9 . ("Gunnar" "Gunder"))
                                (10 . ("Sigurd" "Sigbritt"))
                                (11 . ("Jan" "Jannike"))
                                (12 . ("Frideborg" "Fridolf"))
                                (13 . ("Knut"))
                                (14 . ("Felix" "Felicia"))
                                (15 . ("Laura" "Lorentz"))
                                (16 . ("Hjalmar" "Helmer"))
                                (17 . ("Anton" "Tony"))
                                (18 . ("Hilda" "Hildur"))
                                (19 . ("Henrik"))
                                (20 . ("Fabian" "Sebastian"))
                                (21 . ("Agnes" "Agneta"))
                                (22 . ("Vincent" "Viktor"))
                                (23 . ("Frej" "Freja"))
                                (24 . ("Erika"))
                                (25 . ("Paul" "Pål"))
                                (26 . ("Bodil" "Boel"))
                                (27 . ("Göte" "Göta"))
                                (28 . ("Karl" "Karla"))
                                (29 . ("Diana"))
                                (30 . ("Gunilla" "Gunhild"))
                                (31 . ("Ivar" "Joar"))))

                          (2 . ((1 . ("Max" "Maximilian"))
                                (2 . ())
                                (3 . ("Disa" "Hjördis"))
                                (4 . ("Ansgar" "Anselm"))
                                (5 . ("Agata" "Agda"))
                                (6 . ("Dorotea" "Doris"))
                                (7 . ("Rikard" "Dick"))
                                (8 . ("Berta" "Bert"))
                                (9 . ("Fanny" "Franciska"))
                                (10 . ("Iris"))
                                (11 . ("Yngve" "Inge"))
                                (12 . ("Evelina" "Evy"))
                                (13 . ("Agne" "Ove"))
                                (14 . ("Valentin"))
                                (15 . ("Sigfrid"))
                                (16 . ("Julia" "Julius"))
                                (17 . ("Alexandra" "Sandra"))
                                (18 . ("Frida" "Fritiof"))
                                (19 . ("Gabriella" "Ella"))
                                (20 . ("Vivianne"))
                                (21 . ("Hilding"))
                                (22 . ("Pia"))
                                (23 . ("Torsten" "Torun"))
                                (24 . ("Mattias" "Mats"))
                                (25 . ("Sigvard" "Sivert"))
                                (26 . ("Torgny" "Torkel"))
                                (27 . ("Lage"))
                                (28 . ("Maria"))))

                          (3 . ((1 . ("Albin" "Elvira"))
                                (2 . ("Ernst" "Erna"))
                                (3 . ("Gunborg" "Gunvor"))
                                (4 . ("Adrian" "Adriana"))
                                (5 . ("Tora" "Tove"))
                                (6 . ("Ebba" "Ebbe"))
                                (7 . ("Camilla"))
                                (8 . ("Siv"))
                                (9 . ("Torbjörn" "Torleif"))
                                (10 . ("Edla" "Ada"))
                                (11 . ("Edvin" "Egon"))
                                (12 . ("Viktoria"))
                                (13 . ("Greger"))
                                (14 . ("Matilda" "Maud"))
                                (15 . ("Kristoffer" "Christel"))
                                (16 . ("Herbert" "Gilbert"))
                                (17 . ("Gertrud"))
                                (18 . ("Edvard" "Edmund"))
                                (19 . ("Josef" "Josefina"))
                                (20 . ("Joakim" "Kim"))
                                (21 . ("Bengt"))
                                (22 . ("Kennet" "Kent"))
                                (23 . ("Gerda" "Gerd"))
                                (24 . ("Gabriel" "Rafael"))
                                (25 . ())
                                (26 . ("Emanuel"))
                                (27 . ("Rudolf" "Ralf"))
                                (28 . ("Malkolm" "Morgan"))
                                (29 . ("Jonas" "Jens"))
                                (30 . ("Holger" "Holmfrid"))
                                (31 . ("Ester"))))

                          (4 . ((1 . ("Harald" "Hervor"))
                                (2 . ("Gudmund" "Ingesund"))
                                (3 . ("Ferdinand" "Nanna"))
                                (4 . ("Marianne" "Marlene"))
                                (5 . ("Irene" "Irja"))
                                (6 . ("Vilhelm" "Helmi"))
                                (7 . ("Irma" "Irmelin"))
                                (8 . ("Nadja" "Tanja"))
                                (9 . ("Otto" "Ottilia"))
                                (10 . ("Ingvar" "Ingvor"))
                                (11 . ("Ulf" "Ylva"))
                                (12 . ("Liv"))
                                (13 . ("Artur" "Douglas"))
                                (14 . ("Tiburtius"))
                                (15 . ("Olivia" "Oliver"))
                                (16 . ("Patrik" "Patricia"))
                                (17 . ("Elias" "Elis"))
                                (18 . ("Valdemar" "Volmar"))
                                (19 . ("Olaus" "Ola"))
                                (20 . ("Amalia" "Amelie"))
                                (21 . ("Anneli" "Annika"))
                                (22 . ("Allan" "Glenn"))
                                (23 . ("Georg" "Göran"))
                                (24 . ("Vega"))
                                (25 . ("Markus"))
                                (26 . ("Teresia" "Terese"))
                                (27 . ("Engelbrekt"))
                                (28 . ("Ture" "Tyra"))
                                (29 . ("Tyko"))
                                (30 . ("Mariana"))))

                          (5 . ((1 . ("Valborg"))
                                (2 . ("Filip" "Filippa"))
                                (3 . ("John" "Jane"))
                                (4 . ("Monika" "Mona"))
                                (5 . ("Gotthard" "Erhard"))
                                (6 . ("Marit" "Rita"))
                                (7 . ("Carina" "Carita"))
                                (8 . ("Åke"))
                                (9 . ("Reidar" "Reidun"))
                                (10 . ("Esbjörn" "Styrbjörn"))
                                (11 . ("Märta" "Märit"))
                                (12 . ("Charlotta" "Lotta"))
                                (13 . ("Linnea" "Linn"))
                                (14 . ("Halvard" "Halvar"))
                                (15 . ("Sofia" "Sonja"))
                                (16 . ("Ronald" "Ronny"))
                                (17 . ("Rebecka" "Ruben"))
                                (18 . ("Erik"))
                                (19 . ("Maj" "Majken"))
                                (20 . ("Karolina" "Carola"))
                                (21 . ("Konstantin" "Conny"))
                                (22 . ("Hemming" "Henning"))
                                (23 . ("Desideria" "Desiree"))
                                (24 . ("Ivan" "Vanja"))
                                (25 . ("Urban"))
                                (26 . ("Vilhelmina" "Vilma"))
                                (27 . ("Beda" "Blenda"))
                                (28 . ("Ingeborg" "Borghild"))
                                (29 . ("Yvonne" "Jeanette"))
                                (30 . ("Vera" "Veronika"))
                                (31 . ("Petronella" "Pernilla"))))

                          (6 . ((1 . ("Gun" "Gunnel"))
                                (2 . ("Rutger" "Roger"))
                                (3 . ("Ingemar" "Gudmar"))
                                (4 . ("Solbritt" "Solveig"))
                                (5 . ("Bo"))
                                (6 . ("Gustav" "Gösta"))
                                (7 . ("Robert" "Robin"))
                                (8 . ("Eivor" "Majvor"))
                                (9 . ("Börje" "Birger"))
                                (10 . ("Svante" "Boris"))
                                (11 . ("Bertil" "Berthold"))
                                (12 . ("Eskil"))
                                (13 . ("Aina" "Aino"))
                                (14 . ("Håkan" "Hakon"))
                                (15 . ("Margit" "Margot"))
                                (16 . ("Axel" "Axelina"))
                                (17 . ("Torborg" "Torvald"))
                                (18 . ("Björn" "Bjarne"))
                                (19 . ("Germund" "Görel"))
                                (20 . ("Linda"))
                                (21 . ("Alf" "Alvar"))
                                (22 . ("Paulina" "Paula"))
                                (23 . ("Adolf" "Alice"))
                                (24 . ())
                                (25 . ("David" "Salomon"))
                                (26 . ("Rakel" "Lea"))
                                (27 . ("Selma" "Fingal"))
                                (28 . ("Leo"))
                                (29 . ("Peter" "Petra"))
                                (30 . ("Elof" "Leif"))))

                          (7 . ((1 . ("Aron" "Mirjam"))
                                (2 . ("Rosa" "Rosita"))
                                (3 . ("Aurora"))
                                (4 . ("Ulrika" "Ulla"))
                                (5 . ("Laila" "Ritva"))
                                (6 . ("Esaias" "Jessika"))
                                (7 . ("Klas"))
                                (8 . ("Kjell"))
                                (9 . ("Jörgen" "Örjan"))
                                (10 . ("Andre" "Andrea"))
                                (11 . ("Eleonora" "Ellinor"))
                                (12 . ("Herman" "Hermine"))
                                (13 . ("Joel" "Judit"))
                                (14 . ("Folke"))
                                (15 . ("Ragnhild" "Ragnvald"))
                                (16 . ("Reinhold" "Reine"))
                                (17 . ("Bruno"))
                                (18 . ("Fredrik" "Fritz"))
                                (19 . ("Sara"))
                                (20 . ("Margareta" "Greta"))
                                (21 . ("Johanna"))
                                (22 . ("Magdalena" "Madeleine"))
                                (23 . ("Emma"))
                                (24 . ("Kristina" "Kerstin"))
                                (25 . ("Jakob"))
                                (26 . ("Jesper"))
                                (27 . ("Marta"))
                                (28 . ("Botvid" "Seved"))
                                (29 . ("Olof"))
                                (30 . ("Algot"))
                                (31 . ("Helena" "Elin"))))

                          (8 . ((1 . ("Per"))
                                (2 . ("Karin" "Kajsa"))
                                (3 . ("Tage"))
                                (4 . ("Arne" "Arnold"))
                                (5 . ("Ulrik" "Alrik"))
                                (6 . ("Alfons" "Inez"))
                                (7 . ("Dennis" "Denise"))
                                (8 . ("Silvia" "Sylvia"))
                                (9 . ("Roland"))
                                (10 . ("Lars"))
                                (11 . ("Susanna"))
                                (12 . ("Klara"))
                                (13 . ("Kaj"))
                                (14 . ("Uno"))
                                (15 . ("Stella" "Estelle"))
                                (16 . ("Brynolf"))
                                (17 . ("Verner" "Valter"))
                                (18 . ("Ellen" "Lena"))
                                (19 . ("Magnus" "Måns"))
                                (20 . ("Bernhard" "Bernt"))
                                (21 . ("Jon" "Jonna"))
                                (22 . ("Henrietta" "Henrika"))
                                (23 . ("Signe" "Signhild"))
                                (24 . ("Bartolomeus"))
                                (25 . ("Lovisa" "Louise"))
                                (26 . ("Östen"))
                                (27 . ("Rolf" "Raoul"))
                                (28 . ("Gurli" "Leila"))
                                (29 . ("Hans" "Hampus"))
                                (30 . ("Albert" "Albertina"))
                                (31 . ("Arvid" "Vidar"))))

                          (9 . ((1 . ("Samuel"))
                                (2 . ("Justus" "Justina"))
                                (3 . ("Alfhild" "Alva"))
                                (4 . ("Gisela"))
                                (5 . ("Adela" "Heidi"))
                                (6 . ("Lilian" "Lilly"))
                                (7 . ("Regina" "Roy"))
                                (8 . ("Alma" "Hulda"))
                                (9 . ("Anita" "Annette"))
                                (10 . ("Tord" "Turid"))
                                (11 . ("Dagny" "Helny"))
                                (12 . ("Åsa" "Åslög"))
                                (13 . ("Sture"))
                                (14 . ("Ida"))
                                (15 . ("Sigrid" "Siri"))
                                (16 . ("Dag" "Daga"))
                                (17 . ("Hildegard" "Magnhild"))
                                (18 . ("Orvar"))
                                (19 . ("Fredrika"))
                                (20 . ("Elise" "Lisa"))
                                (21 . ("Matteus"))
                                (22 . ("Maurits" "Moritz"))
                                (23 . ("Tekla" "Tea"))
                                (24 . ("Gerhard" "Gert"))
                                (25 . ("Tryggve"))
                                (26 . ("Enar" "Einar"))
                                (27 . ("Dagmar" "Rigmor"))
                                (28 . ("Lennart" "Leonard"))
                                (29 . ("Mikael" "Mikaela"))
                                (30 . ("Helge"))))

                          (10 . ((1 . ("Ragnar" "Ragna"))
                                 (2 . ("Ludvig" "Love"))
                                 (3 . ("Evald" "Osvald"))
                                 (4 . ("Frans" "Frank"))
                                 (5 . ("Bror"))
                                 (6 . ("Jenny" "Jennifer"))
                                 (7 . ("Birgitta" "Britta"))
                                 (8 . ("Nils"))
                                 (9 . ("Ingrid" "Inger"))
                                 (10 . ("Harry" "Harriet"))
                                 (11 . ("Erling" "Jarl"))
                                 (12 . ("Valfrid" "Manfred"))
                                 (13 . ("Berit" "Birgit"))
                                 (14 . ("Stellan"))
                                 (15 . ("Hedvig" "Hillevi"))
                                 (16 . ("Finn"))
                                 (17 . ("Antonia" "Toini"))
                                 (18 . ("Lukas"))
                                 (19 . ("Tore" "Tor"))
                                 (20 . ("Sibylla"))
                                 (21 . ("Ursula" "Yrsa"))
                                 (22 . ("Marika" "Marita"))
                                 (23 . ("Severin" "Sören"))
                                 (24 . ("Evert" "Eilert"))
                                 (25 . ("Inga" "Ingalill"))
                                 (26 . ("Amanda" "Rasmus"))
                                 (27 . ("Sabina"))
                                 (28 . ("Simon" "Simone"))
                                 (29 . ("Viola"))
                                 (30 . ("Elsa" "Isabella"))
                                 (31 . ("Edit" "Edgar"))))

                          (11 . ((1 . ())
                                 (2 . ("Tobias"))
                                 (3 . ("Hubert" "Hugo"))
                                 (4 . ("Sverker"))
                                 (5 . ("Eugen" "Eugenia"))
                                 (6 . ("Gustav" "Adolf"))
                                 (7 . ("Ingegerd" "Ingela"))
                                 (8 . ("Vendela"))
                                 (9 . ("Teodor" "Teodora"))
                                 (10 . ("Martin" "Martina"))
                                 (11 . ("Mårten"))
                                 (12 . ("Konrad" "Kurt"))
                                 (13 . ("Kristian" "Krister"))
                                 (14 . ("Emil" "Emilia"))
                                 (15 . ("Leopold"))
                                 (16 . ("Vibeke" "Viveka"))
                                 (17 . ("Naemi" "Naima"))
                                 (18 . ("Lillemor" "Moa"))
                                 (19 . ("Elisabet" "Lisbet"))
                                 (20 . ("Pontus" "Marina"))
                                 (21 . ("Helga" "Olga"))
                                 (22 . ("Cecilia" "Sissela"))
                                 (23 . ("Klemens"))
                                 (24 . ("Gudrun" "Rune"))
                                 (25 . ("Katarina" "Katja"))
                                 (26 . ("Linus"))
                                 (27 . ("Astrid" "Asta"))
                                 (28 . ("Malte"))
                                 (29 . ("Sune"))
                                 (30 . ("Andreas" "Anders"))))

                          (12 . ((1 . ("Oskar" "Ossian"))
                                 (2 . ("Beata" "Beatrice"))
                                 (3 . ("Lydia"))
                                 (4 . ("Barbara" "Barbro"))
                                 (5 . ("Sven"))
                                 (6 . ("Nikolaus" "Niklas"))
                                 (7 . ("Angela" "Angelika"))
                                 (8 . ("Virginia"))
                                 (9 . ("Anna"))
                                 (10 . ("Malin" "Malena"))
                                 (11 . ("Daniel" "Daniela"))
                                 (12 . ("Alexander" "Alexis"))
                                 (13 . ("Lucia"))
                                 (14 . ("Sten" "Sixten"))
                                 (15 . ("Gottfrid"))
                                 (16 . ("Assar"))
                                 (17 . ("Stig"))
                                 (18 . ("Abraham"))
                                 (19 . ("Isak"))
                                 (20 . ("Israel" "Moses"))
                                 (21 . ("Tomas"))
                                 (22 . ("Natanael" "Jonatan"))
                                 (23 . ("Adam"))
                                 (24 . ("Eva"))
                                 (25 . ())
                                 (26 . ("Stefan" "Staffan"))
                                 (27 . ("Johannes" "Johan"))
                                 (28 . ("Benjamin"))
                                 (29 . ("Natalia" "Natalie"))
                                 (30 . ("Abel" "Set"))
                                 (31 . ("Sylvester"))))))

(lyskom-define-namedays 'sv-pre-1901
                        "Sverige, före 1901"
                        "\
Namnsdagar enligt namnlängd från före 1901, med uppenbara helgdagar
borttagna."
                        '((1 . ((1 . ())
                                (2 . ("Abel" "Set"))
                                (3 . ("Enoch"))
                                (4 . ("Titus"))
                                (5 . ("Simeon"))
                                (6 . ("Epifania"))
                                (7 . ("Knut"))
                                (8 . ("Erhard"))
                                (9 . ("Julianus"))
                                (10 . ("Nikanor"))
                                (11 . ("Hyginus"))
                                (12 . ("Arcadius"))
                                (13 . ("Knut"))
                                (14 . ("Felix"))
                                (15 . ("Maurus"))
                                (16 . ("Hjalmar"))
                                (17 . ("Anton"))
                                (18 . ("Prisca"))
                                (19 . ("Henrik"))
                                (20 . ("Fabianus"))
                                (21 . ("Agneta"))
                                (22 . ("Vincentius"))
                                (23 . ("Emerentia"))
                                (24 . ("Ericus Translatus"))
                                (25 . ("Paulus"))
                                (26 . ("Polykarpus"))
                                (27 . ("Göte"))
                                (28 . ("Karl"))
                                (29 . ("Valerius"))
                                (30 . ("Adelgunda"))
                                (31 . ("Vigilius"))
                                ))
                          (2 . (
                                (1 . ("Maximiliana"))
                                (2 . ())
                                (3 . ("Blasius"))
                                (4 . ("Ansgar (Ansgarius)"))
                                (5 . ("Agatha"))
                                (6 . ("Dorothea"))
                                (7 . ("Rikard"))
                                (8 . ("Malakias"))
                                (9 . ("Apollonia"))
                                (10 . ("Eugenia"))
                                (11 . ("Eufrosyne"))
                                (12 . ("Eulalia"))
                                (13 . ("Agabus"))
                                (14 . ("Valentin"))
                                (15 . ("Sigfrid"))
                                (16 . ("Juliana"))
                                (17 . ("Alexandra"))
                                (18 . ("Concordia"))
                                (19 . ("Gabinus"))
                                (20 . ("Eukarius" "Hulda"))
                                (21 . ("Hilarius"))
                                (22 . ("Petter Katt"))
                                (23 . ("Tertullianus"))
                                (24 . ("Mattias"))
                                (25 . ("Victorinus"))
                                (26 . ("Nestor"))
                                (27 . ("Leander"))
                                (28 . ("Makarius"))
                                ))
                          (3 . (
                                (1 . ("Albin"))
                                (2 . ("Ernst"))
                                (3 . ("Kunigunda"))
                                (4 . ("Adrian"))
                                (5 . ("Conon"))
                                (6 . ("Fulgentius"))
                                (7 . ("Felicitas"))
                                (8 . ("Philemon"))
                                (9 . ())
                                (10 . ("Apollonius"))
                                (11 . ("Vindicianus"))
                                (12 . ("Victoria"))
                                (13 . ("Gregorius"))
                                (14 . ("Matilda"))
                                (15 . ("Kristofer"))
                                (16 . ("Herbert"))
                                (17 . ("Gertrud"))
                                (18 . ("Edvard"))
                                (19 . ("Josef"))
                                (20 . ("Joakim"))
                                (21 . ("Bengt"))
                                (22 . ("Viktor"))
                                (23 . ("Axel"))
                                (24 . ("Gabriel"))
                                (25 . ())
                                (26 . ("Emanuel"))
                                (27 . ("Rupert"))
                                (28 . ("Malchus"))
                                (29 . ("Jonas"))
                                (30 . ("Qvirinus"))
                                (31 . ("Amos"))
                                ))
                          (4 . (
                                (1 . ("Hugo"))
                                (2 . ("Teodosius"))
                                (3 . ("Ferdinand"))
                                (4 . ("Ambrosius"))
                                (5 . ("Irene"))
                                (6 . ("Vilhelm"))
                                (7 . ("Hegesippus"))
                                (8 . ("Perpetuus"))
                                (9 . ("Otto"))
                                (10 . ("Hezekiel"))
                                (11 . ("Antipas"))
                                (12 . ("Julius"))
                                (13 . ("Artur"))
                                (14 . ("Tiburtius"))
                                (15 . ("Olympias"))
                                (16 . ("Patrik"))
                                (17 . ("Elias"))
                                (18 . ("Valerianus"))
                                (19 . ("Bernhard"))
                                (20 . ("Amalia"))
                                (21 . ("Anselm"))
                                (22 . ("Bernhardina"))
                                (23 . ("Georg"))
                                (24 . ("Vega"))
                                (25 . ("Markus"))
                                (26 . ("Kletus"))
                                (27 . ("Antimus" "Teresia"))
                                (28 . ("Theodora"))
                                (29 . ("Tycho"))
                                (30 . ("Mariana"))
                                ))
                          (5 . (
                                (1 . ("Valborg"))
                                (2 . ("Athanasius"))
                                (3 . ())
                                (4 . ("Monika"))
                                (5 . ("Gotthard"))
                                (6 . ("Sigismund"))
                                (7 . ("Stanislaus"))
                                (8 . ("Achatius"))
                                (9 . ("Timotheus"))
                                (10 . ("Epimachus"))
                                (11 . ("Mamertus"))
                                (12 . ("Charlotta"))
                                (13 . ("Linnea"))
                                (14 . ("Corona"))
                                (15 . ("Sophia"))
                                (16 . ("Peregrinus"))
                                (17 . ("Rebecka"))
                                (18 . ("Erik"))
                                (19 . ("Potentiana" "Potentia"))
                                (20 . ("Karolina"))
                                (21 . ("Konstantin"))
                                (22 . ("Hemming"))
                                (23 . ("Desideria" "Deriree"))
                                (24 . ("Rogatius"))
                                (25 . ("Urban"))
                                (26 . ("Vilhelmina"))
                                (27 . ("Beda"))
                                (28 . ("Germanus"))
                                (29 . ("Maximinus"))
                                (30 . ("Basilius"))
                                (31 . ("Petronella"))
                                ))
                          (6 . (
                                (1 . ("Nikodemus"))
                                (2 . ("Marcellinus"))
                                (3 . ("Erasmus"))
                                (4 . ("Optatus"))
                                (5 . ("Bonifacius"))
                                (6 . ("Gustaf"))
                                (7 . ("Robert"))
                                (8 . ("Salomon"))
                                (9 . ("Primus"))
                                (10 . ("Svante"))
                                (11 . ("Barnabas"))
                                (12 . ("Eskil"))
                                (13 . ("Akvilina"))
                                (14 . ("Elisa"))
                                (15 . ("Vitus"))
                                (16 . ("Justina"))
                                (17 . ("Botolf"))
                                (18 . ("Leontius"))
                                (19 . ("Gervasius"))
                                (20 . ("Florentin"))
                                (21 . ("Albanus"))
                                (22 . ("Paulinus"))
                                (23 . ("Adold"))
                                (24 . ())
                                (25 . ("David"))
                                (26 . ("Jeremias"))
                                (27 . ("Crescens"))
                                (28 . ("Leo"))
                                (29 . ("Petrus" "Paulus"))
                                (30 . ("Martialis"))
                                ))
                          (7 . (
                                (1 . ("Aron"))
                                (2 . ())
                                (3 . ("Anatolius"))
                                (4 . ("Ulrika"))
                                (5 . ("Melker"))
                                (6 . ("Esaias"))
                                (7 . ("Klas"))
                                (8 . ("Kilian"))
                                (9 . ("Cyrillus"))
                                (10 . ("Knut"))
                                (11 . ("Eleonora"))
                                (12 . ("Herman"))
                                (13 . ("Joel"))
                                (14 . ("Fokas"))
                                (15 . ())
                                (16 . ("Reinhold"))
                                (17 . ("Alexius"))
                                (18 . ("Fredrik"))
                                (19 . ("Sara"))
                                (20 . ("Margareta"))
                                (21 . ("Johanna"))
                                (22 . ("Maria Magdalena"))
                                (23 . ("Emma"))
                                (24 . ("Kristina"))
                                (25 . ("Jakob"))
                                (26 . ("Martha"))
                                (27 . ())
                                (28 . ("Botvid"))
                                (29 . ("Olof"))
                                (30 . ("Abdon"))
                                (31 . ("Helena"))
                                ))
                          (8 . (
                                (1 . ())
                                (2 . ("Karin"))
                                (3 . ("Stefanus"))
                                (4 . ("Aristarchus"))
                                (5 . ("Dominikus"))
                                (6 . ("Sixtus"))
                                (7 . ("Donatus"))
                                (8 . ("Cyriacus"))
                                (9 . ("Romanus"))
                                (10 . ("Lars"))
                                (11 . ("Susanna"))
                                (12 . ("Klara"))
                                (13 . ("Hippolytus"))
                                (14 . ("Eusebius"))
                                (15 . ())
                                (16 . ("Brynolf"))
                                (17 . ("Verner"))
                                (18 . ("Agapetus"))
                                (19 . ("Magnus"))
                                (20 . ("Samuel"))
                                (21 . ("Josefina"))
                                (22 . ("Henrietta"))
                                (23 . ("Zacheus"))
                                (24 . ("Bartolomeus"))
                                (25 . ("Lovisa"))
                                (26 . ("Zefyrinus"))
                                (27 . ("Rufus"))
                                (28 . ("Augustinus"))
                                (29 . ())
                                (30 . ("Albert"))
                                (31 . ("Arvid"))
                                ))
                          (9 . (
                                (1 . ("Egidius"))
                                (2 . ("Justus"))
                                (3 . ("Seraphia"))
                                (4 . ("Moses"))
                                (5 . ("Eudoxus"))
                                (6 . ("Zacharias" "Sakarias"))
                                (7 . ("Regina"))
                                (8 . ())
                                (9 . ("Augusta"))
                                (10 . ("Teodard"))
                                (11 . ("Protus"))
                                (12 . ("Cyrus"))
                                (13 . ("Amatus"))
                                (14 . ())
                                (15 . ("Nicetas"))
                                (16 . ("Eufemia"))
                                (17 . ("Lambertus"))
                                (18 . ("Methodius"))
                                (19 . ("Fredrika"))
                                (20 . ("Fausta"))
                                (21 . ("Matteus"))
                                (22 . ("Mauritz"))
                                (23 . ("Tekla"))
                                (24 . ("Gerhard"))
                                (25 . ("KLeofas"))
                                (26 . ("Cyprianus"))
                                (27 . ("Damianus"))
                                (28 . ("Leonard"))
                                (29 . ("Mikael"))
                                (30 . ("Hieronymus"))
                                ))
                          (10 . (
                                 (1 . ("Remigius"))
                                 (2 . ("Ludvig"))
                                 (3 . ("Evald"))
                                 (4 . ("Frans"))
                                 (5 . ("Placidus"))
                                 (6 . ("Bruno"))
                                 (7 . ("Birgitta"))
                                 (8 . ("Demetrius"))
                                 (9 . ("Dionysius"))
                                 (10 . ("Gereon"))
                                 (11 . ("Probus"))
                                 (12 . ("Valfrid"))
                                 (13 . ("Theophilus"))
                                 (14 . ("Calixtus"))
                                 (15 . ("Hedvig"))
                                 (16 . ("Gallus"))
                                 (17 . ("Antoinetta"))
                                 (18 . ("Lukas"))
                                 (19 . ("Ptolemeus"))
                                 (20 . ("Kasper"))
                                 (21 . ("Ursula" "Ursila"))
                                 (22 . ("Severus"))
                                 (23 . ("Severin"))
                                 (24 . ("Evergistus"))
                                 (25 . ("Crispinus"))
                                 (26 . ("Amandus"))
                                 (27 . ("Sabina"))
                                 (28 . ("Simon" "Judas"))
                                 (29 . ("Narcissus"))
                                 (30 . ("Zenobia"))
                                 (31 . ("Quintinus"))
                                 ))
                          (11 . (
                                 (1 . ())
                                 (2 . ("Tobias"))
                                 (3 . ("Eustachius"))
                                 (4 . ("Amatius"))
                                 (5 . ("Eugen"))
                                 (6 . ("Gustaf Adolf"))
                                 (7 . ("Engelbert"))
                                 (8 . ("Willehad"))
                                 (9 . ("Teodor"))
                                 (10 . ("Martin Luther"))
                                 (11 . ("Mårten"))
                                 (12 . ("Konrad"))
                                 (13 . ("Kristian"))
                                 (14 . ("Hypatius"))
                                 (15 . ("Leopold"))
                                 (16 . ("Edmund"))
                                 (17 . ("Napoleon"))
                                 (18 . ("Maximus"))
                                 (19 . ("Elisabet"))
                                 (20 . ("Pontus"))
                                 (21 . ("Heliodorus"))
                                 (22 . ("Cecilia"))
                                 (23 . ("Klemens"))
                                 (24 . ("Kristogonus"))
                                 (25 . ("Katarina"))
                                 (26 . ("Linus"))
                                 (27 . ("Vitalis"))
                                 (28 . ("Sten"))
                                 (29 . ("Saturninus"))
                                 (30 . ("Anders"))
                                 ))
                          (12 . (
                                 (1 . ("Oscar"))
                                 (2 . ("Beata"))
                                 (3 . ("Zephanias"))
                                 (4 . ("Barbro"))
                                 (5 . ("Crispina"))
                                 (6 . ("Nikolaus"))
                                 (7 . ("Agaton"))
                                 (8 . ())
                                 (9 . ("Anna"))
                                 (10 . ("Judit"))
                                 (11 . ("Daniel"))
                                 (12 . ("Alexander"))
                                 (13 . ("Lucia"))
                                 (14 . ("Isidor"))
                                 (15 . ("Ireneus"))
                                 (16 . ("Lazarus"))
                                 (17 . ("Ignatius"))
                                 (18 . ("Abraham"))
                                 (19 . ("Isak"))
                                 (20 . ("Jakob"))
                                 (21 . ("Tomas"))
                                 (22 . ("Josef"))
                                 (23 . ("Israel"))
                                 (24 . ("Adam" "Eva"))
                                 (25 . ())
                                 (26 . ("Stefan"))
                                 (27 . ("Johannes"))
                                 (28 . ())
                                 (29 . ())
                                 (30 . ("David"))
                                 (31 . ("Sylvester"))
                                 ))))

(lyskom-define-namedays 'sv-1901
                        "Sverige, 1901 års namnlängd"
                        "\
Namnsdagar enligt namnlängd från 1901, med uppenbara helgdagar borttagna."
                        '((1 . (
                                (1 . ())
                                (2 . ("Svea"))
                                (3 . ("Alfred"))
                                (4 . ("Rut"))
                                (5 . ("Hanna"))
                                (6 . ())
                                (7 . ("August"))
                                (8 . ("Erland"))
                                (9 . ("Gunnar"))
                                (10 . ("Sigurd"))
                                (11 . ("Hugo"))
                                (12 . ("Frideborg"))
                                (13 . ("Knut"))
                                (14 . ("Felix"))
                                (15 . ("Laura"))
                                (16 . ("Hjalmar"))
                                (17 . ("Anton"))
                                (18 . ("Hilda"))
                                (19 . ("Henrik"))
                                (20 . ("Fabian"))
                                (21 . ("Agnes"))
                                (22 . ("Vincent"))
                                (23 . ("Emilia"))
                                (24 . ("Erika"))
                                (25 . ("Paulus"))
                                (26 . ("Botilda"))
                                (27 . ("Göte"))
                                (28 . ("Karl"))
                                (29 . ("Valter"))
                                (30 . ("Gunhild"))
                                (31 . ("Ivar"))
                                ))
                          (2 . (
                                (1 . ("Max"))
                                (2 . ())
                                (3 . ("Disa"))
                                (4 . ("Ansgar"))
                                (5 . ("Agata"))
                                (6 . ("Dorotea"))
                                (7 . ("Rikard"))
                                (8 . ("Berta"))
                                (9 . ("Fanny"))
                                (10 . ("Eugenia"))
                                (11 . ("Yngve"))
                                (12 . ("Evelina"))
                                (13 . ("Agne"))
                                (14 . ("Valentin"))
                                (15 . ("Sigfrid"))
                                (16 . ("Julia"))
                                (17 . ("Alexandra"))
                                (18 . ("Frida"))
                                (19 . ("Gabriella"))
                                (20 . ("Hulda"))
                                (21 . ("Hilding"))
                                (22 . ("Martina"))
                                (23 . ("Torsten"))
                                (24 . ("Mattias"))
                                (25 . ("Sigvard"))
                                (26 . ("Torgny"))
                                (27 . ("Lage"))
                                (28 . ("Maria"))
                                ))
                          (3 . (
                                (1 . ("Albin"))
                                (2 . ("Ernst"))
                                (3 . ("Gunborg"))
                                (4 . ("Adrian"))
                                (5 . ("Tora"))
                                (6 . ("Ebba"))
                                (7 . ("Ottilia"))
                                (8 . ("Filippa"))
                                (9 . ("Torbjörn"))
                                (10 . ("Edla"))
                                (11 . ("Edvin"))
                                (12 . ("Viktoria"))
                                (13 . ("Greger"))
                                (14 . ("Matilda"))
                                (15 . ("Kristofer"))
                                (16 . ("Herbert"))
                                (17 . ("Gertrud"))
                                (18 . ("Edvard"))
                                (19 . ("Josef"))
                                (20 . ("Joakim"))
                                (21 . ("Bengt"))
                                (22 . ("Viktor"))
                                (23 . ("Gerda"))
                                (24 . ("Gabriel"))
                                (25 . ())
                                (26 . ("Emanuel"))
                                (27 . ("Rudolf"))
                                (28 . ("Malkolm"))
                                (29 . ("Jonas"))
                                (30 . ("Holger"))
                                (31 . ("Ester"))
                                ))
                          (4 . (
                                (1 . ("Harald"))
                                (2 . ("Gudmund"))
                                (3 . ("Ferdinand"))
                                (4 . ("Ambrosius"))
                                (5 . ("Nanna"))
                                (6 . ("Vilhelm"))
                                (7 . ("Ingemund"))
                                (8 . ("Hemming"))
                                (9 . ("Otto"))
                                (10 . ("Ingvar"))
                                (11 . ("Ulf"))
                                (12 . ("Julius"))
                                (13 . ("Artur"))
                                (14 . ("Tiburtius"))
                                (15 . ("Olivia"))
                                (16 . ("Patrik"))
                                (17 . ("Elias"))
                                (18 . ("Valdemar"))
                                (19 . ("Olavus Petri"))
                                (20 . ("Amalia"))
                                (21 . ("Anselm"))
                                (22 . ("Albertina"))
                                (23 . ("Georg"))
                                (24 . ("Vega"))
                                (25 . ("Markus"))
                                (26 . ("Teresia"))
                                (27 . ("Engelbrekt"))
                                (28 . ("Ture"))
                                (29 . ("Tyko"))
                                (30 . ("Mariana"))
                                ))
                          (5 . (
                                (1 . ("Valborg"))
                                (2 . ("Filip"))
                                (3 . ("Göta"))
                                (4 . ("Monika"))
                                (5 . ("Gotthard"))
                                (6 . ("Sigmund"))
                                (7 . ("Gustava"))
                                (8 . ("Åke"))
                                (9 . ("Jonatan"))
                                (10 . ("Esbjörn"))
                                (11 . ("Märta"))
                                (12 . ("Charlotta"))
                                (13 . ("Linnea"))
                                (14 . ("Halvard"))
                                (15 . ("Sofia"))
                                (16 . ("Hilma"))
                                (17 . ("Rebecka"))
                                (18 . ("Erik"))
                                (19 . ("Alrik"))
                                (20 . ("Karolina"))
                                (21 . ("Konstantin"))
                                (22 . ("Henning"))
                                (23 . ("Desideria"))
                                (24 . ("Ragnvald"))
                                (25 . ("Urban"))
                                (26 . ("Vilhelmina"))
                                (27 . ("Blenda"))
                                (28 . ("Ingeborg"))
                                (29 . ("Baltsar"))
                                (30 . ("Fritjof"))
                                (31 . ("Isabella"))
                                ))
                          (6 . (
                                (1 . ("Nikodemus"))
                                (2 . ("Rutger"))
                                (3 . ("Ingemar"))
                                (4 . ("Holmfrid"))
                                (5 . ("Bo"))
                                (6 . ("Gustav"))
                                (7 . ("Robert"))
                                (8 . ("Salomon"))
                                (9 . ("Börje"))
                                (10 . ("Svante"))
                                (11 . ("Barnabas" "Bertil"))
                                (12 . ("Eskil"))
                                (13 . ("Aina"))
                                (14 . ("Håkan"))
                                (15 . ("Justina"))
                                (16 . ("Axel"))
                                (17 . ("Torborg"))
                                (18 . ("Björn"))
                                (19 . ("Germund"))
                                (20 . ("Flora"))
                                (21 . ("Alf"))
                                (22 . ("Paulina"))
                                (23 . ("Adolf"))
                                (24 . ())
                                (25 . ("David"))
                                (26 . ("Rakel"))
                                (27 . ("Selma"))
                                (28 . ("Leo"))
                                (29 . ("Petrus"))
                                (30 . ("Elof"))
                                ))
                          (7 . (
                                (1 . ("Aron"))
                                (2 . ("Rosa"))
                                (3 . ("Aurora"))
                                (4 . ("Ulrika"))
                                (5 . ("Melker"))
                                (6 . ("Esaias"))
                                (7 . ("Klas"))
                                (8 . ("Kjell"))
                                (9 . ("Götilda"))
                                (10 . ("Anund"))
                                (11 . ("Eleonora"))
                                (12 . ("Herman"))
                                (13 . ("Joel"))
                                (14 . ("Folke"))
                                (15 . ("Ragnhild"))
                                (16 . ("Reinhold"))
                                (17 . ("Alexis"))
                                (18 . ("Fredrik"))
                                (19 . ("Sara"))
                                (20 . ("Margareta"))
                                (21 . ("Johanna"))
                                (22 . ("Magdalena"))
                                (23 . ("Emma"))
                                (24 . ("Kristina"))
                                (25 . ("Jakob"))
                                (26 . ("Jesper"))
                                (27 . ("Marta"))
                                (28 . ("Botvid"))
                                (29 . ("Olof"))
                                (30 . ("Algot"))
                                (31 . ("Elin"))
                                ))
                          (8 . (
                                (1 . ("Per"))
                                (2 . ("Karin"))
                                (3 . ("Tage"))
                                (4 . ("Arne"))
                                (5 . ("Ulrik"))
                                (6 . ("Sixten"))
                                (7 . ("Arnold"))
                                (8 . ("Sylvia"))
                                (9 . ("Roland"))
                                (10 . ("Lars"))
                                (11 . ("Susanna"))
                                (12 . ("Klara"))
                                (13 . ("Hillevi"))
                                (14 . ("Ebbe"))
                                (15 . ("Stella"))
                                (16 . ("Brynolf"))
                                (17 . ("Verner"))
                                (18 . ("Helena"))
                                (19 . ("Magnus"))
                                (20 . ("Bernhard"))
                                (21 . ("Josefina"))
                                (22 . ("Henrietta"))
                                (23 . ("Signe"))
                                (24 . ("Bartolomeus"))
                                (25 . ("Lovisa"))
                                (26 . ("Östen"))
                                (27 . ("Rolf"))
                                (28 . ("Augustin"))
                                (29 . ("Hans"))
                                (30 . ("Albert"))
                                (31 . ("Arvid"))
                                ))
                          (9 . (
                                (1 . ("Samuel"))
                                (2 . ("Justus"))
                                (3 . ("Alfhild"))
                                (4 . ("Moses"))
                                (5 . ("Adela"))
                                (6 . ("Sakarias"))
                                (7 . ("Regina"))
                                (8 . ("Alma"))
                                (9 . ("Augusta"))
                                (10 . ("Tord"))
                                (11 . ("Dagny"))
                                (12 . ("Tyra"))
                                (13 . ("Ambjörn"))
                                (14 . ("Ida"))
                                (15 . ("Sigrid"))
                                (16 . ("Eufemia"))
                                (17 . ("Hildegard"))
                                (18 . ("Alvar"))
                                (19 . ("Fredrika"))
                                (20 . ("Agda"))
                                (21 . ("Matteus"))
                                (22 . ("Maurits"))
                                (23 . ("Tekla"))
                                (24 . ("Gerhard"))
                                (25 . ("Signild"))
                                (26 . ("Enar"))
                                (27 . ("Dagmar"))
                                (28 . ("Lennart"))
                                (29 . ("Mikael"))
                                (30 . ("Helge"))
                                ))
                          (10 . (
                                 (1 . ("Ragnar"))
                                 (2 . ("Ludvig"))
                                 (3 . ("Evald"))
                                 (4 . ("Frans"))
                                 (5 . ("Bror"))
                                 (6 . ("Jenny"))
                                 (7 . ("Birgitta"))
                                 (8 . ("Nils"))
                                 (9 . ("Ingrid"))
                                 (10 . ("Helmer"))
                                 (11 . ("Erling"))
                                 (12 . ("Valfrid"))
                                 (13 . ("Teofil"))
                                 (14 . ("Manfred"))
                                 (15 . ("Hedvig"))
                                 (16 . ("Fingal"))
                                 (17 . ("Antoinetta"))
                                 (18 . ("Lukas"))
                                 (19 . ("Tore"))
                                 (20 . ("Kasper" "Sibylla"))
                                 (21 . ("Birger"))
                                 (22 . ("Seved"))
                                 (23 . ("Sören"))
                                 (24 . ("Evert"))
                                 (25 . ("Inga"))
                                 (26 . ("Amanda"))
                                 (27 . ("Sabina"))
                                 (28 . ("Simon"))
                                 (29 . ("Viola"))
                                 (30 . ("Elsa"))
                                 (31 . ("Edit"))
                                 ))
                          (11 . (
                                 (1 . ())
                                 (2 . ("Tobias"))
                                 (3 . ("Hubert"))
                                 (4 . ("Sverker"))
                                 (5 . ("Eugen"))
                                 (6 . ("Gustav Adolf"))
                                 (7 . ("Ingegerd"))
                                 (8 . ("Vendela"))
                                 (9 . ("Teodor"))
                                 (10 . ("Martin Luther"))
                                 (11 . ("Mårten"))
                                 (12 . ("Konrad"))
                                 (13 . ("Kristian"))
                                 (14 . ("Emil"))
                                 (15 . ("Leopold"))
                                 (16 . ("Edmund"))
                                 (17 . ("Napoleon"))
                                 (18 . ("Magnhild"))
                                 (19 . ("Elisabet"))
                                 (20 . ("Pontus"))
                                 (21 . ("Helga"))
                                 (22 . ("Cecilia"))
                                 (23 . ("Klemens"))
                                 (24 . ("Gudrun"))
                                 (25 . ("Katarina"))
                                 (26 . ("Torkel"))
                                 (27 . ("Astrid"))
                                 (28 . ("Malte"))
                                 (29 . ("Sune"))
                                 (30 . ("Anders"))
                                 ))
                          (12 . (
                                 (1 . ("Oskar"))
                                 (2 . ("Beata"))
                                 (3 . ("Lydia"))
                                 (4 . ("Barbro"))
                                 (5 . ("Sven"))
                                 (6 . ("Nikolaus"))
                                 (7 . ("Agaton"))
                                 (8 . ("Virginia"))
                                 (9 . ("Anna"))
                                 (10 . ("Malin"))
                                 (11 . ("Daniel"))
                                 (12 . ("Alexander"))
                                 (13 . ("Lucia"))
                                 (14 . ("Sten"))
                                 (15 . ("Gottfrid"))
                                 (16 . ("Assar"))
                                 (17 . ("Inge"))
                                 (18 . ("Abraham"))
                                 (19 . ("Isak"))
                                 (20 . ("Israel"))
                                 (21 . ("Tomas"))
                                 (22 . ("Natanael"))
                                 (23 . ("Adam"))
                                 (24 . ("Eva"))
                                 (25 . ())
                                 (26 . ("Stefan"))
                                 (27 . ("Johannes"))
                                 (28 . ())
                                 (29 . ("Abel"))
                                 (30 . ("Set"))
                                 (31 . ("Sylvester"))
                                 ))))

(lyskom-define-namedays 'sv-1986
                        "Sverige, 1986 års namnlängd"
                        "\
Namnsdagar enligt namnlängd från 1986, med uppenbara helgdagar borttagna."
                        '((1 . (
                                (1 . ())
                                (2 . ("Svea" "Bore" "Sverre"))
                                (3 . ("Alfred" "Alfrida" "Annefrid"))
                                (4 . ("Rut" "Ritva" "Roger"))
                                (5 . ("Hanna" "Hannele" "Hanny"))
                                (6 . ())
                                (7 . ("August" "Öjar" "Örjan"))
                                (8 . ("Erland" "Erhard" "Erla"))
                                (9 . ("Gunnar" "Gun" "Gunno"))
                                (10 . ("Sigurd" "Sigrun" "Sigyn"))
                                (11 . ("Hugo" "Hagar"))
                                (12 . ("Frideborg" "Fridolf" "Fridolin"))
                                (13 . ("Knut" "Kennet" "Kent"))
                                (14 . ("Felix" "Felicia" "Fritz"))
                                (15 . ("Laura" "Lauritz" "Lotten"))
                                (16 . ("Hjalmar" "Herdis" "Hjördis"))
                                (17 . ("Anton" "Anja" "Antonia"))
                                (18 . ("Hilda" "Hildor" "Hildur"))
                                (19 . ("Henrik" "Henrika" "Henry"))
                                (20 . ("Fabian" "Lina" "Linus"))
                                (21 . ("Agnes" "Agnar" "Agneta"))
                                (22 . ("Vincent" "Vanja" "Veine"))
                                (23 . ("Emilia" "Emilie" "Mildred"))
                                (24 . ("Erika" "Jarl" "Jarla"))
                                (25 . ("Paulus" "Paul" "Paula"))
                                (26 . ("Botilda" "Tilda" "Tilly"))
                                (27 . ("Göte" "Götar" "Jöns"))
                                (28 . ("Karl" "Kally" "Karla"))
                                (29 . ("Valter" "Valerie" "Volter"))
                                (30 . ("Gunhild" "Gunilla" "Gunnel"))
                                (31 . ("Ivar" "Iva" "Ivan"))
                                ))
                          (2 . (
                                (1 . ("Max" "Marielle" "Marietta"))
                                (2 . ())
                                (3 . ("Disa" "Dick" "Didrik"))
                                (4 . ("Ansgar" "Anneli" "Ansa"))
                                (5 . ("Agata" "Aili" "Alvi"))
                                (6 . ("Dorotea" "Dora" "Doris"))
                                (7 . ("Rikard" "Ricky" "Rigmor"))
                                (8 . ("Berta" "Bert" "Bertram"))
                                (9 . ("Fanny" "Sanny" "Sonny"))
                                (10 . ("Eugenia" "Egon" "Eira"))
                                (11 . ("Yngve" "Yvette" "Yvonne"))
                                (12 . ("Evelina" "Elaine" "Evelyn"))
                                (13 . ("Agne" "Alin" "Alina"))
                                (14 . ("Valentin" "Valentina" "Vally"))
                                (15 . ("Sigfrid" "Sigbert" "Sigbritt"))
                                (16 . ("Julia" "Juliana" "Juliette"))
                                (17 . ("Alexandra" "Sandor" "Sandra"))
                                (18 . ("Frida" "Fride" "Frode"))
                                (19 . ("Gabriella" "Ella" "Elna"))
                                (20 . ("Hulda" "Haldis" "Haldo"))
                                (21 . ("Hilding" "Hildeborg" "Hildemar"))
                                (22 . ("Martina" "Tim" "Tina"))
                                (23 . ("Torsten" "Toivo" "Torun"))
                                (24 . ("Mattias" "Matti" "Mats"))
                                (25 . ("Sigvard" "Sigvald" "Sigvor"))
                                (26 . ("Torgny" "Torvald"))
                                (27 . ("Lage" "Laila" "Lave"))
                                (28 . ("Maria" "Marie" "Mary"))
                                ))
                          (3 . (
                                (1 . ("Albin" "Alba" "Alban"))
                                (2 . ("Ernst" "Erna" "Ernfrid"))
                                (3 . ("Gunborg" "Gunbritt" "Gunvald"))
                                (4 . ("Adrian" "Adrienne" "Astor"))
                                (5 . ("Tora" "Toini" "Tor"))
                                (6 . ("Ebba" "Ebon" "Yvonne"))
                                (7 . ("Ottilia" "Petra" "Petronella"))
                                (8 . ("Filippa" "Gunlög" "Åslög"))
                                (9 . ("Torbjörn" "Torben" "Torgun"))
                                (10 . ("Edla" "Edling" "Ethel"))
                                (11 . ("Edvin" "Diana" "Edna"))
                                (12 . ("Viktoria" "Vibeke" "Viking"))
                                (13 . ("Greger" "Grels" "Greta"))
                                (14 . ("Matilda" "Maud" "Moa"))
                                (15 . ("Kristofer" "Christel" "Christer"))
                                (16 . ("Herbert" "Herta" "Hervor"))
                                (17 . ("Gertrud" "Gertie" "Gölin"))
                                (18 . ("Edvard" "Eda" "Eddie"))
                                (19 . ("Josef" "James" "Janet"))
                                (20 . ("Joakim" "Jockum" "Kim"))
                                (21 . ("Bengt" "Bengta" "Benita"))
                                (22 . ("Viktor" "Vimar" "Våge"))
                                (23 . ("Gerda" "Anngerd" "Gerd"))
                                (24 . ("Gabriel" "Gabrielle" "Gunni"))
                                (25 . ())
                                (26 . ("Emanuel" "Emanuella" "Immanuel"))
                                (27 . ("Rudolf" "Rode" "Rudi"))
                                (28 . ("Malkolm" "Elma" "Elmer"))
                                (29 . ("Jonas" "Jon" "Jonna"))
                                (30 . ("Holger" "Olga"))
                                (31 . ("Ester" "Estrid" "Vasti"))
                                ))
                          (4 . (
                                (1 . ("Harald" "Hadar" "Hardy"))
                                (2 . ("Gudmund" "Gudmar" "Gunder"))
                                (3 . ("Ferdinand" "Gunvi" "Gunvor"))
                                (4 . ("Ambrosius" "Irene" "Irina"))
                                (5 . ("Nanna" "Nancy" "Nanny"))
                                (6 . ("Vilhelm" "William" "Willy"))
                                (7 . ("Ingemund" "Ingemo" "Irma"))
                                (8 . ("Hemming" "Heimir" "Helmut"))
                                (9 . ("Otto" "Orvar" "Ottar"))
                                (10 . ("Ingvar" "Ingvald" "Ingvor"))
                                (11 . ("Ulf" "Ylva" "Yrsa"))
                                (12 . ("Julius" "Gillis"))
                                (13 . ("Artur" "Aldor" "Atle"))
                                (14 . ("Tiburtius" "Ellen" "Elly"))
                                (15 . ("Olivia" "Oliver" "Ove"))
                                (16 . ("Patrik" "Patricia" "Percy"))
                                (17 . ("Elias" "Elis" "Elise"))
                                (18 . ("Valdemar" "Valdis" "Volmar"))
                                (19 . ("Olavus Petri" "Olaus" "Olavi"))
                                (20 . ("Amalia" "Amelie" "Amy"))
                                (21 . ("Anselm" "Annevi" "Annvor"))
                                (22 . ("Albertina" "Alida" "Allan"))
                                (23 . ("Georg" "Georgina" "Jörgen"))
                                (24 . ("Vega" "Viggo" "Viveka"))
                                (25 . ("Markus" "Marika" "Mark"))
                                (26 . ("Teresia" "Terese" "Tessy"))
                                (27 . ("Engelbrekt" "Engelbert" "Enok"))
                                (28 . ("Ture" "Turid" "Tuve"))
                                (29 . ("Tyko" "Toralf" "Torulf"))
                                (30 . ("Mariana" "Marianne" "Marina"))
                                ))
                          (5 . (
                                (1 . ("Valborg" "Maj" "Maja"))
                                (2 . ("Filip" "Åsa" "Åse"))
                                (3 . ("Göta" "Görel" "Götmar"))
                                (4 . ("Monika" "Majne" "Mona"))
                                (5 . ("Gotthard" "Gotthild" "Gotty"))
                                (6 . ("Sigmund" "Sigge"))
                                (7 . ("Gustava" "Gullvi" "Gullbritt"))
                                (8 . ("Åke" "Åge" "Ågot"))
                                (9 . ("Jonatan" "John" "Johnny"))
                                (10 . ("Esbjörn" "Elvy" "Essy"))
                                (11 . ("Märta" "Meta" "Märit"))
                                (12 . ("Charlotta" "Charlotte" "Lotta"))
                                (13 . ("Linnea" "Linn" "Lis"))
                                (14 . ("Halvard" "Halvor" "Halvar"))
                                (15 . ("Sofia" "Sia" "Sofie"))
                                (16 . ("Hilma" "Helvi" "Hilmer"))
                                (17 . ("Rebecka" "Renee" "Rosita"))
                                (18 . ("Erik" "Erk" "Jerker"))
                                (19 . ("Alrik" "Alda" "Altea"))
                                (20 . ("Karolina" "Carola" "Caroline"))
                                (21 . ("Konstantin" "Conny" "Konstantia"))
                                (22 . ("Henning" "Henny" "Pål"))
                                (23 . ("Desideria" "Dennis" "Desiree"))
                                (24 . ("Ragnvald" "Ragnvi" "Ragnvor"))
                                (25 . ("Urban" "Una" "Uno"))
                                (26 . ("Vilhelmina" "Vilma" "Vilmar"))
                                (27 . ("Blenda" "Beda" "Britten"))
                                (28 . ("Ingeborg" "Ingabritt" "Ingbritt"))
                                (29 . ("Baltsar" "Bill" "Billy"))
                                (30 . ("Fritjof" "Majny" "Majvi"))
                                (31 . ("Isabella" "Iris" "Isa"))
                                ))
                          (6 . (
                                (1 . ("Nikodemus" "Nina" "Ninni"))
                                (2 . ("Rutger" "Runa" "Rune"))
                                (3 . ("Ingemar" "Ingar" "Ingmarie"))
                                (4 . ("Holmfrid" "Helfrid" "Helfrida"))
                                (5 . ("Bo" "Bodil" "Boel"))
                                (6 . ("Gustav" "Gusten" "Gösta"))
                                (7 . ("Robert" "Robin" "Ruben"))
                                (8 . ("Salomon" "Sally"))
                                (9 . ("Börje" "Belinda" "Björg"))
                                (10 . ("Svante" "Sante" "Sjunne"))
                                (11 . ("Bertil" "Berit" "Berthold"))
                                (12 . ("Eskil" "Eje" "Evan"))
                                (13 . ("Aina" "Aino" "Roine"))
                                (14 . ("Håkan" "Hakon" "Hakvin"))
                                (15 . ("Justina" "Jim" "Jimmy"))
                                (16 . ("Axel" "Axelia" "Axelina"))
                                (17 . ("Torborg" "Torhild" "Toril"))
                                (18 . ("Björn" "Bjarne" "Björne"))
                                (19 . ("Germund" "Jerry" "Jill"))
                                (20 . ("Flora" "Florence" "Florentin"))
                                (21 . ("Alf" "Alvin" "Alvina"))
                                (22 . ("Paulina" "Pamela" "Paulette"))
                                (23 . ("Adolf" "Adolfina" "Ally"))
                                (24 . ())
                                (25 . ("David" "Davida" "Daisy"))
                                (26 . ("Rakel" "Rafael" "Ralf"))
                                (27 . ("Selma" "Selim" "Selmer"))
                                (28 . ("Leo" "Lola" "Liselott"))
                                (29 . ("Petrus" "Peter" "Petter"))
                                (30 . ("Elof" "Elvira" "Viran"))
                                ))
                          (7 . (
                                (1 . ("Aron" "Arent" "Arild"))
                                (2 . ("Rosa" "Rose" "Rosmarie"))
                                (3 . ("Aurora" "Andre" "Aurelia"))
                                (4 . ("Ulrika" "Ellika" "Ulla"))
                                (5 . ("Melker" "Marja" "Mirjam"))
                                (6 . ("Esaias" "Elisiv" "Esse"))
                                (7 . ("Klas" "Claudia" "Klaus"))
                                (8 . ("Kjell" "Katja" "Kettil"))
                                (9 . ("Götilda" "Göran" "Jörn"))
                                (10 . ("Anund" "Anita" "Ante"))
                                (11 . ("Eleonora" "Eleonor" "Ellinor"))
                                (12 . ("Herman" "Hanne" "Hermine"))
                                (13 . ("Joel" "Joar" "Jorunn"))
                                (14 . ("Folke" "Fale" "Fylgia"))
                                (15 . ("Ragnhild" "Ragni" "Runo"))
                                (16 . ("Reinhold" "Reine" "Reino"))
                                (17 . ("Alexis" "Alex" "Alice"))
                                (18 . ("Fredrik" "Fred" "Freddy"))
                                (19 . ("Sara" "Charles" "Saga"))
                                (20 . ("Margareta" "Margit" "Margret"))
                                (21 . ("Johanna" "Jean" "Jeanette"))
                                (22 . ("Magdalena" "Magda" "Madeleine"))
                                (23 . ("Emma" "Elena" "Emmy"))
                                (24 . ("Kristina" "Kerstin" "Kristin"))
                                (25 . ("Jakob" "Jack"))
                                (26 . ("Jesper" "Jessika" "Jesse"))
                                (27 . ("Marta" "Marit" "Marita"))
                                (28 . ("Botvid" "Reidar" "Reidun"))
                                (29 . ("Olof" "Ola" "Olle"))
                                (30 . ("Algot" "Margot" "Vilgot"))
                                (31 . ("Elin" "Elon" "Elina"))
                                ))
                          (8 . (
                                (1 . ("Per" "Peder" "Pernilla"))
                                (2 . ("Karin" "Karen" "Kåre"))
                                (3 . ("Tage" "Tanja" "Truls"))
                                (4 . ("Arne" "Arna" "Arnevi"))
                                (5 . ("Ulrik" "Unn" "Unni"))
                                (6 . ("Sixten" "Säve" "Sölve"))
                                (7 . ("Arnold" "Annika" "Annmari"))
                                (8 . ("Sylvia" "Silja" "Silvia"))
                                (9 . ("Roland" "Ronald" "Ronny"))
                                (10 . ("Lars" "Lasse" "Lorentz"))
                                (11 . ("Susanna" "Sanna" "Susanne"))
                                (12 . ("Klara" "Clarence" "Clary"))
                                (13 . ("Hillevi" "Hilja" "Ilja"))
                                (14 . ("Ebbe" "Eberhard" "Efraim"))
                                (15 . ("Stella" "Estelle" "Stefan"))
                                (16 . ("Brynolf" "Benjamin" "Benny"))
                                (17 . ("Verner" "Verna" "Veronika"))
                                (18 . ("Helena" "Helen" "Helny"))
                                (19 . ("Magnus" "Mogens" "Måns"))
                                (20 . ("Bernhard" "Berna" "Bernt"))
                                (21 . ("Josefina" "Josefin"))
                                (22 . ("Henrietta" "Harriet" "Harry"))
                                (23 . ("Signe" "Signar" "Signy"))
                                (24 . ("Bartolomeus" "Carita" "Rita"))
                                (25 . ("Lovisa" "Louis" "Louise"))
                                (26 . ("Östen" "Ejvind" "Öjvind"))
                                (27 . ("Rolf" "Raoul" "Rasmus"))
                                (28 . ("Augustin" "Gusti" "Gurli"))
                                (29 . ("Hans" "Hampus" "Hasse"))
                                (30 . ("Albert" "Albrekt" "Aste"))
                                (31 . ("Arvid" "Arvida" "Vidar"))
                                ))
                          (9 . (
                                (1 . ("Samuel" "Sam" "Solveig"))
                                (2 . ("Justus" "Jane" "Judit"))
                                (3 . ("Alfhild" "Alfons" "Arja"))
                                (4 . ("Moses" "Molly" "My"))
                                (5 . ("Adela" "Adele" "Adin"))
                                (6 . ("Sakarias" "Siv" "Sivert"))
                                (7 . ("Regina" "Gilbert" "Gisela"))
                                (8 . ("Alma" "Adils" "Almar"))
                                (9 . ("Augusta" "Gunda" "Gunde"))
                                (10 . ("Tord" "Tordis" "Torgil"))
                                (11 . ("Dagny" "Dag" "Daga"))
                                (12 . ("Tyra" "Tyr"))
                                (13 . ("Ambjörn" "Stig" "Styrbjörn"))
                                (14 . ("Ida" "Idar" "Vida"))
                                (15 . ("Sigrid" "Siri" "Solbritt"))
                                (16 . ("Eufemia" "Cornelia" "Cornelius"))
                                (17 . ("Hildegard" "Hilbert" "Hildebrand"))
                                (18 . ("Alvar" "Alve" "Alva"))
                                (19 . ("Fredrika" "Frej" "Freja"))
                                (20 . ("Agda" "Jan" "Jannika"))
                                (21 . ("Matteus" "Majbritt" "Majlis"))
                                (22 . ("Maurits" "Marlene" "Moritz"))
                                (23 . ("Tekla" "Trond" "Tryggve"))
                                (24 . ("Gerhard" "Gert" "Glenn"))
                                (25 . ("Signild" "Sanfrid" "Signhild"))
                                (26 . ("Enar" "Einar" "Eja"))
                                (27 . ("Dagmar" "Donald" "Douglas"))
                                (28 . ("Lennart" "Lena" "Leonard"))
                                (29 . ("Mikael" "Majken" "Mikaela"))
                                (30 . ("Helge" "Heidi" "Härje"))
                                ))
                          (10 . (
                                 (1 . ("Ragnar" "Ragna" "Ragne"))
                                 (2 . ("Ludvig" "Levi" "Liv"))
                                 (3 . ("Evald" "Eila" "Eilert"))
                                 (4 . ("Frans" "Franciska" "Frank"))
                                 (5 . ("Bror" "Brage" "Bruno"))
                                 (6 . ("Jenny" "Jennifer" "Jens"))
                                 (7 . ("Birgitta" "Birgit" "Britt"))
                                 (8 . ("Nils" "Nilla" "Nelly"))
                                 (9 . ("Ingrid" "Inger" "Ingolf"))
                                 (10 . ("Helmer" "Helmina" "Helmy"))
                                 (11 . ("Erling" "Elvin" "Elvina"))
                                 (12 . ("Valfrid" "Ina" "Inez"))
                                 (13 . ("Teofil" "Terje" "Tjelvar"))
                                 (14 . ("Manfred" "Mandor" "Manne"))
                                 (15 . ("Hedvig" "Hartvig" "Hedda"))
                                 (16 . ("Fingal" "Finn" "Flemming"))
                                 (17 . ("Antoinetta" "Annette" "Tony"))
                                 (18 . ("Lukas" "Lillemor" "Lilly"))
                                 (19 . ("Tore" "Bojan" "Borghild"))
                                 (20 . ("Sibylla" "Camilla" "Kasper"))
                                 (21 . ("Birger" "Brita" "Britta"))
                                 (22 . ("Seved" "Sigvid" "Ursula"))
                                 (23 . ("Sören" "Severin"))
                                 (24 . ("Evert" "Eivor" "Elving"))
                                 (25 . ("Inga" "Ingalill" "Ingert"))
                                 (26 . ("Amanda" "Manda" "Mandy"))
                                 (27 . ("Sabina" "Sebastian" "Sussy"))
                                 (28 . ("Simon" "Simeon" "Simone"))
                                 (29 . ("Viola" "Vivi" "Vivianne"))
                                 (30 . ("Elsa" "Elsie" "Ilse"))
                                 (31 . ("Edit" "Edgar" "Edor"))
                                 ))
                          (11 . (
                                 (1 . ())
                                 (2 . ("Tobias" "Tova" "Tove"))
                                 (3 . ("Hubert" "Raymond" "Roy"))
                                 (4 . ("Sverker" "Nore" "Nora"))
                                 (5 . ("Eugen" "Ebert" "Egil"))
                                 (6 . ("Gustav Adolf" "Gull" "Gulli"))
                                 (7 . ("Ingegerd" "Ingel" "Ingela"))
                                 (8 . ("Vendela" "Vanda" "Ville"))
                                 (9 . ("Teodor" "Tea" "Ted"))
                                 (10 . ("Martin Luther" "Mait" "Martin"))
                                 (11 . ("Mårten" "Marion" "Morgan"))
                                 (12 . ("Konrad" "Kuno" "Kurt"))
                                 (13 . ("Kristian" "Karsten" "Kersti"))
                                 (14 . ("Emil" "Milly" "Mimmi"))
                                 (15 . ("Leopold" "Leif" "Lilian"))
                                 (16 . ("Edmund" "Elida" "Elisa"))
                                 (17 . ("Napoleon" "Naemi" "Naima"))
                                 (18 . ("Magnhild" "Magna" "Magne"))
                                 (19 . ("Elisabet" "Lisa" "Lisbet"))
                                 (20 . ("Pontus" "Polly" "Povel"))
                                 (21 . ("Helga" "Helle" "Hilde"))
                                 (22 . ("Cecilia" "Cilla" "Cissi"))
                                 (23 . ("Klemens" "Ketty" "Kitty"))
                                 (24 . ("Gudrun" "Gullan" "Gullvor"))
                                 (25 . ("Katarina" "Carina" "Katrin"))
                                 (26 . ("Torkel" "Torleif"))
                                 (27 . ("Astrid" "Asta" "Astri"))
                                 (28 . ("Malte" "Malvina" "Mia"))
                                 (29 . ("Sune" "Sonja" "Synnöve"))
                                 (30 . ("Anders" "Andrea" "Andreas"))
                                 ))
                          (12 . (
                                 (1 . ("Oskar" "Ole" "Ossian"))
                                 (2 . ("Beata" "Beatrice" "Betty"))
                                 (3 . ("Lydia" "Linda" "Love"))
                                 (4 . ("Barbro" "Barbara" "Boris"))
                                 (5 . ("Sven" "Svend" "Svenning"))
                                 (6 . ("Nikolaus" "Niklas" "Nikolina"))
                                 (7 . ("Agaton" "Angela" "Angelika"))
                                 (8 . ("Virginia" "Vera" "Vesta"))
                                 (9 . ("Anna" "Ann" "Annie"))
                                 (10 . ("Malin" "Majvor" "Malena"))
                                 (11 . ("Daniel" "Dan" "Daniela"))
                                 (12 . ("Alexander" "Pia" "Pierre"))
                                 (13 . ("Lucia" "Lisen" "Lisette"))
                                 (14 . ("Sten" "Stina" "Sture"))
                                 (15 . ("Gottfrid" "Kaj" "Kajsa"))
                                 (16 . ("Assar" "Odd" "Osvald"))
                                 (17 . ("Inge" "Ilona" "Irmeli"))
                                 (18 . ("Abraham" "Abdon" "Gideon"))
                                 (19 . ("Isak" "Isidor" "Isidora"))
                                 (20 . ("Israel" "Gina" "Gitte"))
                                 (21 . ("Tomas" "Tom" "Tommy"))
                                 (22 . ("Natanael" "Natalia" "Natan"))
                                 (23 . ("Adam" "Ada" "Adina"))
                                 (24 . ("Eva" "Evita" "Evy"))
                                 (25 . ())
                                 (26 . ("Stefan" "Staffan" "Stefanie"))
                                 (27 . ("Johannes" "Hannes" "Johan"))
                                 (28 . ())
                                 (29 . ("Abel" "Abbe"))
                                 (30 . ("Set" "Viva" "Vivan"))
                                 (31 . ("Sylvester" "Sylve" "Sylvi"))
                                 ))))

(lyskom-define-namedays 'sv-1993
                        "Sverige, 1993 års namnlängd"
                        "\
Namnsdagar enligt namnlängd från 1993, med uppenbara helgdagar borttagna."
                        '((1 . (
                                (1 . ())
                                (2 . ("Svea" "Sverker"))
                                (3 . ("Alfred" "Alfrida"))
                                (4 . ("Rut" "Ritva"))
                                (5 . ("Hanna" "Hannele"))
                                (6 . ("Baltsar" "Kasper"))
                                (7 . ("August" "Augusta"))
                                (8 . ("Erland" "Erhard"))
                                (9 . ("Gunnar" "Gunder"))
                                (10 . ("Sigurd" "Sigmund"))
                                (11 . ("Hugo" "Hagar"))
                                (12 . ("Frideborg" "Fridolf"))
                                (13 . ("Knut"))
                                (14 . ("Felix" "Felicia"))
                                (15 . ("Laura" "Liv"))
                                (16 . ("Hjalmar" "Hervor"))
                                (17 . ("Anton" "Tony"))
                                (18 . ("Hilda" "Hildur"))
                                (19 . ("Henrik" "Henry"))
                                (20 . ("Fabian" "Sebastian"))
                                (21 . ("Agnes" "Agneta"))
                                (22 . ("Vincent" "Veine"))
                                (23 . ("Emilia" "Emilie"))
                                (24 . ("Erika" "Eira"))
                                (25 . ("Paul" "Pål"))
                                (26 . ("Bodil" "Boel"))
                                (27 . ("Göte" "Göta"))
                                (28 . ("Karl" "Karla"))
                                (29 . ("Valter" "Vilma"))
                                (30 . ("Gunhild" "Gunilla"))
                                (31 . ("Ivar" "Joar"))
                                ))
                          (2 . (
                                (1 . ("Max" "Magda"))
                                (2 . ("Marja" "Mia"))
                                (3 . ("Disa" "Hjördis"))
                                (4 . ("Ansgar" "Anselm"))
                                (5 . ("Lisa" "Elise"))
                                (6 . ("Dorotea" "Dora"))
                                (7 . ("Rikard" "Dick"))
                                (8 . ("Berta" "Berthold"))
                                (9 . ("Fanny" "Betty"))
                                (10 . ("Egon" "Egil"))
                                (11 . ("Yngve" "Ingolf"))
                                (12 . ("Evelina" "Evy"))
                                (13 . ("Agne" "Agnar"))
                                (14 . ("Valentin" "Tina"))
                                (15 . ("Sigfrid" "Sigbritt"))
                                (16 . ("Julia" "Jill"))
                                (17 . ("Alexandra" "Sandra"))
                                (18 . ("Frida" "Fritz"))
                                (19 . ("Gabriella" "Ella"))
                                (20 . ("Rasmus" "Ruben"))
                                (21 . ("Hilding" "Hulda"))
                                (22 . ("Marina" "Marlene"))
                                (23 . ("Torsten" "Torun"))
                                (24 . ("Mattias" "Mats"))
                                (25 . ("Sigvard" "Sivert"))
                                (26 . ("Torgny" "Torkel"))
                                (27 . ("Lage" "Laila"))
                                (28 . ("Maria" "Maja"))
                                ))
                          (3 . (
                                (1 . ("Albin" "Inez"))
                                (2 . ("Ernst" "Erna"))
                                (3 . ("Gunborg" "Gunvor"))
                                (4 . ("Adrian" "Ada"))
                                (5 . ("Tora" "Tor"))
                                (6 . ("Ebba" "Ebbe"))
                                (7 . ("Isidor" "Doris"))
                                (8 . ("Siv" "Saga"))
                                (9 . ("Torbjörn" "Ambjörn"))
                                (10 . ("Edla" "Ethel"))
                                (11 . ("Edvin" "Elon"))
                                (12 . ("Viktoria" "Viktor"))
                                (13 . ("Greger" "Iris"))
                                (14 . ("Matilda" "Maud"))
                                (15 . ("Kristofer" "Christel"))
                                (16 . ("Herbert" "Gilbert"))
                                (17 . ("Gertrud" "Görel"))
                                (18 . ("Edvard" "Eddie"))
                                (19 . ("Josef" "Josefina"))
                                (20 . ("Joakim" "Kim"))
                                (21 . ("Bengt" "Benny"))
                                (22 . ("Viking" "Vilgot"))
                                (23 . ("Gerda" "Gerd"))
                                (24 . ("Gabriel" "Rafael"))
                                (25 . ("Mary" "Marion"))
                                (26 . ("Emanuel" "Manne"))
                                (27 . ("Ralf" "Raymond"))
                                (28 . ("Elma" "Elmer"))
                                (29 . ("Jonas" "Jens"))
                                (30 . ("Holger" "Reidar"))
                                (31 . ("Ester" "Estrid"))
                                ))
                          (4 . (
                                (1 . ("Harald" "Halvar"))
                                (2 . ("Gunnel" "Gun"))
                                (3 . ("Ferdinand" "Florence"))
                                (4 . ("Irene" "Irja"))
                                (5 . ("Nanna" "Nanny"))
                                (6 . ("Vilhelm" "Willy"))
                                (7 . ("Irma" "Mimmi"))
                                (8 . ("Vanja" "Ronja"))
                                (9 . ("Otto" "Ottilia"))
                                (10 . ("Ingvar" "Ingvor"))
                                (11 . ("Ulf" "Ylva"))
                                (12 . ("Julius" "Gillis"))
                                (13 . ("Artur" "Douglas"))
                                (14 . ("Tiburtius" "Tim"))
                                (15 . ("Olivia" "Oliver"))
                                (16 . ("Patrik" "Patricia"))
                                (17 . ("Elias" "Elis"))
                                (18 . ("Valdemar" "Volmar"))
                                (19 . ("Olaus" "Ola"))
                                (20 . ("Amalia" "Amelie"))
                                (21 . ("Annika" "Anneli"))
                                (22 . ("Allan" "Alida"))
                                (23 . ("Georg" "Göran"))
                                (24 . ("Vega" "Viveka"))
                                (25 . ("Markus" "Mark"))
                                (26 . ("Teresia" "Terese"))
                                (27 . ("Engelbrekt" "Enok"))
                                (28 . ("Ture" "Tyko"))
                                (29 . ("Kennet" "Kent"))
                                (30 . ("Mariana" "Marianne"))
                                ))
                          (5 . (
                                (1 . ("Valborg" "Maj"))
                                (2 . ("Filip" "Filippa"))
                                (3 . ("John" "Jack"))
                                (4 . ("Monika" "Mona"))
                                (5 . ("Vivianne" "Vivan"))
                                (6 . ("Marit" "Rita"))
                                (7 . ("Lilian" "Lilly"))
                                (8 . ("Åke" "Ove"))
                                (9 . ("Jonatan" "Gideon"))
                                (10 . ("Elvira" "Elvy"))
                                (11 . ("Märta" "Märit"))
                                (12 . ("Charlotta" "Lotta"))
                                (13 . ("Linnea" "Nina"))
                                (14 . ("Lillemor" "Lill"))
                                (15 . ("Sofia" "Sonja"))
                                (16 . ("Hilma" "Hilmer"))
                                (17 . ("Nore" "Nora"))
                                (18 . ("Erik" "Jerker"))
                                (19 . ("Majken" "Majvor"))
                                (20 . ("Karolina" "Lina"))
                                (21 . ("Konstantin" "Conny"))
                                (22 . ("Henning" "Hemming"))
                                (23 . ("Desiree" "Renee"))
                                (24 . ("Ivan" "Yvonne"))
                                (25 . ("Urban" "Ursula"))
                                (26 . ("Vilhelmina" "Helmy"))
                                (27 . ("Blenda" "Beda"))
                                (28 . ("Ingeborg" "Borghild"))
                                (29 . ("Jean" "Jeanette"))
                                (30 . ("Fritiof" "Frej"))
                                (31 . ("Isabella" "Isa"))
                                ))
                          (6 . (
                                (1 . ("Rune" "Runa"))
                                (2 . ("Rutger" "Roger"))
                                (3 . ("Ingemar" "Gudmar"))
                                (4 . ("Solveig" "Solbritt"))
                                (5 . ("Bo" "Boris"))
                                (6 . ("Gustav" "Gösta"))
                                (7 . ("Robert" "Robin"))
                                (8 . ("Eivor" "Elaine"))
                                (9 . ("Petra" "Petronella"))
                                (10 . ("Kerstin" "Karsten"))
                                (11 . ("Bertil" "Berit"))
                                (12 . ("Eskil" "Esbjörn"))
                                (13 . ("Aina" "Eila"))
                                (14 . ("Håkan" "Heidi"))
                                (15 . ("Margit" "Mait"))
                                (16 . ("Axel" "Axelina"))
                                (17 . ("Torborg" "Torvald"))
                                (18 . ("Björn" "Bjarne"))
                                (19 . ("Germund" "Jerry"))
                                (20 . ("Linda" "Linn"))
                                (21 . ("Alf" "Alva"))
                                (22 . ("Paulina" "Paula"))
                                (23 . ("Adolf" "Adela"))
                                (24 . ("Johan" "Jan"))
                                (25 . ("David" "Salomon"))
                                (26 . ("Gunni" "Jim"))
                                (27 . ("Selma" "Herta"))
                                (28 . ("Leo" "Leopold"))
                                (29 . ("Petrus" "Peter"))
                                (30 . ("Elof" "Leif"))
                                ))
                          (7 . (
                                (1 . ("Aron" "Mirjam"))
                                (2 . ("Rosa" "Rosita"))
                                (3 . ("Aurora" "Adina"))
                                (4 . ("Ulrika" "Ulla"))
                                (5 . ("Melker" "Agaton"))
                                (6 . ("Ronald" "Ronny"))
                                (7 . ("Klas" "Kaj"))
                                (8 . ("Kjell" "Tjelvar"))
                                (9 . ("Jörgen" "Örjan"))
                                (10 . ("Anund" "Gunda"))
                                (11 . ("Eleonora" "Ellinor"))
                                (12 . ("Herman" "Hermine"))
                                (13 . ("Joel" "Judit"))
                                (14 . ("Folke" "Odd"))
                                (15 . ("Ragnhild" "Ragnvald"))
                                (16 . ("Reinhold" "Reine"))
                                (17 . ("Alexis" "Alice"))
                                (18 . ("Fredrik" "Fred"))
                                (19 . ("Sara" "Sally"))
                                (20 . ("Margareta" "Greta"))
                                (21 . ("Johanna" "Jane"))
                                (22 . ("Magdalena" "Madeleine"))
                                (23 . ("Emma" "Emmy"))
                                (24 . ("Kristina" "Stina"))
                                (25 . ("Jakob" "James"))
                                (26 . ("Jesper" "Jessika"))
                                (27 . ("Marta" "Moa"))
                                (28 . ("Botvid" "Seved"))
                                (29 . ("Olof" "Olle"))
                                (30 . ("Algot" "Margot"))
                                (31 . ("Elin" "Elna"))
                                ))
                          (8 . (
                                (1 . ("Per" "Pernilla"))
                                (2 . ("Karin" "Kajsa"))
                                (3 . ("Tage" "Tanja"))
                                (4 . ("Arne" "Arnold"))
                                (5 . ("Ulrik" "Alrik"))
                                (6 . ("Sixten" "Sölve"))
                                (7 . ("Dennis" "Donald"))
                                (8 . ("Silvia" "Sylvia"))
                                (9 . ("Roland" "Roine"))
                                (10 . ("Lars" "Lorentz"))
                                (11 . ("Susanna" "Sanna"))
                                (12 . ("Klara" "Clary"))
                                (13 . ("Hillevi" "Gullvi"))
                                (14 . ("William" "Bill"))
                                (15 . ("Stella" "Stellan"))
                                (16 . ("Brynolf" "Sigyn"))
                                (17 . ("Verner" "Veronika"))
                                (18 . ("Helena" "Lena"))
                                (19 . ("Magnus" "Måns"))
                                (20 . ("Bernhard" "Bernt"))
                                (21 . ("Jon" "Jonna"))
                                (22 . ("Henrietta" "Henny"))
                                (23 . ("Signe" "Signhild"))
                                (24 . ("Bartolomeus" "Bert"))
                                (25 . ("Lovisa" "Louise"))
                                (26 . ("Östen" "Ejvind"))
                                (27 . ("Rolf" "Rudolf"))
                                (28 . ("Gurli" "Gull"))
                                (29 . ("Hans" "Hampus"))
                                (30 . ("Albert" "Albertina"))
                                (31 . ("Arvid" "Vidar"))
                                ))
                          (9 . (
                                (1 . ("Samuel" "Sam"))
                                (2 . ("Justus" "Justina"))
                                (3 . ("Alfhild" "Alfons"))
                                (4 . ("Gisela" "Glenn"))
                                (5 . ("Harry" "Harriet"))
                                (6 . ("Sakarias" "Esaias"))
                                (7 . ("Regina" "Roy"))
                                (8 . ("Alma" "Ally"))
                                (9 . ("Anita" "Anja"))
                                (10 . ("Tord" "Tove"))
                                (11 . ("Dagny" "Daniela"))
                                (12 . ("Tyra" "Åsa"))
                                (13 . ("Sture" "Styrbjörn"))
                                (14 . ("Ida" "Ellida"))
                                (15 . ("Sigrid" "Siri"))
                                (16 . ("Dag" "Daga"))
                                (17 . ("Hildegard" "Magnhild"))
                                (18 . ("Alvar" "Orvar"))
                                (19 . ("Fredrika" "Carita"))
                                (20 . ("Agda" "Agata"))
                                (21 . ("Ellen" "Elly"))
                                (22 . ("Maurits" "Morgan"))
                                (23 . ("Tekla" "Tea"))
                                (24 . ("Gerhard" "Gert"))
                                (25 . ("Kåre" "Tryggve"))
                                (26 . ("Einar" "Enar"))
                                (27 . ("Dagmar" "Rigmor"))
                                (28 . ("Lennart" "Leonard"))
                                (29 . ("Mikael" "Mikaela"))
                                (30 . ("Helge" "Helny"))
                                ))
                          (10 . (
                                 (1 . ("Ragnar" "Ragna"))
                                 (2 . ("Ludvig" "Louis"))
                                 (3 . ("Evald" "Osvald"))
                                 (4 . ("Frans" "Frank"))
                                 (5 . ("Bror" "Bruno"))
                                 (6 . ("Jenny" "Jennifer"))
                                 (7 . ("Birgitta" "Britta"))
                                 (8 . ("Nils" "Nelly"))
                                 (9 . ("Ingrid" "Inger"))
                                 (10 . ("Helmer" "Hadar"))
                                 (11 . ("Erling" "Jarl"))
                                 (12 . ("Valfrid" "Ernfrid"))
                                 (13 . ("Birgit" "Britt"))
                                 (14 . ("Manfred" "Helfrid"))
                                 (15 . ("Hedvig" "Hedda"))
                                 (16 . ("Fingal" "Finn"))
                                 (17 . ("Antonia" "Annette"))
                                 (18 . ("Lukas" "Matteus"))
                                 (19 . ("Tore" "Torleif"))
                                 (20 . ("Sibylla" "Camilla"))
                                 (21 . ("Birger" "Börje"))
                                 (22 . ("Marika" "Marita"))
                                 (23 . ("Sören" "Severin"))
                                 (24 . ("Evert" "Eilert"))
                                 (25 . ("Inga" "Ingvald"))
                                 (26 . ("Amanda" "My"))
                                 (27 . ("Sabina" "Ina"))
                                 (28 . ("Simon" "Simone"))
                                 (29 . ("Viola" "Vivi"))
                                 (30 . ("Elsa" "Elsie"))
                                 (31 . ("Edit" "Edgar"))
                                 ))
                          (11 . (
                                 (1 . ("Andre" "Andrea"))
                                 (2 . ("Tobias" "Toini"))
                                 (3 . ("Hubert" "Diana"))
                                 (4 . ("Uno" "Unn"))
                                 (5 . ("Eugen" "Eugenia"))
                                 (6 . ("Gustav Adolf"))
                                 (7 . ("Ingegerd" "Ingela"))
                                 (8 . ("Vendela" "Vanda"))
                                 (9 . ("Teodor" "Ted"))
                                 (10 . ("Martin" "Martina"))
                                 (11 . ("Mårten"))
                                 (12 . ("Konrad" "Kurt"))
                                 (13 . ("Kristian" "Krister"))
                                 (14 . ("Emil" "Mildred"))
                                 (15 . ("Katja" "Nadja"))
                                 (16 . ("Edmund" "Gudmund"))
                                 (17 . ("Naemi" "Nancy"))
                                 (18 . ("Pierre" "Percy"))
                                 (19 . ("Elisabet" "Lisbeth"))
                                 (20 . ("Pontus" "Pia"))
                                 (21 . ("Helga" "Olga"))
                                 (22 . ("Cecilia" "Cornelia"))
                                 (23 . ("Klemens" "Clarence"))
                                 (24 . ("Gudrun" "Runar"))
                                 (25 . ("Katarina" "Carina"))
                                 (26 . ("Linus" "Love"))
                                 (27 . ("Astrid" "Asta"))
                                 (28 . ("Malte" "Malkolm"))
                                 (29 . ("Sune" "Synnöve"))
                                 (30 . ("Anders" "Andreas"))
                                 ))
                          (12 . (
                                 (1 . ("Oskar" "Ossian"))
                                 (2 . ("Beata" "Beatrice"))
                                 (3 . ("Lydia" "Carola"))
                                 (4 . ("Barbro" "Barbara"))
                                 (5 . ("Sven" "Svante"))
                                 (6 . ("Nikolaus" "Niklas"))
                                 (7 . ("Angelika" "Angela"))
                                 (8 . ("Virginia" "Vera"))
                                 (9 . ("Anna" "Annie"))
                                 (10 . ("Malin" "Malena"))
                                 (11 . ("Daniel" "Dan"))
                                 (12 . ("Alexander" "Alex"))
                                 (13 . ("Lucia"))
                                 (14 . ("Sten" "Stig"))
                                 (15 . ("Gottfrid" "Gotthard"))
                                 (16 . ("Assar" "Astor"))
                                 (17 . ("Inge" "Ingemund"))
                                 (18 . ("Abraham" "Efraim"))
                                 (19 . ("Isak" "Rebecka"))
                                 (20 . ("Israel" "Moses"))
                                 (21 . ("Tomas" "Tom"))
                                 (22 . ("Natanael" "Natalia"))
                                 (23 . ("Adam"))
                                 (24 . ("Eva"))
                                 (25 . ())
                                 (26 . ("Stefan" "Staffan"))
                                 (27 . ("Johannes" "Hannes"))
                                 (28 . ("Rakel" "Benjamin"))
                                 (29 . ("Abel" "Set"))
                                 (30 . ("Gunlög" "Åslög"))
                                 (31 . ("Sylvester"))
                                 ))))



(lyskom-define-namedays 'fi
                        "Finska"
                        "\
Namnsdagar ur någon finsk namnlängd. Eftersom utvecklarna inte
kan finska är beskrivningen på svenska, med förhoppningen att
någon som kan finska kommer att reta sig på den och skriva om
texten på rätt språk."
                        '((1 . ((1 . ())
                                (2 . ("Aapeli"))
                                (3 . ("Elmeri" "Elmer" "Elmo"))
                                (4 . ("Ruut"))
                                (5 . ("Lea" "Leea"))
                                (6 . ("Harri"))
                                (7 . ("Aukusti" "Aku" "August"))
                                (8 . ("Hilppa" "Titta"))
                                (9 . ("Veikko" "Veli" "Veijo"))
                                (10 . ("Nyyrikki"))
                                (11 . ("Kari" "Karri"))
                                (12 . ("Toini"))
                                (13 . ("Nuutti"))
                                (14 . ("Sakari" "Saku"))
                                (15 . ("Solja"))
                                (16 . ("Ilmari" "Ilmo"))
                                (17 . ("Toni" "Anttoni" "Antto" "Anton"))
                                (18 . ("Laura"))
                                (19 . ("Heikki" "Henrik" "Henrikki" "Henri"))
                                (20 . ("Henna" "Henni" "Henriikka"))
                                (21 . ("Aune" "Oona" "Auni"))
                                (22 . ("Visa"))
                                (23 . ("Eine" "Eini" "Enni"))
                                (24 . ("Senja"))
                                (25 . ("Paavo" "Pauli" "Paavali" "Paul"))
                                (26 . ("Joonatan"))
                                (27 . ("Viljo"))
                                (28 . ("Kaarlo" "Kalle" "Kaarle" "Mies"))
                                (29 . ("Valtteri"))
                                (30 . ("Irja"))
                                (31 . ("Alli"))
                                ))
                          (2 . (
                                (1 . ("Riitta"))
                                (2 . ("Aamu" "Jemina"))
                                (3 . ("Valo"))
                                (4 . ("Armi" "Ronja"))
                                (5 . ("Asser"))
                                (6 . ("Terhi" "Teija" "Tiia" "Tea" "Terhikki"))
                                (7 . ("Riku" "Rikhard"))
                                (8 . ("Laina"))
                                (9 . ("Raija" "Raisa"))
                                (10 . ("Elina" "Elna" "Ella" "Ellen"))
                                (11 . ("Talvikki"))
                                (12 . ("Elma" "Elmi"))
                                (13 . ("Sulo" "Sulho"))
                                (14 . ("Voitto" "Valentin" "Tino"))
                                (15 . ("Sipi" "Sippo"))
                                (16 . ("Kai"))
                                (17 . ("Väinö" "Väonämö"))
                                (18 . ("Kaino"))
                                (19 . ("Eija"))
                                (20 . ("Heli" "Helinä" "Heljä" "Hely"))
                                (21 . ("Keijo"))
                                (22 . ("Tuulikki" "Tuuli" "Tuulia"))
                                (23 . ("Aslak"))
                                (24 . ("Matti" "Matias"))
                                (25 . ("Tuija" "Tuire"))
                                (26 . ("Nestori"))
                                (27 . ("Torsti"))
                                (28 . ("Onni"))
                                ))
                          (3 . (
                                (1 . ("Alpo" "Alvi" "Alpi"))
                                (2 . ("Virve" "Virva"))
                                (3 . ("Kauko"))
                                (4 . ("Ari" "Arsi" "Atro"))
                                (5 . ("Laila" "Leila"))
                                (6 . ("Tarmo"))
                                (7 . ("Tarja" "Taru"))
                                (8 . ("Vilppu"))
                                (9 . ("Auvo"))
                                (10 . ("Aurora" "Aura" "Auri"))
                                (11 . ("Kalervo"))
                                (12 . ("Reijo" "Reko"))
                                (13 . ("Erno" "Tarvo" "Ernesti"))
                                (14 . ("Matilda" "Tilda"))
                                (15 . ("Risto"))
                                (16 . ("Ilkka"))
                                (17 . ("Kerttu" "Kerttuli"))
                                (18 . ("Eetu" "Edvard"))
                                (19 . ("Jooseppi" "Juuso" "Joosef" "Josefiina"))
                                (20 . ("Aki" "Joakim" "Kim" "Jaakkima"))
                                (21 . ("Pentti"))
                                (22 . ("Vihtori"))
                                (23 . ("Akseli"))
                                (24 . ("Kaapo" "Gabriel" "Kaapro" "Kaappo"))
                                (25 . ("Aija"))
                                (26 . ("Manu" "Immanuel" "Manne" "Immo"))
                                (27 . ("Sauli" "Saul"))
                                (28 . ("Armas"))
                                (29 . ("Joonas" "Jouni" "Joni" "Joonas" "Jonne" "Jonni"))
                                (30 . ("Usko"))
                                (31 . ("Irma" "Irmeli"))
                                ))
                          (4 . (
                                (1 . ("Raita" "Pulmu"))
                                (2 . ("Pellervo"))
                                (3 . ("Sampo"))
                                (4 . ("Ukko"))
                                (5 . ("Irene" "Irina" "Ira" "Iro"))
                                (6 . ("Vilho" "Ville" "Viljami" "Vilhelm" "Vili" "Jami"))
                                (7 . ("Allan" "Ahvo"))
                                (8 . ("Suoma" "Suometar"))
                                (9 . ("Elias" "Eelis" "Eljas"))
                                (10 . ("Tero"))
                                (11 . ("Verna"))
                                (12 . ("Julius" "Julia" "Juliaana"))
                                (13 . ("Tellervo"))
                                (14 . ("Taito"))
                                (15 . ("Lina" "Tuomi"))
                                (16 . ("Jalo" "Patrik"))
                                (17 . ("Otto"))
                                (18 . ("Valto" "Valdemar"))
                                (19 . ("Pälvi" "Pilvi"))
                                (20 . ("Lauha"))
                                (21 . ("Anssi" "Anselmi"))
                                (22 . ("Alina"))
                                (23 . ("Yrjö" "Jyrki" "Jyri" "Yrjänä" "Jori"))
                                (24 . ("Pertti" "Albert" "Altti"))
                                (25 . ("Markku" "Markus" "Marko"))
                                (26 . ("Terttu" "Teresa"))
                                (27 . ("Merja"))
                                (28 . ("Ilpo" "Ilppo" "Tuure"))
                                (29 . ("Teijo"))
                                (30 . ("Mirja" "Mirva" "Mira" "Miia" "Mirjami" "Mirka" "Mirkka"))
                                ))
                          (5 . (
                                (1 . ("Vappu" "Valpuri"))
                                (2 . ("Vuokko" "Viivi"))
                                (3 . ("Outi"))
                                (4 . ("Ruusu" "Roosa"))
                                (5 . ("Maini"))
                                (6 . ("Ylermi"))
                                (7 . ("Helmi" "Kastehelmi"))
                                (8 . ("Heino"))
                                (9 . ("Timo"))
                                (10 . ("Aino" "Aina" "Ainikki"))
                                (11 . ("Osmo"))
                                (12 . ("Liotta"))
                                (13 . ("Kukka" "Floora"))
                                (14 . ("Tuula"))
                                (15 . ("Sofia" "Sinja" "Sohvi"))
                                (16 . ("Esteri" "Essi" "Ester"))
                                (17 . ("Maila" "Maili" "Mailis" "Rebekka"))
                                (18 . ("Erkki" "Eero" "Eerikki" "Eerika"))
                                (19 . ("Emilia" "Emma" "Emmi" "Milla" "Milja" "Milka" "Amalia"))
                                (20 . ("Lilja" "Karoliina" "Lilli"))
                                (21 . ("Kosti" "Konsta" "Konstantin"))
                                (22 . ("Hemmikki" "Hemmo"))
                                (23 . ("Lyydia" "Lyyli"))
                                (24 . ("Tuukka" "Tuoko"))
                                (25 . ("Urpo"))
                                (26 . ("Vilhelmiina" "Minna" "Vilma" "Miina" "Mimmi"))
                                (27 . ("Ritva"))
                                (28 . ("Alma"))
                                (29 . ("Oiva" "Oivi" "Olivia"))
                                (30 . ("Pasi"))
                                (31 . ("Helka" "Helga"))
                                ))
                          (6 . (
                                (1 . ("Teemu" "Nikodemus"))
                                (2 . ("Venla"))
                                (3 . ("Orvokki" "Viola"))
                                (4 . ("Toivo"))
                                (5 . ("Sulevi"))
                                (6 . ("Kustaa" "Kyösti" "Kustavi"))
                                (7 . ("Suvi" "Robert" "Roope"))
                                (8 . ("Salomo" "Salomon"))
                                (9 . ("Ensio"))
                                (10 . ("Seppo"))
                                (11 . ("Impi" "Immi"))
                                (12 . ("Esko"))
                                (13 . ("Raili" "Raila"))
                                (14 . ("Kielo"))
                                (15 . ("Vieno" "Viena"))
                                (16 . ("Päivi" "Päivikki" "Päivä"))
                                (17 . ("Urho"))
                                (18 . ("Tapio"))
                                (19 . ("Siiri"))
                                (20 . ("Into"))
                                (21 . ("Ahti" "Ahto"))
                                (22 . ("PaulaLiina" "Pauliina"))
                                (23 . ("Aatto" "Aatu" "Aadolf"))
                                (24 . ("Johannes" "Juhani" "Juha" "Juho" "Jukka" "Jussi" "Janne" "Jani" "Juhana"))
                                (25 . ("Uuno"))
                                (26 . ("Jorm" "Jarmo" "Jarkko" "Jarno" "Jere" "Jeremias"))
                                (27 . ("Elviira" "Elvi"))
                                (28 . ("Leo"))
                                (29 . ("Pietari" "Pekka" "Petri" "Petra" "Petteri" "Pekko"))
                                (30 . ("Päiviö" "Päivö"))
                                ))
                          (7 . (
                                (1 . ("Aaro" "Aaron"))
                                (2 . ("Maria" "Mari" "Maija" "Meeri" "Maaria" "Marika" "Maiju" "Maikki" "Kukka-Maaria"))
                                (3 . ("Arvo"))
                                (4 . ("Ulla" "Ulpu"))
                                (5 . ("Unto" "Untamo"))
                                (6 . ("Esa" "Esaias"))
                                (7 . ("Klaus" "Launo"))
                                (8 . ("Turo" "Turkka"))
                                (9 . ("Ilta" "Jasmin"))
                                (10 . ("Saima" "Saimi"))
                                (11 . ("Elli" "Noora" "Nelli" "Eleonoora"))
                                (12 . ("Hermanni" "Herkko" "Herman"))
                                (13 . ("Ilari" "Lari" "Joel"))
                                (14 . ("Aliisa"))
                                (15 . ("Rauni" "Rauna"))
                                (16 . ("Reino"))
                                (17 . ("Ossi" "Ossian"))
                                (18 . ("Riikka"))
                                (19 . ("Saara" "Sari" "Salli" "Salla" "Sara"))
                                (20 . ("Marketta" "Maarit" "Reeta" "Maaret" "Reetta" "Margareeta"))
                                (21 . ("Johanna" "Hanna" "Hanne" "Jenni" "Jonna" "Jenna" "Hannele" "Joanna"))
                                (22 . ("Leena" "Leeni" "Lenita" "Matleena"))
                                (23 . ("Oili" "Olga"))
                                (24 . ("Kristiina" "Kirsti" "Tiina" "Kirsi" "Kiia"))
                                (25 . ("Jaakko" "Jaakoppi" "Jaakob"))
                                (26 . ("Martta"))
                                (27 . ("Heidi"))
                                (28 . ("Atso"))
                                (29 . ("Olavi" "Olli" "Uolevi" "Uoti"))
                                (30 . ("Asta"))
                                (31 . ("Helena" "Elena"))
                                ))
                          (8 . (
                                (1 . ("Maire"))
                                (2 . ("Kimmo"))
                                (3 . ("Linnea" "Nea" "Vanamo"))
                                (4 . ("Veera"))
                                (5 . ("Salme" "Sanelma"))
                                (6 . ("Toimi" "Keimo"))
                                (7 . ("Lahja"))
                                (8 . ("Sylvi" "Sylvia" "Silva"))
                                (9 . ("Erja" "Eira"))
                                (10 . ("Lauri" "Lasse" "Lassi"))
                                (11 . ("Sanna" "Sanni" "Susanna" "Susanne"))
                                (12 . ("Klaara"))
                                (13 . ("Jesse"))
                                (14 . ("Onerva" "Kanerva"))
                                (15 . ("Marjatta" "Marja" "Jaana" "Marjo" "Marjut" "Marjaana" "Marjukka" "Marita" "Maritta" "Marianne" "Marianna"))
                                (16 . ("Aulis"))
                                (17 . ("Verneri"))
                                (18 . ("Leevi"))
                                (19 . ("Mauno" "Maunu"))
                                (20 . ("Samuli" "Sami" "Samuel" "Samu"))
                                (21 . ("Soini" "Veini"))
                                (22 . ("Iivari" "Iivo"))
                                (23 . ("Varma" "Signe"))
                                (24 . ("Perttu"))
                                (25 . ("Loviisa"))
                                (26 . ("Ilma" "Ilmi" "Ilmatar"))
                                (27 . ("Rauli"))
                                (28 . ("Tauno"))
                                (29 . ("Iines" "Iina" "Iinari"))
                                (30 . ("Eemil" "Eemeli"))
                                (31 . ("Arvi"))
                                ))
                          (9 . (
                                (1 . ("Pirkka"))
                                (2 . ("Sinikka" "Sini"))
                                (3 . ("Soili" "Soile" "Soila"))
                                (4 . ("Ansa"))
                                (5 . ("Roni" "Mainio"))
                                (6 . ("Asko"))
                                (7 . ("Miro" "Arho" "Arhippa"))
                                (8 . ("Taimi"))
                                (9 . ("Eevert" "Isto"))
                                (10 . ("Kalevi" "Kaleva"))
                                (11 . ("Aleksanteri" "Ali" "Ale" "Aleksandra"))
                                (12 . ("Valma" "Vilja"))
                                (13 . ("Orvo"))
                                (14 . ("Iida"))
                                (15 . ("Sirpa"))
                                (16 . ("Hellevi" "Hillevi" "Hille" "Hilla"))
                                (17 . ("Aili" "Aila"))
                                (18 . ("Tyyne" "Tytti" "Tyyni"))
                                (19 . ("Reija"))
                                (20 . ("Varpu" "Vaula"))
                                (21 . ("Mervi"))
                                (22 . ("Mauri"))
                                (23 . ("Mielikki"))
                                (24 . ("Alvar" "Auno"))
                                (25 . ("Kullervo"))
                                (26 . ("Kuisma"))
                                (27 . ("Vesa"))
                                (28 . ("Arja"))
                                (29 . ("Mikael" "Mikko" "Miika" "Miikka" "Miska" "Mikaela"))
                                (30 . ("Sorja" "Sirja"))
                                ))
                          (10 . (
                                 (1 . ("Rauno" "Rainer" "Raine" "Raino"))
                                 (2 . ("Valio"))
                                 (3 . ("Raimo"))
                                 (4 . ("Saila" "Saija"))
                                 (5 . ("Inkeri" "Inka"))
                                 (6 . ("Minttu" "Pinja"))
                                 (7 . ("Pirkko" "Pirjo" "Piritta" "Pirita" "Birgitta"))
                                 (8 . ("Hilja"))
                                 (9 . ("Ilona"))
                                 (10 . ("Aleksi" "Aleksis"))
                                 (11 . ("Otso" "Ohto"))
                                 (12 . ("Aarre" "Aarto"))
                                 (13 . ("Taina" "Tanja" "Taija"))
                                 (14 . ("Elsa" "Else" "Elsi"))
                                 (15 . ("Helvi" "Heta"))
                                 (16 . ("Sirkka" "Sirkku"))
                                 (17 . ("Saini" "Saana"))
                                 (18 . ("Satu" "Säde"))
                                 (19 . ("Uljas"))
                                 (20 . ("Kauno" "Kasperi"))
                                 (21 . ("Ursula"))
                                 (22 . ("Anja" "Anita" "Anniina" "Anitta"))
                                 (23 . ("Severi"))
                                 (24 . ("Asmo" "Rasmus"))
                                 (25 . ("Sointu"))
                                 (26 . ("Amanda" "Niina" "Manta"))
                                 (27 . ("Helli" "Hellä" "Hellin" "Helle"))
                                 (28 . ("Simo"))
                                 (29 . ("Alfred" "Urmas"))
                                 (30 . ("Eila"))
                                 (31 . ("Artturi" "Arto" "Arttu"))
                                 ))
                          (11 . (
                                 (1 . ("Pyry" "Lyly"))
                                 (2 . ("Topi" "Topias"))
                                 (3 . ("Terho"))
                                 (4 . ("Hertta"))
                                 (5 . ("Reima"))
                                 (6 . ("Kustaa Aadolf"))
                                 (7 . ("Taisto"))
                                 (8 . ("Aatos"))
                                 (9 . ("Teuvo"))
                                 (10 . ("Martti"))
                                 (11 . ("Panu"))
                                 (12 . ("Virpi"))
                                 (13 . ("Ano" "Kristian"))
                                 (14 . ("Iiris"))
                                 (15 . ("Janika" "Janita" "Janina" "Janette"))
                                 (16 . ("Aarne" "Aarno" "Aarni"))
                                 (17 . ("Eino" "Einari"))
                                 (18 . ("Tenho" "Jousia"))
                                 (19 . ("Elisabet" "Liisa" "Eliisa" "Elisa" "Elise" "Liisi"))
                                 (20 . ("Jalmari" "Jari"))
                                 (21 . ("Hilma"))
                                 (22 . ("Silja" "Selja"))
                                 (23 . ("Ismo"))
                                 (24 . ("Lempi" "Lemmikki" "Sivi"))
                                 (25 . ("Katri" "Kaisa" "Kaija" "Katja" "Kaarina" "Katariina" "Katriina" "Kati" "Kaisu" "Riina"))
                                 (26 . ("Sisko"))
                                 (27 . ("Hilkka"))
                                 (28 . ("Heini"))
                                 (29 . ("Aimo"))
                                 (30 . ("Antti" "Antero" "Atte"))
                                 ))
                          (12 . (
                                 (1 . ("Oskari"))
                                 (2 . ("Anelma" "Unelma"))
                                 (3 . ("Vellamo" "Meri"))
                                 (4 . ("Airi" "Aira"))
                                 (5 . ("Selma"))
                                 (6 . ("Niilo" "Nikolai" "Niko" "Niklas"))
                                 (7 . ("Sampsa"))
                                 (8 . ("Kyllikki" "Kylli"))
                                 (9 . ("Anna" "Anne" "Anni" "Anu" "Annikki" "Anneli" "Annukka" "Annika"))
                                 (10 . ("Jutta"))
                                 (11 . ("Taneli" "Tatu" "Daniel"))
                                 (12 . ("Tuovi"))
                                 (13 . ("Seija"))
                                 (14 . ("Jouko"))
                                 (15 . ("Heimo"))
                                 (16 . ("Auli" "Aulikki" "Aada"))
                                 (17 . ("Raakel"))
                                 (18 . ("Aapo" "Aappo" "Rami"))
                                 (19 . ("Iikka" "Iiro" "Iisakki" "Isko"))
                                 (20 . ("Benjamin" "Kerkko"))
                                 (21 . ("Tuomas" "Tuomo" "Tommi" "Tomi"))
                                 (22 . ("Raafael"))
                                 (23 . ("Senni"))
                                 (24 . ("Aatami" "Eeva" "Eevi" "Eveliina"))
                                 (25 . ())
                                 (26 . ("Tapani" "Teppo" "Tahvo"))
                                 (27 . ("Hannu" "Hannes"))
                                 (28 . ("Piia"))
                                 (29 . ("Rauha"))
                                 (30 . ("Daavid" "Taavetti" "Taavi"))
                                 (31 . ("Sylvester" "Silvo"))
                                 ))))

(lyskom-define-namedays 'fi-sv
                        "Finlandssvenska"
                        "\
Namnsdagar enligt någon finlandssvensk namnlängd."
                        '((1 . ((1 . ())
                                (2 . ("Gehard" "Gert"))
                                (3 . ("Elmer" "Helmer"))
                                (4 . ("Rut"))
                                (5 . ("Lea"))
                                (6 . ("Harry" "Harriet"))
                                (7 . ("August" "Augusta"))
                                (8 . ("Gunnar"))
                                (9 . ("Bror"))
                                (10 . ("Sigurd"))
                                (11 . ("Osvald"))
                                (12 . ("Eivor"))
                                (13 . ("Knut"))
                                (14 . ("Zacharias"))
                                (15 . ("Doris"))
                                (16 . ("Gudmund" "Germund"))
                                (17 . ("Anton" "Tony" "Antonia"))
                                (18 . ("Laura"))
                                (19 . ("Henrik" "Henry" "Henrika" "Henrietta"))
                                (20 . ("Sebastian" "Fabian"))
                                (21 . ("Agnes" "Agneta"))
                                (22 . ("Fridolf"))
                                (23 . ("Ingegerd" "Ingela"))
                                (24 . ("Jarl"))
                                (25 . ("Paul"))
                                (26 . ("Jonatan"))
                                (27 . ("Folke"))
                                (28 . ("Karl" "Carola"))
                                (29 . ("Valter" "Volter"))
                                (30 . ("Gunnel" "Gunilla"))
                                (31 . ("Alfhild" "Alfa"))
                                ))
                          (2 . (
                                (1 . ("Berit"))
                                (2 . ("Disa"))
                                (3 . ("Hugo"))
                                (4 . ("My" "Ronja"))
                                (5 . ("Sture"))
                                (6 . ("Dorotea" "Dora" "Tea" "Dorrit"))
                                (7 . ("Rikard" "Dick"))
                                (8 . ("Rigmor"))
                                (9 . ("Bodil"))
                                (10 . ("Elin" "Ellen" "Ella" "Elna"))
                                (11 . ("Isabella" "Isa"))
                                (12 . ("Elma"))
                                (13 . ("Algot"))
                                (14 . ("Valenton"))
                                (15 . ("Sigfrid"))
                                (16 . ("Kaj"))
                                (17 . ("Carita" "Rita"))
                                (18 . ("Tyra"))
                                (19 . ("Fritjof"))
                                (20 . ("Ragnborg"))
                                (21 . ("Alf" "Alva"))
                                (22 . ("Hildur" "Hilda"))
                                (23 . ("Bjarne"))
                                (24 . ("Mattias" "Mats"))
                                (25 . ("Jessica"))
                                (26 . ("Ingvar" "Ingvald"))
                                (27 . ("Torsten"))
                                (28 . ("Rune" "Runa"))
                                ))
                          (3 . (
                                (1 . ("Albin"))
                                (2 . ("Fanny"))
                                (3 . ("Torbjörn"))
                                (4 . ("Adrian" "Atle"))
                                (5 . ("Laila" "Leila"))
                                (6 . ("Rudolf" "Rolf"))
                                (7 . ("Gudrun"))
                                (8 . ("Filip" "Filippa"))
                                (9 . ("Edvin"))
                                (10 . ("Aurora"))
                                (11 . ("Torgny" "Torkel"))
                                (12 . ("Gregorius" "Greger" "Grels"))
                                (13 . ("Ernst"))
                                (14 . ("Matilda"))
                                (15 . ("Kristoffer"))
                                (16 . ("Herbert"))
                                (17 . ("Gertrud" "Trude"))
                                (18 . ("Edvard"))
                                (19 . ("Josef" "Josefina"))
                                (20 . ("Joakim" "Kim"))
                                (21 . ("Bengt" "Bengta" "Benita"))
                                (22 . ("Viktor" "Viktoria"))
                                (23 . ("Axel"))
                                (24 . ("Gabriel" "Gabriella"))
                                (25 . ("Gunvor" "Gunnevi"))
                                (26 . ("Emanuel" "Immanuel" "Manne"))
                                (27 . ("Torvald"))
                                (28 . ("Sune"))
                                (29 . ("Jonas"))
                                (30 . ("Tage" "Guy"))
                                (31 . ("Irma" "Irmelin"))
                                ))
                          (4 . (
                                (1 . ("Harald"))
                                (2 . ("Vidar"))
                                (3 . ("Ferdinand"))
                                (4 . ("Odin"))
                                (5 . ("Irene"))
                                (6 . ("Vilhelm" "William"))
                                (7 . ("Allan"))
                                (8 . ("Rea"))
                                (9 . ("Elias" "Elis" "Eliel"))
                                (10 . ("Tryggve"))
                                (11 . ("Verna"))
                                (12 . ("Julius" "Julia" "Juliana"))
                                (13 . ("Etel"))
                                (14 . ("Ruben"))
                                (15 . ("Linda" "Linn"))
                                (16 . ("Patrik" "Patricia"))
                                (17 . ("Otto"))
                                (18 . ("Valdemar" "Volmar"))
                                (19 . ("Bernhard" "Bernt"))
                                (20 . ("Arla"))
                                (21 . ("Camilla"))
                                (22 . ("Alina"))
                                (23 . ("Georg" "Gäran" "Örjan" "Jöran" "Jörgen" "Jörn"))
                                (24 . ("Albert" "Albertina"))
                                (25 . ("Markus"))
                                (26 . ("Teresia" "Teresa" "Terese"))
                                (27 . ("Engelbrekt" "Engelbert"))
                                (28 . ("Ture"))
                                (29 . ("Henning"))
                                (30 . ("Mirjam" "Mia"))
                                ))
                          (5 . (
                                (1 . ("Valborg"))
                                (2 . ("Vivan" "Vivi"))
                                (3 . ("Linus"))
                                (4 . ("Rosa"))
                                (5 . ("Ulrik"))
                                (6 . ("Frej" "Frejvid" "Freja"))
                                (7 . ("Tua"))
                                (8 . ("Åke"))
                                (9 . ("Dennis"))
                                (10 . ("Aina" "Aino"))
                                (11 . ("Kennet" "Kent"))
                                (12 . ("Charlotta" "Lotta" "Lotten"))
                                (13 . ("Flora"))
                                (14 . ("Edit"))
                                (15 . ("Sofia" "Sonja"))
                                (16 . ("Ester"))
                                (17 . ("Rebecka" "Hagar"))
                                (18 . ("Erik" "Jerker" "Erika"))
                                (19 . ("Emilia" "Emma" "Amalia" "Mili"))
                                (20 . ("Karolina" "Lilli" "Lilian"))
                                (21 . ("Konstantin" "Conny"))
                                (22 . ("Hemming"))
                                (23 . ("Lydia"))
                                (24 . ("Alarik"))
                                (25 . ("Urban"))
                                (26 . ("Vilhelmina" "Minna"))
                                (27 . ("Ingeborg"))
                                (28 . ("Alma"))
                                (29 . ("Orvar" "Oliver" "Olivia"))
                                (30 . ("Rurik"))
                                (31 . ("Helge" "Helga"))
                                ))
                          (6 . (
                                (1 . ("Rabbe"))
                                (2 . ("Vendla"))
                                (3 . ("Viola"))
                                (4 . ("Selim"))
                                (5 . ("Stig"))
                                (6 . ("Gustav" "Gösta" "Gustava"))
                                (7 . ("Robert" "Robin"))
                                (8 . ("Kjell" "Kettil"))
                                (9 . ("Dag" "Daga"))
                                (10 . ("Svante"))
                                (11 . ("Hjördis"))
                                (12 . ("Eskil"))
                                (13 . ("Åsa" "Aslög"))
                                (14 . ("Tord"))
                                (15 . ("Mona" "Monika"))
                                (16 . ("Eugen"))
                                (17 . ("Birger" "Börje"))
                                (18 . ("Ingolf"))
                                (19 . ("Sigrid" "Siri"))
                                (20 . ("Inga"))
                                (21 . ("Ingemar" "Ingmar" "Inge"))
                                (22 . ("Paula" "Paulina" "Lina"))
                                (23 . ("Adolf"))
                                (24 . ("Johannes" "Johan" "Jan" "Jon" "John" "Johnny" "Jens"))
                                (25 . ("Uno"))
                                (26 . ("Viveka"))
                                (27 . ("Elvira" "Elvi"))
                                (28 . ("Leo" "Leopold"))
                                (29 . ("Peter" "Per" "Pette" "Petra" "Pernilla"))
                                (30 . ("Leif"))
                                ))
                          (7 . (
                                (1 . ("Kasimir"))
                                (2 . ("Maria" "Marika" "Maja" "Maj" "Mary"))
                                (3 . ("Egil" "Ejvind" "Öjvind"))
                                (4 . ("Ulrika" "Ulla"))
                                (5 . ("Ulf"))
                                (6 . ("Halvar"))
                                (7 . ("Klas" "Klaus"))
                                (8 . ("Tor" "Tore" "Tora"))
                                (9 . ("Nanna" "Nanny"))
                                (10 . ("Saima" "Saimi"))
                                (11 . ("Eleonora" "Nora" "Leonora" "Elli" "Ellinor"))
                                (12 . ("Herman"))
                                (13 . ("Joel"))
                                (14 . ("Alice"))
                                (15 . ("Ragnhild" "Ragni" "Ragna"))
                                (16 . ("Reinhold"))
                                (17 . ("Ossian"))
                                (18 . ("Fredrik" "Fred" "Fredrika"))
                                (19 . ("Sara" "Sally"))
                                (20 . ("Margareta" "Margit" "Marit" "Greta" "Gretel"))
                                (21 . ("Johanna" "Joanna" "Jonna" "Jenny" "Hanna" "Hannele"))
                                (22 . ("Magdalena" "Malin" "Lena"))
                                (23 . ("Olga"))
                                (24 . ("Kristina" "Kerstin" "Kristel" "Kia" "Stina" "Tina"))
                                (25 . ("Jakob" "Jim"))
                                (26 . ("Marta"))
                                (27 . ("Heidi"))
                                (28 . ("Håkan"))
                                (29 . ("Olof" "Olav" "Ola" "Ole" "Olle"))
                                (30 . ("Asta"))
                                (31 . ("Helena"))
                                ))
                          (8 . (
                                (1 . ("Gerda" "Gerd"))
                                (2 . ("Holger"))
                                (3 . ("Linnea"))
                                (4 . ("Vera"))
                                (5 . ("Gurli"))
                                (6 . ("Sixten"))
                                (7 . ("Yrsa"))
                                (8 . ("Sylvia"))
                                (9 . ("Eira" "Natalie"))
                                (10 . ("Lars"))
                                (11 . ("Susanna" "Susanne" "Sanna"))
                                (12 . ("Klara"))
                                (13 . ("Alfons"))
                                (14 . ("Svea"))
                                (15 . ("Marita" "Marianne" "Marlene"))
                                (16 . ("Brynolf"))
                                (17 . ("Verner" "Veronika"))
                                (18 . ("Bo"))
                                (19 . ("Magnus" "Måns"))
                                (20 . ("Samuel" "Sam"))
                                (21 . ("Sven"))
                                (22 . ("Ivar"))
                                (23 . ("Signe" "Signhild"))
                                (24 . ("Bertel" "Bertil" "Berta"))
                                (25 . ("Lovisa" "Louise"))
                                (26 . ("Märta" "Märit"))
                                (27 . ("Ralf" "Rufus"))
                                (28 . ("Hilding"))
                                (29 . ("Ines" "Ina"))
                                (30 . ("Emil"))
                                (31 . ("Arvid"))
                                ))
                          (9 . (
                                (1 . ("Gottfrid"))
                                (2 . ("Sunnöve"))
                                (3 . ("Solveig"))
                                (4 . ("Roland"))
                                (5 . ("Ragnvald" "Ronald" "Ronny"))
                                (6 . ("Felix"))
                                (7 . ("Regina" "Roy"))
                                (8 . ("Göte" "Göta"))
                                (9 . ("Evert"))
                                (10 . ("Östen"))
                                (11 . ("Alexander" "Alex" "Alexandra" "Sandra"))
                                (12 . ("Dagny"))
                                (13 . ("Ylva"))
                                (14 . ("Ida"))
                                (15 . ("Runar"))
                                (16 . ("Hellevi" "Hillevi" "Helvig"))
                                (17 . ("Hildegard" "Hildegerd"))
                                (18 . ("Pamela"))
                                (19 . ("Torborg"))
                                (20 . ("Erna"))
                                (21 . ("Gunborg" "Gunlög"))
                                (22 . ("Mauritz" "Moritz"))
                                (23 . ("Tekla"))
                                (24 . ("Alvar"))
                                (25 . ("Sigvard"))
                                (26 . ("Finn"))
                                (27 . ("Torolf"))
                                (28 . ("Leonard" "Lennart"))
                                (29 . ("Mikael" "Mikaela"))
                                (30 . ("Roger"))
                                ))
                          (10 . (
                                 (1 . ("Ragnar" "Rainer"))
                                 (2 . ("Ludvig"))
                                 (3 . ("Evald"))
                                 (4 . ("Frans" "Frank"))
                                 (5 . ("Ingrid" "Inger"))
                                 (6 . ("Bruno"))
                                 (7 . ("Birgitta" "Brita" "Britta" "Britt" "Birgit"))
                                 (8 . ("Peik"))
                                 (9 . ("Marina"))
                                 (10 . ("Alexis"))
                                 (11 . ("Björn"))
                                 (12 . ("Valfrid"))
                                 (13 . ("Ebbe" "Ebba"))
                                 (14 . ("Elsa" "Else" "Ilse"))
                                 (15 . ("Hedvig" "Hedda"))
                                 (16 . ("Stella"))
                                 (17 . ("Vesta" "Diana"))
                                 (18 . ("Saga"))
                                 (19 . ("Viking"))
                                 (20 . ("Kasper" "Jesper"))
                                 (21 . ("Ursula"))
                                 (22 . ("Anita" "Anja"))
                                 (23 . ("Severin" "Sören"))
                                 (24 . ("Fjalar" "Rasmus"))
                                 (25 . ("Yvonne"))
                                 (26 . ("Amanda" "Nina" "Ninni"))
                                 (27 . ("Sabina"))
                                 (28 . ("Simon"))
                                 (29 . ("Alfred"))
                                 (30 . ("Jennifer"))
                                 (31 . ("Artur"))
                                 ))
                          (11 . (
                                 (1 . ("Gun" "Gunhild"))
                                 (2 . ("Tobias"))
                                 (3 . ("Erland"))
                                 (4 . ("Carina"))
                                 (5 . ("Torhild"))
                                 (6 . ("Gustav Adolf"))
                                 (7 . ("Glenn"))
                                 (8 . ("Styrbjörn"))
                                 (9 . ("Teodor" "Theo"))
                                 (10 . ("Martin" "Mårten" "Martina"))
                                 (11 . ("Boris"))
                                 (12 . ("Konrad" "Kurt"))
                                 (13 . ("Kristian" "Krister"))
                                 (14 . ("Iris"))
                                 (15 . ("Janina" "Jannika" "Jeanette"))
                                 (16 . ("Arne" "Arnold" "Arna"))
                                 (17 . ("Einar" "Enar"))
                                 (18 . ("Max"))
                                 (19 . ("Elisabet" "Lisa" "Lisen" "Lisbet" "Elise"))
                                 (20 . ("Hjalmar" "Pontus"))
                                 (21 . ("Hilma"))
                                 (22 . ("Cecilia"))
                                 (23 . ("Yngve" "Yngvar"))
                                 (24 . ("Siv"))
                                 (25 . ("Katarina" "Karin" "Kajsa" "Katja"))
                                 (26 . ("Dagmar"))
                                 (27 . ("Astrid" "Estrid"))
                                 (28 . ("Sten"))
                                 (29 . ("Edgar"))
                                 (30 . ("Anders" "Andreas" "André" "Andrea"))
                                 ))
                          (12 . (
                                 (1 . ("Oskar"))
                                 (2 . ("Beata" "Beatrice"))
                                 (3 . ("Urda"))
                                 (4 . ("Barbara" "Barbro"))
                                 (5 . ("Selma"))
                                 (6 . ("Nils" "Niklas"))
                                 (7 . ("Agata" "Agda" "Ågot"))
                                 (8 . ("Magnhild" "Magna"))
                                 (9 . ("Anna" "Anni" "Anne" "Annika" "Anneli" "Annette"))
                                 (10 . ("Judit" "Jutta"))
                                 (11 . ("Daniel" "Dan" "Daniela"))
                                 (12 . ("Tove"))
                                 (13 . ("Lucia"))
                                 (14 . ("Ove"))
                                 (15 . ("Sverker"))
                                 (16 . ("Ada" "Adele"))
                                 (17 . ("Rakel"))
                                 (18 . ("Abraham"))
                                 (19 . ("Isak"))
                                 (20 . ("Benjamin" "Ben" "Benny"))
                                 (21 . ("Tomas" "Tom" "Tommy"))
                                 (22 . ("Rafael"))
                                 (23 . ("Elof"))
                                 (24 . ("Adam" "Eva"))
                                 (25 . ())
                                 (26 . ("Stefan" "Staffan"))
                                 (27 . ("Hans"))
                                 (28 . ("Pia"))
                                 (29 . ("Frida"))
                                 (30 . ("David"))
                                 (31 . ("Sylvester"))
                                 ))))

(lyskom-define-namedays 'hu
                        "Magyar"
                        "\
Namnsdagar ur någon ungersk namnlängd. Eftersom utvecklarna inte kan
ungerska så är beskrivningen på svenska, med förhoppningen att någon
som kan ungerska kommer att reta sig på den och skriva om texten på
rätt språk."
                        '((1 . (
                                (1 . ("Fruzsina"))
                                (2 . ("Ábel"))
                                (3 . ("Genovéna" "Benjámin"))
                                (4 . ("Titusz" "Leóna" "Angéla"))
                                (5 . ("Simon" "Amata"))
                                (6 . ("Boldizsár"))
                                (7 . ("Attila" "Ramóna"))
                                (8 . ("Gyöngyvér"))
                                (9 . ("Marcell"))
                                (10 . ("Melánia"))
                                (11 . ("Ágota"))
                                (12 . ("Ernõ"))
                                (13 . ("Veronika"))
                                (14 . ("Bódog"))
                                (15 . ("Lóránt" "Lóránd"))
                                (16 . ("Gusztáv"))
                                (17 . ("Antal" "Antónia"))
                                (18 . ("Piroska"))
                                (19 . ("Sára" "Márió"))
                                (20 . ("Fábián" "Sebestyén"))
                                (21 . ("Ágnes"))
                                (22 . ("Vince" "Artúr" "Anasztáz"))
                                (23 . ("Zelma" "Rajmund"))
                                (24 . ("Timót"))
                                (25 . ("Pál"))
                                (26 . ("Vanda" "Paula"))
                                (27 . ("Angelika" "Angéla"))
                                (28 . ("Károly" "Karola"))
                                (29 . ("Adél"))
                                (30 . ("Martina" "Gerda"))
                                (31 . ("Marcella"))
                                ))
                          (2 . (
                                (1 . ("Ignác"))
                                (2 . ("Karolina" "Aida"))
                                (3 . ("Balázs"))
                                (4 . ("Ráhel" "Csenge" "András"))
                                (5 . ("Ágota" "Ingrid"))
                                (6 . ("Dorottya" "Dóra" "Ajándék" "Amanda"))
                                (7 . ("Tódor" "Rómeó"))
                                (8 . ("Aranka"))
                                (9 . ("Abigél" "Alex"))
                                (10 . ("Elvira"))
                                (11 . ("Bertold" "Marietta" "Adolf"))
                                (12 . ("Lívia" "Lídia"))
                                (13 . ("Ella" "Linda"))
                                (14 . ("Bálint" "Valentin"))
                                (15 . ("Kolos" "Georgina"))
                                (16 . ("Julianna" "Lilla"))
                                (17 . ("Donát"))
                                (18 . ("Bernadett"))
                                (19 . ("Zsuzsanna"))
                                (20 . ("Aladár" "Álmos" "Amata"))
                                (21 . ("Eleonóra"))
                                (22 . ("Gerzson"))
                                (23 . ("Alfréd"))
                                (24 . ("Mátyás"))
                                (25 . ("Géza"))
                                (26 . ("Edina"))
                                (27 . ("Ákos" "Bátor"))
                                (28 . ("Elemér"))
                                ))
                          (3 . (
                                (1 . ("Albin" "Albina"))
                                (2 . ("Lujza"))
                                (3 . ("Kornélia"))
                                (4 . ("Kázmér" "Adorján" "Adrián" "Adrienn"))
                                (5 . ("Adorján" "Adrián" "Adrienn"))
                                (6 . ("Leonora" "Inez"))
                                (7 . ("Tamás"))
                                (8 . ("Zoltán"))
                                (9 . ("Franciska" "Fanni"))
                                (10 . ("Ildikó"))
                                (11 . ("Szilárd" "Aladár"))
                                (12 . ("Gergely"))
                                (13 . ("Krisztián" "Ajtóny"))
                                (14 . ("Matild"))
                                (15 . ("Kristóf"))
                                (16 . ("Henrietta"))
                                (17 . ("Gertrúd" "Patrik"))
                                (18 . ("Sándor" "Ede" "Alexandra"))
                                (19 . ("József" "Bánk"))
                                (20 . ("Klaudia"))
                                (21 . ("Benedek"))
                                (22 . ("Beáta" "Izolda"))
                                (23 . ("Emõke"))
                                (24 . ("Gábor" "Karina"))
                                (25 . ("Irén" "Írisz"))
                                (26 . ("Emánuel"))
                                (27 . ("Hajnalka"))
                                (28 . ("Gedeon" "Johanna"))
                                (29 . ("Aguszta"))
                                (30 . ("Zalán" "Amade"))
                                (31 . ("Árpád" "Ajtóny"))
                                ))
                          (4 . (
                                (1 . ("Hugó"))
                                (2 . ("Áron"))
                                (3 . ("Buda" "Richárd"))
                                (4 . ("Izidor"))
                                (5 . ("Vince"))
                                (6 . ("Vilmos" "Bíborka"))
                                (7 . ("Herman"))
                                (8 . ("Dénes"))
                                (9 . ("Erhard"))
                                (10 . ("Zsolt"))
                                (11 . ("Leó" "Szaniszló"))
                                (12 . ("Gyula"))
                                (13 . ("Ida"))
                                (14 . ("Tibor"))
                                (15 . ("Anasztázia" "Tas"))
                                (16 . ("Csongor"))
                                (17 . ("Rudolf"))
                                (18 . ("Andrea" "Ilma" "András"))
                                (19 . ("Emma"))
                                (20 . ("Tivadar"))
                                (21 . ("Konrád"))
                                (22 . ("Csilla" "Noémi"))
                                (23 . ("Béla"))
                                (24 . ("György"))
                                (25 . ("Márk"))
                                (26 . ("Ervin"))
                                (27 . ("Zita"))
                                (28 . ("Valéria"))
                                (29 . ("Péter" "Albert" "Albertína"))
                                (30 . ("Katalin" "Kitti"))
                                ))
                          (5 . (
                                (1 . ("Fülöp" "Jakab" "Amarilla"))
                                (2 . ("Zsigmond"))
                                (3 . ("Tímea" "Irma"))
                                (4 . ("Mónika" "Flórián"))
                                (5 . ("Györgyi"))
                                (6 . ("Ivett" "Frida"))
                                (7 . ("Gizella"))
                                (8 . ("Mihály"))
                                (9 . ("Gergely"))
                                (10 . ("Ármin" "Pálma"))
                                (11 . ("Ferenc" "Adolf"))
                                (12 . ("Pongrác"))
                                (13 . ("Szervác" "Imola"))
                                (14 . ("Bonifác"))
                                (15 . ("Zsófia" "Szonja"))
                                (16 . ("Mózes" "Botond"))
                                (17 . ("Paszkál"))
                                (18 . ("Erik" "Alexandra"))
                                (19 . ("Ivó" "Milán"))
                                (20 . ("Bernát" "Felícia"))
                                (21 . ("Konstantin" "András"))
                                (22 . ("Júlia" "Rita"))
                                (23 . ("Dezsõ"))
                                (24 . ("Eszter" "Eliza"))
                                (25 . ("Orbán"))
                                (26 . ("Fülöp" "Evelin" "Amanda"))
                                (27 . ("Hella"))
                                (28 . ("Emil" "Csanád" "Ágoston"))
                                (29 . ("Magdolna"))
                                (30 . ("Janka" "Zsanett"))
                                (31 . ("Angéla" "Petronella"))
                                ))
                          (6 . (
                                (1 . ("Tünde"))
                                (2 . ("Kármen" "Anita" "Anna" "Annamária"))
                                (3 . ("Klotild"))
                                (4 . ("Bulcsú"))
                                (5 . ("Fatime"))
                                (6 . ("Norbert" "Cintia"))
                                (7 . ("Róbert"))
                                (8 . ("Medárd"))
                                (9 . ("Félix" "Annabella"))
                                (10 . ("Margit" "Gréta"))
                                (11 . ("Barnabás"))
                                (12 . ("Villó"))
                                (13 . ("Antal" "Anett" "Anna"))
                                (14 . ("Vazul"))
                                (15 . ("Jolán" "Vid"))
                                (16 . ("Jusztin"))
                                (17 . ("Laura" "Alida" "Adolf" "Alina" "Alinda"))
                                (18 . ("Arnold" "Levente"))
                                (19 . ("Gyárfás"))
                                (20 . ("Rafael"))
                                (21 . ("Alajos" "Leila" "Aladár" "Aloma"))
                                (22 . ("Paulina"))
                                (23 . ("Zoltán"))
                                (24 . ("Iván"))
                                (25 . ("Vilmos"))
                                (26 . ("János" "Pál"))
                                (27 . ("Lászlo"))
                                (28 . ("Levente" "Irén"))
                                (29 . ("Péter" "Pál"))
                                (30 . ("Pál"))
                                ))
                          (7 . (
                                (1 . ("Tihamér" "Annamária"))
                                (2 . ("Ottó"))
                                (3 . ("Kornél" "Soma" "Anatol"))
                                (4 . ("Ulrik"))
                                (5 . ("Emese" "Sarolta" "Antal"))
                                (6 . ("Csaba"))
                                (7 . ("Apollonia"))
                                (8 . ("Ellák"))
                                (9 . ("Lukrécia"))
                                (10 . ("Amália" "Alma"))
                                (11 . ("Nóra" "Lili"))
                                (12 . ("Izabella" "Dalma"))
                                (13 . ("Jenõ"))
                                (14 . ("Örs" "Stella"))
                                (15 . ("Henrik" "Roland"))
                                (16 . ("Valter"))
                                (17 . ("Endre" "Elek" "Ajándék" "András"))
                                (18 . ("Frigyes"))
                                (19 . ("Emília"))
                                (20 . ("Íllás"))
                                (21 . ("Dániel" "Daniella"))
                                (22 . ("Magdolna"))
                                (23 . ("Lenke"))
                                (24 . ("Kinga" "Kincsó"))
                                (25 . ("Kristóf" "Jakab"))
                                (26 . ("Anna" "Anikó" "Annamária"))
                                (27 . ("Olga" "Liliana" "Ajtóny"))
                                (28 . ("Szabolcs"))
                                (29 . ("Márta" "Flóra"))
                                (30 . ("Judit" "Xénia"))
                                (31 . ("Oszkár"))
                                ))
                          (8 . (
                                (1 . ("Boglárka"))
                                (2 . ("Lehel" "Alfonz"))
                                (3 . ("Hermina"))
                                (4 . ("Domonkos" "Dominika"))
                                (5 . ("Krisztina" "Afrodité"))
                                (6 . ("Berta" "Bettína"))
                                (7 . ("Ibolya" "Afrodité"))
                                (8 . ("László"))
                                (9 . ("Emõd"))
                                (10 . ("Lõrinc"))
                                (11 . ("Zsuzsanna" "Tiborc"))
                                (12 . ("Klára"))
                                (13 . ("Ipoly"))
                                (14 . ("Marcell"))
                                (15 . ("Mária"))
                                (16 . ("Ábrahám"))
                                (17 . ("Jácint" "Anasztáz"))
                                (18 . ("Ilona"))
                                (19 . ("Huba"))
                                (20 . ("István"))
                                (21 . ("Sámuel" "Hajna"))
                                (22 . ("Menyhért" "Mirjam"))
                                (23 . ("Bence"))
                                (24 . ("Bertalan"))
                                (25 . ("Lajos" "Patrícia"))
                                (26 . ("Izsó"))
                                (27 . ("Gáspár"))
                                (28 . ("Ágoston"))
                                (29 . ("Beatrix" "Erna"))
                                (30 . ("Rózsa"))
                                (31 . ("Erika" "Bella"))
                                ))
                          (9 . (
                                (1 . ("Egyed" "Egon"))
                                (2 . ("Rebeka" "Dorina"))
                                (3 . ("Hilda"))
                                (4 . ("Rozália"))
                                (5 . ("Viktor" "Lõrinc" "Alpár"))
                                (6 . ("Zakariás"))
                                (7 . ("Regina"))
                                (8 . ("Mária" "Adrienn" "Adorján" "Adrián"))
                                (9 . ("Ádám" "András"))
                                (10 . ("Nikolett" "Hunor"))
                                (11 . ("Teodóra"))
                                (12 . ("Mária"))
                                (13 . ("Kornél" "Amata"))
                                (14 . ("Szeréna" "Roxána"))
                                (15 . ("Enikõ" "Melitta"))
                                (16 . ("Edit"))
                                (17 . ("Zsófia"))
                                (18 . ("Diána"))
                                (19 . ("Vilhelmina"))
                                (20 . ("Friderika"))
                                (21 . ("Máté" "Mirella"))
                                (22 . ("Móric"))
                                (23 . ("Tekla"))
                                (24 . ("Gellért" "Mercédesz"))
                                (25 . ("Eufrózina" "Kende"))
                                (26 . ("Jusztina"))
                                (27 . ("Adalbert" "Adolf" "Albertina"))
                                (28 . ("Vencel"))
                                (29 . ("Mihály"))
                                (30 . ("Jeromos"))
                                ))
                          (10 . (
                                 (1 . ("Malvin"))
                                 (2 . ("Petra"))
                                 (3 . ("Helga"))
                                 (4 . ("Ferenc"))
                                 (5 . ("Aurél"))
                                 (6 . ("Brúnó" "Renáta"))
                                 (7 . ("Amália"))
                                 (8 . ("Koppány"))
                                 (9 . ("Dénes"))
                                 (10 . ("Gedeon"))
                                 (11 . ("Brigitta"))
                                 (12 . ("Miksa"))
                                 (13 . ("Kálmán" "Ede"))
                                 (14 . ("Helén"))
                                 (15 . ("Teréz"))
                                 (16 . ("Gál"))
                                 (17 . ("Hedvig"))
                                 (18 . ("Lukács"))
                                 (19 . ("Nándor"))
                                 (20 . ("Vendel"))
                                 (21 . ("Orsolya"))
                                 (22 . ("Elõd"))
                                 (23 . ("Gyöngyi"))
                                 (24 . ("Salamon"))
                                 (25 . ("Blanka" "Bianka"))
                                 (26 . ("Dömötör"))
                                 (27 . ("Szabina"))
                                 (28 . ("Simon" "Szimonetta"))
                                 (29 . ("Nárcisz"))
                                 (30 . ("Alfonz"))
                                 (31 . ("Farkas"))
                                 ))
                          (11 . (
                                 (1 . ("Marianna"))
                                 (2 . ("Achilles"))
                                 (3 . ("Gyõzõ"))
                                 (4 . ("Károly"))
                                 (5 . ("Imre"))
                                 (6 . ("Lénárd"))
                                 (7 . ("Rezsõ"))
                                 (8 . ("Zsombor"))
                                 (9 . ("Tivadar"))
                                 (10 . ("Réka" "András"))
                                 (11 . ("Márton"))
                                 (12 . ("Jónás" "Renátó" "Aba"))
                                 (13 . ("Szilvia"))
                                 (14 . ("Alíz"))
                                 (15 . ("Albert" "Lipót"))
                                 (16 . ("Ödön"))
                                 (17 . ("Hortenzia" "Gergõ"))
                                 (18 . ("Jenõ"))
                                 (19 . ("Erzsébet"))
                                 (20 . ("Jolán"))
                                 (21 . ("Olivér"))
                                 (22 . ("Cecília"))
                                 (23 . ("Kelemen" "Klementína"))
                                 (24 . ("Emma"))
                                 (25 . ("Katalin" "Alan"))
                                 (26 . ("Virág"))
                                 (27 . ("Virgil"))
                                 (28 . ("Stefánia"))
                                 (29 . ("Taksony"))
                                 (30 . ("András" "Andor"))
                                 ))
                          (12 . (
                                 (1 . ("Elza"))
                                 (2 . ("Melinda" "Vivien"))
                                 (3 . ("Ferenc"))
                                 (4 . ("Borbála" "Barbara"))
                                 (5 . ("Vilma"))
                                 (6 . ("Miklós"))
                                 (7 . ("Ambrus"))
                                 (8 . ("Mária"))
                                 (9 . ("Natália"))
                                 (10 . ("Judit"))
                                 (11 . ("Árpád"))
                                 (12 . ("Gabriella"))
                                 (13 . ("Luca" "Otília"))
                                 (14 . ("Szilárda"))
                                 (15 . ("Valér"))
                                 (16 . ("Etelka" "Aletta" "Albina"))
                                 (17 . ("Lázár" "Olimpia"))
                                 (18 . ("Auguszta"))
                                 (19 . ("Viola"))
                                 (20 . ("Teofil"))
                                 (21 . ("Tamás"))
                                 (22 . ("Zénó"))
                                 (23 . ("Viktória"))
                                 (24 . ("Ádám" "Éva" "Ada" "Adél"))
                                 (25 . ("Eugénia"))
                                 (26 . ("István"))
                                 (27 . ("János"))
                                 (28 . ("Kamilla"))
                                 (29 . ("Tamás" "Tamara"))
                                 (30 . ("Dávid"))
                                 (31 . ("Szilveszter"))
                                 ))
                          ))


(lyskom-define-namedays 'cz
                        "Czechia"
                        "\
Namnsdagar ur någon tjeckisk namnlängd. Eftersom utvecklarna inte kan
tjeckiska så är beskrivningen på svenska, med förhoppningen att någon
som kan tjeckiska kommer att reta sig på den och skriva om texten på
rätt språk."
                        '((1 . (
                                (1 . ("Nov-Bý-A rok"))
                                (2 . ("Karina"))
                                (3 . ("Radmila"))
                                (4 . ("Diana"))
                                (5 . ("Dalimil"))
                                (6 . ("T-Bøi-A kr-Bálové"))-A
                                (7 . ("Vilma"))
                                (8 . ("-BÈestmír"))-A
                                (9 . ("Vladan"))
                                (10 . ("B-Bøetislav"))-A
                                (11 . ("Bohdana"))
                                (12 . ("Pravoslav"))
                                (13 . ("Edita"))
                                (14 . ("Radovan"))
                                (15 . ("Alice"))
                                (16 . ("Ctirad"))
                                (17 . ("Drahoslav"))
                                (18 . ("Vladislav"))
                                (19 . ("Doubravka"))
                                (20 . ("Ilona"))
                                (21 . ("B-Bìla"))-A
                                (22 . ("Slavom-Bír"))-A
                                (23 . ("Zden-Bìk"))-A
                                (24 . ("Milena"))
                                (25 . ("Milo-B¹"))-A
                                (26 . ("Zora"))
                                (27 . ("Ingrid"))
                                (28 . ("Ot-Býlie"))-A
                                (29 . ("Zdislava"))
                                (30 . ("Robin"))
                                (31 . ("Marika"))
                                ))
                          (2 . (
                                (1 . ("Hynek"))
                                (2 . ("Nela"))
                                (3 . ("Bla-B¾ej"))-A
                                (4 . ("Jarmila"))
                                (5 . ("Dobromila"))
                                (6 . ("Vanda"))
                                (7 . ("Veronika"))
                                (8 . ("Milada"))
                                (9 . ("Apolena"))
                                (10 . ("Mojm-Bír"))-A
                                (11 . ("Bo-B¾ena"))-A
                                (12 . ("Slav-Bìna"))-A
                                (13 . ("V-Bìnceslav"))-A
                                (14 . ("Valent-Býn"))-A
                                (15 . ("Ji-Bøina"))-A
                                (16 . ("Ljuba"))
                                (17 . ("Miloslava"))
                                (18 . ("Gizela"))
                                (19 . ("Patrik"))
                                (20 . ("Old-Bøich"))-A
                                (21 . ("Lenka"))
                                (22 . ("Petr"))
                                (23 . ("Svatopluk"))
                                (24 . ("Mat-Bìj"))-A
                                (25 . ("Liliana"))
                                (26 . ("Dora"))
                                (27 . ("Alexandr"))
                                (28 . ("Lum-Bír"))-A
                                ))
                          (3 . (
                                (1 . ("Alb-Bín"))-A
                                (2 . ("Ane-B¾ka"))-A
                                (3 . ("Kamil"))
                                (4 . ("Stela"))
                                (5 . ("Kazim-Bír"))-A
                                (6 . ("Miroslav"))
                                (7 . ("Tom-Bá¹"))-A
                                (8 . ("Gabriela"))
                                (9 . ("Franti-B¹ka"))-A
                                (10 . ("Viktorie"))
                                (11 . ("And-Bìla"))-A
                                (12 . ("-BØehoø"))-A
                                (13 . ("R-Bù¾ena"))-A
                                (14 . ("Matylda" "R-Bút"))-A
                                (15 . ("Ida"))
                                (16 . ("Amos"))
                                (17 . ("Vlastimil"))
                                (18 . ("Eduard"))
                                (19 . ("Josef"))
                                (20 . ("Sv-Bìtlana"))-A
                                (21 . ("Radek"))
                                (22 . ("Leona"))
                                (23 . ("Ivona"))
                                (24 . ("Gabriel"))
                                (25 . ("Mari-Bán"))-A
                                (26 . ("Emanuel"))
                                (27 . ("Dita"))
                                (28 . ("So-Bòa"))-A
                                (29 . ("Ta-B»ána"))-A
                                (30 . ("Arno-B¹t"))-A
                                (31 . ("Kvido"))
                                ))
                          (4 . (
                                (1 . ("Hugo"))
                                (2 . ("Erika"))
                                (3 . ("Richard"))
                                (4 . ("Ivana"))
                                (5 . ("Miroslava"))
                                (6 . ("Vendula"))
                                (7 . ("He-Bøman"-A "Herm-Bína"))-A
                                (8 . ("Ema"))
                                (9 . ("Du-B¹an"))-A
                                (10 . ("Darja"))
                                (11 . ("Izabela"))
                                (12 . ("Julius"))
                                (13 . ("Ale-B¹"))-A
                                (14 . ("Vincenc"))
                                (15 . ("Anast-Bázie"))-A
                                (16 . ("Irena"))
                                (17 . ("Rudolf"))
                                (18 . ("Val-Bérie"))-A
                                (19 . ("Rostislav"))
                                (20 . ("Marcela"))
                                (21 . ("Alexandra"))
                                (22 . ("Ev-B¾enie"))-A
                                (23 . ("Vojt-Bìch"))-A
                                (24 . ("Ji-Bøí"))-A
                                (25 . ("Marek"))
                                (26 . ("Oto"))
                                (27 . ("Jaroslav"))
                                (28 . ("Vlastislav"))
                                (29 . ("Robert"))
                                (30 . ("Blahoslav"))
                                ))
                          (5 . (
                                (1 . ("Sv-Bátek-A pr-Báce"))-A
                                (2 . ("Zikmund"))
                                (3 . ("Alexej"))
                                (4 . ("Kv-Bìtoslav"))-A
                                (5 . ("Klaudie"))
                                (6 . ("Radoslav"))
                                (7 . ("Stanislav"))
                                (8 . ())
                                (9 . ("Ctibor"))
                                (10 . ("Bla-B¾ena"))-A
                                (11 . ("Svatava"))
                                (12 . ("Pankr-Bác"))-A
                                (13 . ("Serv-Bác"))-A
                                (14 . ("Bonif-Bác"))-A
                                (15 . ("-B®ofie"))-A
                                (16 . ("P-Bøemysl"))-A
                                (17 . ("Aneta"))
                                (18 . ("Nata-B¹a"))-A
                                (19 . ("Ivo"))
                                (20 . ("Zby-B¹ek"))-A
                                (21 . ("Monika"))
                                (22 . ("Emil"))
                                (23 . ("Vladim-Bír"))-A
                                (24 . ("Jana"))
                                (25 . ("Viola"))
                                (26 . ("Filip"))
                                (27 . ("Valdemar"))
                                (28 . ("Vil-Bém"))-A
                                (29 . ("Maximili-Bán"-A "Maxim"))
                                (30 . ("Ferdinand"))
                                (31 . ("Kamila"))
                                ))
                          (6 . (
                                (1 . ("Laura"))
                                (2 . ("Jarmil"))
                                (3 . ("Tamara"))
                                (4 . ("Dalibor"))
                                (5 . ("Dobroslav"))
                                (6 . ("Norbert"))
                                (7 . ("Iveta"))
                                (8 . ("Medard"))
                                (9 . ("Stanislava"))
                                (10 . ("Gita"))
                                (11 . ("Fl-Bóra"))-A
                                (12 . ("Antonie"))
                                (13 . ("Anton-Bín"))-A
                                (14 . ("Vasil"))
                                (15 . ("V-Bít"))-A
                                (16 . ("Zbyn-Bìk"))-A
                                (17 . ("Adolf"))
                                (18 . ("Milan"))
                                (19 . ("Leo-B¹"))-A
                                (20 . ("Kv-Bìta"))-A
                                (21 . ("Alois"))
                                (22 . ("Pavla"))
                                (23 . ("Zde-Bòka"))-A
                                (24 . ("Jan"))
                                (25 . ("Ivan"))
                                (26 . ("Adriana"))
                                (27 . ("Ladislav"))
                                (28 . ("Lubom-Bír"))-A
                                (29 . ("Petr a Pav"))
                                (30 . ("-B©árka"))-A
                                ))
                          (7 . (
                                (1 . ("Jaroslava"))
                                (2 . ("Patricie"))
                                (3 . ("Radom-Bír"))-A
                                (4 . ("Prokop"))
                                (5 . ("Cyril"))
                                (6 . ("Jan Hus"))
                                (7 . ("Bohuslava"))
                                (8 . ("Nora"))
                                (9 . ("Drahoslava"))
                                (10 . ("Libu-B¹e"))-A
                                (11 . ("Olga"))
                                (12 . ("Bo-Bøek"))-A
                                (13 . ("Mark-Béta"))-A
                                (14 . ("Karol-Bína"))-A
                                (15 . ("Jind-Bøich"))-A
                                (16 . ("Lubo-B¹"))-A
                                (17 . ("Martina"))
                                (18 . ("Drahom-Bíra"))-A
                                (19 . ("-BÈenìk"))-A
                                (20 . ("Ilja"))
                                (21 . ("V-Bítìzslav"))-A
                                (22 . ("Magdal-Béna"))-A
                                (23 . ("Libor"))
                                (24 . ("Krist-Býna"))-A
                                (25 . ("Jakub"))
                                (26 . ("Anna"))
                                (27 . ("V-Bìroslav"))-A
                                (28 . ("Viktor"))
                                (29 . ("Marta"))
                                (30 . ("Bo-Bøivoj"))-A
                                (31 . ("Ign-Bác"))-A
                                ))
                          (8 . (
                                (1 . ("Oskar"))
                                (2 . ("Gustav"))
                                (3 . ("Milu-B¹e"))-A
                                (4 . ("Dominik"))
                                (5 . ("Milivoj"))
                                (6 . ("Old-Bøi¹ka"))-A
                                (7 . ("Lada"))
                                (8 . ("Sob-Bìslav"))-A
                                (9 . ("Roman"))
                                (10 . ("Vav-Bøinec"))-A
                                (11 . ("Zuzana"))
                                (12 . ("Kl-Bára"))-A
                                (13 . ("Alena"))
                                (14 . ("Alan"))
                                (15 . ("Hana"))
                                (16 . ("J-Báchym"))-A
                                (17 . ("Petra"))
                                (18 . ("Helena"))
                                (19 . ("Ludv-Bík"))-A
                                (20 . ("Bernard"))
                                (21 . ("Johana"))
                                (22 . ("Bohuslav"))
                                (23 . ("Sandra"))
                                (24 . ("Bartolom-Bìj"))-A
                                (25 . ("Radim"))
                                (26 . ("Lud-Bìk"))-A
                                (27 . ("Otakar"))
                                (28 . ("August-Býn"))-A
                                (29 . ())
                                (30 . ("-B©tìpán"))-A
                                (31 . ("Pavl-Bína"))-A
                                ))
                          (9 . (
                                (1 . ("Linda" "Samuel"))
                                (2 . ("Ad-Béla"))-A
                                (3 . ("Bronislav"))
                                (4 . ("Jind-Bøi¹ka"))-A
                                (5 . ("Boris"))
                                (6 . ("Boleslav"))
                                (7 . ("Regina"))
                                (8 . ("Mariana"))
                                (9 . ("Daniela"))
                                (10 . ("Irma"))
                                (11 . ("Denisa"))
                                (12 . ("Marie"))
                                (13 . ("Lubor"))
                                (14 . ("Radka"))
                                (15 . ("Jolana"))
                                (16 . ("Ludmil"))
                                (17 . ("Nad-Bì¾da"))-A
                                (18 . ("Kry-B¹tof"))-A
                                (19 . ("Zita"))
                                (20 . ("Oleg"))
                                (21 . ("Matou-B¹"))-A
                                (22 . ("Darina"))
                                (23 . ("Berta"))
                                (24 . ("Jarom-Bír"))-A
                                (25 . ("Zlata"))
                                (26 . ("Andrea"))
                                (27 . ("Jon-Bá¹"))-A
                                (28 . ("V-Báclav"))-A
                                (29 . ("Michal"))
                                (30 . ("Jeron-Bým"))-A
                                ))
                          (10 . (
                                 (1 . ("Igor"))
                                 (2 . ("Ol-Bívie"-A "Galina" "Oliver"))
                                 (3 . ("Bohumil"))
                                 (4 . ("Franti-B¹ek"))-A
                                 (5 . ("Eli-B¹ka"))-A
                                 (6 . ("Hanu-B¹"))-A
                                 (7 . ("Sergej"))
                                 (8 . ("V-Bìra"))-A
                                 (9 . ("-B©tefan"-A "S-Bára"))-A
                                 (10 . ("Marina"))
                                 (11 . ("Andrej"))
                                 (12 . ("Marcel"))
                                 (13 . ("Ren-Báta"))-A
                                 (14 . ("Ag-Báta"))-A
                                 (15 . ("Tereza"))
                                 (16 . ("Havel"))
                                 (17 . ("Hedvika"))
                                 (18 . ("Luk-Bá¹"))-A
                                 (19 . ("Michaela"))
                                 (20 . ("Vendel-Bín"))-A
                                 (21 . ("Brigita"))
                                 (22 . ("Sabina"))
                                 (23 . ("Teodor"))
                                 (24 . ("Nina"))
                                 (25 . ("Be-Báta"))-A
                                 (26 . ("Erik"))
                                 (27 . ("-B©arlota"-A "Zoja" "Zoe"))
                                 (28 . ())
                                 (29 . ("Silvie"))
                                 (30 . ("Tade-Bá¹"))-A
                                 (31 . ("-B©tìpánka"))-A
                                 ))
                          (11 . (
                                 (1 . ("Felix"))
                                 (2 . ("Du-B¹ièky"))-A
                                 (3 . ("Hubert"))
                                 (4 . ("Karel"))
                                 (5 . ("Miriam"))
                                 (6 . ("Lib-Bìna"))-A
                                 (7 . ("Saskie"))
                                 (8 . ("Bohum-Bír"))-A
                                 (9 . ("Bohdan"))
                                 (10 . ("Ev-B¾en"))-A
                                 (11 . ("MArtin"))
                                 (12 . ("Benedikt"))
                                 (13 . ("Tibor"))
                                 (14 . ("S-Báva"))-A
                                 (15 . ("Leopold"))
                                 (16 . ("Otmar"))
                                 (17 . ("Mahulena"))
                                 (18 . ("Romana"))
                                 (19 . ("Al-B¾bìta"))-A
                                 (20 . ("Nikola"))
                                 (21 . ("Albert"))
                                 (22 . ("Cec-Bílie"))-A
                                 (23 . ("Klement"))
                                 (24 . ("Em-Bílie"))-A
                                 (25 . ("Kate-Bøina"))-A
                                 (26 . ("Artur"))
                                 (27 . ("Xenie"))
                                 (28 . ("Ren-Bé"))-A
                                 (29 . ("Zina"))
                                 (30 . ("Ond-Bøej"))-A
                                 ))
                          (12 . (
                                 (1 . ("Iva"))
                                 (2 . ("Blanka"))
                                 (3 . ("Svatoslav"))
                                 (4 . ("Barbora"))
                                 (5 . ("Jitka"))
                                 (6 . ("Mikul-Bá¹"))-A
                                 (7 . ("Ambro-B¾"-A "Benjam-Bín"))-A
                                 (8 . ("Kv-Bìtoslava"))-A
                                 (9 . ("Vratislav"))
                                 (10 . ("Julie"))
                                 (11 . ("Dana"))
                                 (12 . ("Simona"))
                                 (13 . ("Lucie"))
                                 (14 . ("L-Býdie"))-A
                                 (15 . ("Radan" "Radana"))
                                 (16 . ("Alb-Bína"))-A
                                 (17 . ("Daniel"))
                                 (18 . ("Miloslav"))
                                 (19 . ("Ester"))
                                 (20 . ("Dagmar"))
                                 (21 . ("Nat-Bálie"))-A
                                 (22 . ("-B©imon"))-A
                                 (23 . ("Vlasta"))
                                 (24 . ("Adam a Eva"))
                                 (25 . ("Bo-B¾í-A hod"))
                                 (26 . ("-B©tìpán"))-A
                                 (27 . ("-B®aneta"))-A
                                 (28 . ("Bohumila"))
                                 (29 . ("Judita"))
                                 (30 . ("David"))
                                 (31 . ("Silvestr"))
                                 ))
                          ))

(lyskom-define-namedays 'fi-orthodox
                        "Finska (ortodoxa)"
                        "\
Namnsdagar ur någon finsk ortodox namnlängd. Eftersom utvecklarna inte
kan finska är beskrivningen på svenska, med förhoppningen att någon
som kan finska kommer att reta sig på den och skriva om texten på rätt
språk."
                        '((1 . ((1 . ("Vasili" "Vilho" "Pasi"))
                                (2 . ("Sylvester" "Tapio" "Serafim"))
                                (3 . ("Malakia" "Harto"))
                                (4 . ("Isto"))
                                (5 . ("Pentti" "Tauno" "Sylvi"))
                                (6 . ())
                                (7 . ("Johannes" "Juhani" "Juha"))
                                (8 . ("Jyrki" "Yrjö" "Tuomi" "Keijo"))
                                (9 . ("Jehto" "Ilpo" "Filip" "Vilppu" "Heimo"))
                                (10 . ("Riiko" "Marko" "Tommo" "Paavali"))
                                (11 . ("Teo" "Mikael" "Miihkali" "Mikko"))
                                (12 . ("Tatjana" "Tanja" "Taina" "Sauvo"))
                                (13 . ("Jermi" "Tarvo" "Ele"))
                                (14 . ("Aatto" "Aatos" "Benjamin" "Niina"))
                                (15 . ("Paavali" "Paavo" "Johannes" "Juhani"))
                                (16 . ("Leonilla" "Leila"))
                                (17 . ("Anttoni" "Toni"))
                                (18 . ("Ahvo" "Ohvo" "Kirilä" "Kiril"))
                                (19 . ("Makari" "Kari"))
                                (20 . ("Jehki"))
                                (21 . ("Maksima" "Visa" "Jouko" "Valeri" "Auni"))
                                (22 . ("Timo" "Anastisi" "Nasto"))
                                (23 . ("Klemens" "Klementti" "Liimo" "Oiva" "Aatos"))
                                (24 . ("Senja" "Senni" "Seni"))
                                (25 . ("Riiko" "Riku" "Reijo"))
                                (26 . ("Maria" "Marja" "Mari" "Arjo"))
                                (27 . ("Johannes" "Juhani" "Juha"))
                                (28 . ("Raimo" "Reima" "Jehri"))
                                (29 . ("Ignati" "Iknata" "Iikka"))
                                (30 . ("Vasili" "Vilho" "Riiko" "Johannes" "Ilpo"))
                                (31 . ("Kyrö" "Johannes" "Juhani" "Juha" "Nikita" "Niikko" "Ritva"))
                                ))
                          (2 . (
                                (1 . ("Triifon" "Tuija" "Tua" "Aino"))
                                (2 . ())
                                (3 . ("Simeoni" "Simana" "Hanna"))
                                (4 . ("Isko" "Jyrki" "Yrjö" "Kirilä" "Kiril"))
                                (5 . ("Helli" "Helvi" "Hellevi" "Tuula" "Tuulikki"))
                                (6 . ("Nyyrikki" "Nyri" "Terhi"))
                                (7 . ("Pertti" "Luukas"))
                                (8 . ("Feodor" "Heikki" "Sakari"))
                                (9 . ("Voitto" "Pellervo"))
                                (10 . ("Kaarle" "Valentina" "Enni" "Anna"))
                                (11 . ("Valassi" "Lassi" "Dimitri" "Miska" "Mitja" "Valto"))
                                (12 . ("Mitjo" "Aleksi" "Aleksis"))
                                (13 . ("Martti"))
                                (14 . ("Soini" "Kiril"))
                                (15 . ("Anssi" "Anto"))
                                (16 . ("Vilkko"))
                                (17 . ("Feodor" "Heikki" "Marianne" "Marjaana"))
                                (18 . ("Leonilla" "Leila"))
                                (19 . ("Arhippa" "Arhi"))
                                (20 . ("Leo" "Agaton" "Oiva"))
                                (21 . ("Timo" "Jesto"))
                                (22 . ("Mauri"))
                                (23 . ("Viljo"))
                                (24 . ("Asmo" "Osmo"))
                                (25 . ("Tarasi"))
                                (26 . ("Pirkka"))
                                (27 . ("Prokko" "Okko"))
                                (28 . ("Vasili" "Vilho" "Kassian" "Kiira"))
                                ))
                          (3 . (
                                (1 . ("Outi" "Tyyne"))
                                (2 . ("Esko" "Teuvo" "Arseni" "Arsi" "Taila"))
                                (3 . ("Otto" "Kleo" "Pasi"))
                                (4 . ("Arvo"))
                                (5 . ("Kuuno"))
                                (6 . ("Heikko" "Teuvo" "Kosti" "Kyösti" "Viljo"))
                                (7 . ("Ilmari" "Ilmo" "Toivo"))
                                (8 . ("Lasari" "Lasse" "Ahvo" "Ohvo"))
                                (9 . ("Rauli" "Valeri"))
                                (10 . ("Konni" "Galina" "Iina" "Inna"))
                                (11 . ("Roni" "Jehki"))
                                (12 . ("Teo" "Riiko" "Riku" "Reijo"))
                                (13 . ("Nikifor" "Voitto"))
                                (14 . ("Pentti"))
                                (15 . ("Armas"))
                                (16 . ("Savina" "Risto"))
                                (17 . ("Aleksi" "Patrik" "Makari" "Kari"))
                                (18 . ("Kirilä" "Kiril"))
                                (19 . ("Darja" "Tarja" "Larja"))
                                (20 . ("Tinja" "Svetlana"))
                                (21 . ("Jaakko"))
                                (22 . ("Vasili" "Vilho" "Helmi" "Rosita"))
                                (23 . ("Niko" "Lyydia" "Lyyli"))
                                (24 . ("Arto"))
                                (25 . ("Maria" "Mari" "Maija" "Marjatta" "Meeri"))
                                (26 . ("Gabriel" "Kauri" "Alli" "Larissa" "Maisa"))
                                (27 . ("Matja"))
                                (28 . ("Ilari" "Ilkka" "Teppo" "Teppana" "Tapani"))
                                (29 . ("Markus" "Markku" "Kirilä" "Kiril" "Joona" "Joonas"))
                                (30 . ("Johannes" "Juhani" "Juha"))
                                (31 . ("Ylermi"))
                                ))
                          (4 . (
                                (1 . ("Maria" "Marja" "Mari" "Jehki"))
                                (2 . ("Tiitus" "Tito"))
                                (3 . ("Nikita" "Niikko"))
                                (4 . ("Jyrki" "Yrjö" "Ossi" "Virve" "Virpi"))
                                (5 . ("Turo" "Siro" "Tuulo"))
                                (6 . ("Tyko"))
                                (7 . ("Lippo" "Jyrki" "Yrjö"))
                                (8 . ("Iiro" "Ermo"))
                                (9 . ("Aulis" "Uoti" "Risto" "Anastasia"))
                                (10 . ("Tero"))
                                (11 . ("Antippa"))
                                (12 . ("Vasili" "Vilho"))
                                (13 . ("Arto"))
                                (14 . ("Martti" "Tuomi"))
                                (15 . ("Ari" "Rohkima" "Runo"))
                                (16 . ("Irina" "Irinja" "Irja" "Rauha" "Lempi" "Leonid" "Heini" "Aliisa"))
                                (17 . ("Simeoni" "Simana" "Aake" "Sosima"))
                                (18 . ("Johannes" "Juhani" "Hannele" "Hanni"))
                                (19 . ("Johannes" "Juhani" "Juha" "Nuutti"))
                                (20 . ("Feodor" "Heikki" "Aleksanteri" "Ale"))
                                (21 . ("Jani" "Janne" "Feodor" "Heikki" "Hilppa"))
                                (22 . ("Feodor" "Heikki"))
                                (23 . ("Jyrki" "Yrjö" "Aleksandra" "Sanni" "Sanja"))
                                (24 . ("Elisabet" "Liisa" "Elisa" "Elsa" "Sauvo"))
                                (25 . ("Markus" "Markku"))
                                (26 . ("Vasili" "Vilho" "Teppo" "Teppana" "Tapani"))
                                (27 . ("Simeoni" "Simana"))
                                (28 . ("Jason" "Maksima"))
                                (29 . ("Mauno"))
                                (30 . ("Jaakko" "Ignati"))
                                ))
                          (5 . (
                                (1 . ("Jere" "Jari" "Jarkko" "Nuutti" "Tamara"))
                                (2 . ("Tamara"))
                                (3 . ("Ahvo" "Ohvo" "Saija" "Soja"))
                                (4 . ("Timo" "Maura" "Teo"))
                                (5 . ("Pelagia" "Alvi" "Alpo"))
                                (6 . ("Irene" "Irina" "Irinja" "Irja" "Rauha"))
                                (7 . ("Jopi" "Job"))
                                (8 . ("Aake"))
                                (9 . ("Johannes" "Juhani" "Arseni ,Arsi"))
                                (10 . ("Esa" "Risto" "Nikolai" "Niko" "Niilo"))
                                (11 . ("Simo" "Taisia" "Taija"))
                                (12 . ("Ilkka" "Kiril" "Kirilä" "Mefodi" "Torsti"))
                                (13 . ("Ilmo" "Herman" "Hemmo"))
                                (14 . ("Lauha" "Isko"))
                                (15 . ("Heimo" "Esa"))
                                (16 . ("Heikki" "Raimo" "Reima" "Muusa"))
                                (17 . ("Jussi" "Antero"))
                                (18 . ("Petri" "Teino" "Klaudia" "Faina" "Julia"))
                                (19 . ("Patrik" "Niila" "Dimitri" "Miska" "Mitja"))
                                (20 . ("Vesa" "Aleksi" "Asko"))
                                (21 . ("Konsta" "Helena" "Mikael" "Feodor"))
                                (22 . ("Pasi"))
                                (23 . ("Mikael" "Mikko" "Leino" "Onni"))
                                (24 . ("Simeoni" "Simana" "Niikko" "Voitto" "Kristian"))
                                (25 . ("Johannes" "Juham Juhani"))
                                (26 . ("Viljo" "Alf" "Jyrki" "Yrjö"))
                                (27 . ("Päivö" "Terho"))
                                (28 . ("Tyko" "Niikko" "Voitto" "Ignati" "Kaino"))
                                (29 . ("Heidi" "Johannes" "Juhani" "Juha"))
                                (30 . ("Iisakki"))
                                (31 . ("Erjo"))
                                ))
                          (6 . (
                                (1 . ("Usti" "Teino"))
                                (2 . ("Voitto" "Johannes" "Juhani" "Juha"))
                                (3 . ("Valo" "Paula" "Pauliina"))
                                (4 . ("Mitro"))
                                (5 . ("Terho" "Joona" "Joonas" "Igor" "Iikka"))
                                (6 . ("Ilkka" "Ilari" "Vesa" "Visa" "Joonas"))
                                (7 . ("Teuvo" "Eira" "Erja" "Valeria" "Vaula"))
                                (8 . ("Feodor" "Heikki"))
                                (9 . ("Kirilä" "Kiril"))
                                (10 . ("Timo" "Aleksanteri" "Ale" "Antoniina"))
                                (11 . ("Perttu"))
                                (12 . ("Unto" "Pietari" "Petri" "Pekka" "Arseni"))
                                (13 . ("Akuliina" "Alina" "Liina" "Ville"))
                                (14 . ("Eeli" "Torsti"))
                                (15 . ("Aamos" "Joonas" "Aukusti" "Kriska"))
                                (16 . ("Tiihon"))
                                (17 . ("Sauri" "Manuel" "Manu" "Sauli" "Ismo"))
                                (18 . ("Leino" "Onni"))
                                (19 . ("Juuda"))
                                (20 . ("Torsti"))
                                (21 . ("Uljas" "Jesse" "Harras"))
                                (22 . ("Ahri" "Harras"))
                                (23 . ("Ahri" "Aura"))
                                (24 . ("Johannes" "Juhani" "Juha" "Eero"))
                                (25 . ("Rauni" "Pietari" "Petro" "Pekka"))
                                (26 . ("Taavi" "Taavetti"))
                                (27 . ("Johanna" "Jonna" "Sampo" "Sampsa"))
                                (28 . ("Sergei" "Herman" "Kyrö" "Johannes"))
                                (29 . ("Pietari" "Petri" "Pekka" "Paavali" "Paavo" "Pauli"))
                                (30 . ("Andreas" "Simo" "Petri" "Jaakko" "Juha" "Tani" "Matti" "Tuomas"))
                                ))
                          (7 . (
                                (1 . ("Kosma" "Kuisma" "Tami"))
                                (2 . ("Maria" "Mari" "Maija" "Marja" "Marjatta" "Meeri"))
                                (3 . ("Anto" "Anatoli"))
                                (4 . ("Antti" "Antero" "Martta"))
                                (5 . ("Ahvo" "Ohvo" "Leimu" "Sergei"))
                                (6 . ("Sisso"))
                                (7 . ("Kiia" "Tuomas" "Tuomo"))
                                (8 . ("Prokko" "Okko"))
                                (9 . ("Valto"))
                                (10 . ("Anton" "Anttoni" "Toni"))
                                (11 . ("Jefimia" "Olga" "Oilo" "Helga"))
                                (12 . ("Kauko" "Ilari" "Mikael" "Veronika"))
                                (13 . ("Kauri" "Teppo" "Teppana" "Tapani"))
                                (14 . ("Aki" "Eeli"))
                                (15 . ("Kerkko" "Jutta" "Vladimir"))
                                (16 . ("Teino" "Aila" "Aili"))
                                (17 . ("Marina" "Marketta" "Maarit" "Erho"))
                                (18 . ("Eemeli" "Eemil" "Into"))
                                (19 . ("Riina" "Teijo" "Serafim"))
                                (20 . ("Elias" "Eelis" "Ilja"))
                                (21 . ("Maria" "Johanne" "Juhani" "Juha" "Simeoni" "Simana"))
                                (22 . ("Maria" "Magdaleena"))
                                (23 . ("Rohkima" "Runo" "Viljo" "Tuukka"))
                                (24 . ("Kristiina" "Kirsti" "Kirsi" "Tiina" "Boris"))
                                (25 . ("Anna" "Anni" "Piia" "Ruusa"))
                                (26 . ("Jorma" "Jarmo" "Paraske" "Eeva"))
                                (27 . ("Leimo" "Pauno"))
                                (28 . ("Timo"))
                                (29 . ("Tea" "Teija" "Serafiima" "Fiina"))
                                (30 . ("Silvo" "Herman" "Hemmo" "Valentin"))
                                (31 . ("Kim" "Kimmo"))
                                ))
                          (8 . (
                                (1 . ("Soile" "Soili" "Ele"))
                                (2 . ("Stefna" "Teemu" "Vasili" "Vilho" "Ville"))
                                (3 . ("Iisakki" "Anton" "Salome" "Salme"))
                                (4 . ("Maksim" "Kustaa" "Martti" "Teino"))
                                (5 . ("Joutsi" "Nonna" "Nanna"))
                                (6 . ())
                                (7 . ("Tarmo"))
                                (8 . ("Eemeli" "Eemil"))
                                (9 . ("Matias" "Matti" "Herman" "Hemmo"))
                                (10 . ("Lauri"))
                                (11 . ("Susanna" "Sanna"))
                                (12 . ("Huoti"))
                                (13 . ("Maksima" "Tiihon" "Noora" "Tiia"))
                                (14 . ("Miika"))
                                (15 . ("Maria" "Mari" "Maija" "Marja" "Marjatta" "Meeri"))
                                (16 . ("Toimi"))
                                (17 . ("Miro" "Heikki" "Teuvo"))
                                (18 . ("Lauri"))
                                (19 . ("Antti" "Antero"))
                                (20 . ("Samuel" "Samuli"))
                                (21 . ("Tatu" "Assi" "Usko" "Avrami" "Aapo"))
                                (22 . ("Ake" "Laila"))
                                (23 . ("Reino"))
                                (24 . ("Tyko" "Siiri" "Sirkka"))
                                (25 . ("Perttu" "Tiitus" "Tito"))
                                (26 . ("Atro" "Natalia"))
                                (27 . ("Pimen" "Piminä"))
                                (28 . ("Mooses"))
                                (29 . ("Hannes" "Hannu" "Iivo" "Juha" "Juhana"))
                                (30 . ("Ale" "Aleksander" "Paavo" "Pauli"))
                                (31 . ("Kiprian"))
                                ))
                          (9 . (
                                (1 . ("Simeoni" "Simana" "Joosua"))
                                (2 . ("Johannes" "Juhani" "Veli" "Veikko"))
                                (3 . ("Anto" "Isto" "Ari" "Viici"))
                                (4 . ("Mooses" "Irma" "Irmeli"))
                                (5 . ("Sakari" "Elisabet" "Liisa" "Elisa" "Elsa"))
                                (6 . ("Raisa" "Raija"))
                                (7 . ("Johannes" "Juhani" "Juha"))
                                (8 . ("Maria" "Mari" "Maija" "Marjatta" "Meeri"))
                                (9 . ("Joakim" "Aki" "Anna" "Severi" "Ossi"))
                                (10 . ("Minna" "Miina" "Tuire"))
                                (11 . ("Lahja" "Sergei" "Herman" "Hemmo" "Iia"))
                                (12 . ("Ahti"))
                                (13 . ("Niila" "Vieno"))
                                (14 . ())
                                (15 . ("Nikita" "Voitto"))
                                (16 . ("Jefimia" "Milla" "Milja"))
                                (17 . ("Veera" "Elpi" "Elvi" "Lempi" "Sofia" "Sonja"))
                                (18 . ("Arja" "Aira" "Airi"))
                                (19 . ("Tuure" "Feodor" "Heikki" "Taavetti" "Taavi" "Konsta" "Kosti"))
                                (20 . ("Jesto" "Mikael" "Mikko" "Olli" "Helge"))
                                (21 . ("Dimitri" "Miska" "Mitja"))
                                (22 . ("Tuukka" "Joona" "Joonas"))
                                (23 . ("Iira" "Raita"))
                                (24 . ("Tekla" "Silvo"))
                                (25 . ("Opri" "Sergei"))
                                (26 . ("Johannes" "Juhani" "Juha"))
                                (27 . ("Kaari"))
                                (28 . ("Hariton" "Harri"))
                                (29 . ("Kyrö"))
                                (30 . ("Riiki" "Riku" "Reijo" "Kaija"))
                                ))
                          (10 . (
                                 (1 . ("Romana" "Sauvo"))
                                 (2 . ("Kiprian" "Stiina" "Antti" "Antero"))
                                 (3 . ("Teino"))
                                 (4 . ("Guri"))
                                 (5 . ("Karita" "Pietari" "Petri" "Pekka" "Aleksi" "Joona" "Joonas" "Ilpo"))
                                 (6 . ("Tuomas" "Tuomo"))
                                 (7 . ("Sergei" "Vaka"))
                                 (8 . ("Pelagia" "Laina"))
                                 (9 . ("Jaakko" "Antero" "Hannele" "Hanni"))
                                 (10 . ("Oula" "Eila"))
                                 (11 . ("Ilpo" "Teo" "Siina"))
                                 (12 . ("Antero" "Kosma" "Kuisma"))
                                 (13 . ("Viljo" "Armi"))
                                 (14 . ("Nasari" "Paraske" "Eeva"))
                                 (15 . ("Jehki"))
                                 (16 . ("Heino"))
                                 (17 . ("Antti" "Antero"))
                                 (18 . ("Luukas"))
                                 (19 . ("Joel"))
                                 (20 . ("Artemi" "Arttu" "Arvo"))
                                 (21 . ("Iiari" "Ilkka"))
                                 (22 . ("Erkki"))
                                 (23 . ("Jaakko"))
                                 (24 . ("Aarre"))
                                 (25 . ("Marko"))
                                 (26 . ("Dimitri" "Miska" "Mitja"))
                                 (27 . ("Nestori"))
                                 (28 . ("Tero" "Neonilla" "Teppo" "Teppana" "Tapani" "Arseni" "Paraske" "Eeva"))
                                 (29 . ("Anastasia" "Asta" "Aapo" "Rostislav"))
                                 (30 . ("Veijo" "Vilja"))
                                 (31 . ("Urpo" "Panu" "Taisto"))
                                 ))
                          (11 . (
                                 (1 . ("Kosma" "Kuisma" "Taimi"))
                                 (2 . ("Ahto" "Ohto"))
                                 (3 . ("Ossi"))
                                 (4 . ("Joni" "Jonne" "Kauno"))
                                 (5 . ("Joona" "Joonas" "Kai"))
                                 (6 . ("Paavali" "Paavo" "Pauli"))
                                 (7 . ("Lasari" "Lasse" "Kaisa"))
                                 (8 . ("Mikael" "Mika" "Miihkali" "Mikko" "Raafael"))
                                 (9 . ("Pirkka" "Matja" "Eija" "Eini"))
                                 (10 . ("Iiro" "Ere" "Martti"))
                                 (11 . ("Viktor" "Iida" "Feodor" "Heikki"))
                                 (12 . ("Johannes" "Juhani" "Juha" "Niilo"))
                                 (13 . ("Johannes" "Juhani" "Juha"))
                                 (14 . ("Ilpo"))
                                 (15 . ("Guri"))
                                 (16 . ("Matti"))
                                 (17 . ("Riiki" "Riku" "Reijo"))
                                 (18 . ("Rauno" "Romana"))
                                 (19 . ("Päiviö"))
                                 (20 . ("Riiko" "Riku" "Reijo" "Kauko"))
                                 (21 . ("Maria" "Maija" "Marjatta"))
                                 (22 . ("Silja" "Mikael" "Miihkali" "Mikko"))
                                 (23 . ("Riiko" "Riku" "Aleksanteri" "Mitro"))
                                 (24 . ("Katariina" "Katja"))
                                 (25 . ("Liimo" "Pekka" "Petri" "Katariina" "Katja"))
                                 (26 . ("Jyrki" "Yrjö"))
                                 (27 . ("Jaakko"))
                                 (28 . ("Teppo" "Teppana" "Tapani" "Arho"))
                                 (29 . ("Aake"))
                                 (30 . ("Antti" "Antero"))
                                 ))
                          (12 . (
                                 (1 . ("Uuno"))
                                 (2 . ("Auvo" "Mira" "Mirka" "Salomo"))
                                 (3 . ("Sauvo"))
                                 (4 . ("Varvara" "Johannes" "Aleksanteri"))
                                 (5 . ("Sauvo"))
                                 (6 . ("Nikolai" "Niko" "Niilo"))
                                 (7 . ("Ambrosius" "Anton" "Anttoni" "Toni"))
                                 (8 . ("Tapio" "Kirilä" "Kiril"))
                                 (9 . ("Anna" "Anni" "Anja" "Anneli" "Anita"))
                                 (10 . ("Minkka" "Kirmo"))
                                 (11 . ("Taneli"))
                                 (12 . ("Piri" "Pirkka"))
                                 (13 . ("Lusia" "Seija"))
                                 (14 . ("Aari"))
                                 (15 . ("Paavo" "Pauli" "Teppo" "Tapani" "Triifon"))
                                 (16 . ("Fanni"))
                                 (17 . ("Taneli"))
                                 (18 . ("Seppo"))
                                 (19 . ("Vaito"))
                                 (20 . ("Ignatios" "Juha"))
                                 (21 . ("Jaana" "Pietari" "Petri" "Pekka"))
                                 (22 . ("Anastasia" "Nasti" "Asta"))
                                 (23 . ("Tuulo"))
                                 (24 . ("Jenni"))
                                 (25 . ("Immanuel"))
                                 (26 . ("Jehki"))
                                 (27 . ("Joosef" "Daavid" "Jaakko" "Teppo"))
                                 (28 . ("Domna" "Vilja"))
                                 (29 . ("Markko"))
                                 (30 . ("Ani"))
                                 (31 . ("Maila"))
                                 ))))



(defun kom-list-nameday-lists ()
  (interactive)
  (with-output-to-temp-buffer "*Help*"
    (princ
     (mapconcat 
      (lambda (x)
        (format "%s -- %s\n%s\n\n"
                (nameday-data->code (cdr x))
                (nameday-data->name (cdr x))
                (nameday-data->description (cdr x))))
      lyskom-namedays
      ""))))

;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: commands1.el
;;;;
;;;; This file contains the code for some of the high level commands.
;;;;


(eval-when-compile
  (require 'lyskom-command "command"))


;;; ================================================================
;;;                  F} uppmuntran - Get appreciation 

;;; Author: Inge Wallin


(def-kom-command kom-get-appreciation ()
  "Provides a little light in the dark."
  (interactive)
  (lyskom-insert-string 'appreciation))
  

;;; ================================================================
;;;                      F} Sk{ll - Get abuse

;;; Author: Inge Wallin


(def-kom-command kom-get-abuse ()
  "Provides a bit of verbal abuse."
  (interactive)
  (lyskom-insert-string 'abuse))


;;; ================================================================
;;;            Utpl}na - Delete a person or a conference

;;; Author: Inge Wallin


(def-kom-command kom-delete-conf ()
  "Deletes a person or a conference. In order to delete a conference
you must be the supervisor of the conference. Once a conference is
deleted, it can never be restored, and texts with no recipients other
than the deleted conference will become inaccessible, and will
eventually be permanently deleted."
  (interactive)
  (let ((conf-stat 
	 (lyskom-read-conf-stat 'what-conf-to-delete '(all) nil nil t)))
    (if conf-stat
	(if (lyskom-ja-or-nej-p
	     (lyskom-format 'confirm-delete-pers-or-conf
			    (if (conf-type->letterbox (conf-stat->conf-type 
						       conf-stat))
				(lyskom-get-string 'the-pers)
			      (lyskom-get-string 'the-conf))
			    (conf-stat->name conf-stat)))
	    (if (blocking-do 'delete-conf
			     (conf-stat->conf-no conf-stat))
		(progn
		  (lyskom-format-insert 'conf-is-deleted
					(conf-stat->name conf-stat))
		  (when (= (conf-stat->conf-no conf-stat)
			   lyskom-pers-no)
		    (lyskom-insert (lyskom-get-string
				    'you-have-deleted-yourself))
                    (lyskom-init-membership)
		    (setq lyskom-pers-no nil
			  lyskom-to-do-list (lyskom-create-read-list)
			  lyskom-reading-list (lyskom-create-read-list)
			  lyskom-pending-commands (cons
						   'kom-start-anew
						   lyskom-pending-commands))))
	      (lyskom-format-insert 'you-could-not-delete
				    conf-stat))
	  (lyskom-insert-string 'deletion-not-confirmed))
      (lyskom-insert-string 'somebody-else-deleted-that-conf))))


;;; ================================================================
;;;                Radera (text) - Delete a text

;;; Author: Inge Wallin


(def-kom-command kom-delete-text (text-no)
  "Permanently deletes a text. In order to delete a text you must
be the author of the text, or the supervisor of the author.

It is a very bad idea to delete texts that have comments. By deleting
a text with comments, the comments lost their context. This can lead
to fragmented discussion trees, where it is impossible to understand
what a comment means, or indeed what the whole discussion is about.

Instead of deleting a text, consider moving it to a conference created
specifically for storing texts that are no longer interesting. Several
LysKOM systems have such conferences, so ask around.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-text-to-delete)))
  (if text-no
      (let* ((text-stat (blocking-do 'get-text-stat text-no))
             (num-marks (and text-stat (text-stat->no-of-marks text-stat)))
             (num-comments (and text-stat (length (lyskom-text-comments text-stat))))
             (is-marked-by-me (cache-text-is-marked text-no)))

        (if (null text-stat)
            (lyskom-report-command-answer nil)
          (when (and (or (eq 0 num-marks)
                         (lyskom-j-or-n-p 
                          (lyskom-format 'delete-marked-text
                                         (if (> num-marks 0)
                                             (if is-marked-by-me
                                                 (if (= num-marks 1)
                                                     (lyskom-get-string 'delete-marked-by-you)
                                                   (lyskom-format 'delete-marked-by-you-and-others
                                                                  (1- num-marks)))
                                               (lyskom-format 'delete-marked-by-several
                                                              num-marks))))))
                     (or (eq 0 num-comments)
                         (progn (lyskom-format-insert "%#1@%#2t\n" 
                                                      `(face ,kom-warning-face)
                                                      (lyskom-get-string 'delete-commented-text-help))
                                (lyskom-beep t)
                                (and (lyskom-j-or-n-p
                                      (lyskom-get-string 'delete-commented-text))
                                     (lyskom-j-or-n-p
                                      (lyskom-get-string 'really-delete-commented-text))))))
            (lyskom-format-insert 'deleting-text text-no)
            (when (lyskom-report-command-answer 
                   (blocking-do 'delete-text text-no))
              (when is-marked-by-me
                (lyskom-unmark-text text-no))))))
    (lyskom-insert 'confusion-what-to-delete)))



;;; ================================================================
;;;        ]terse presentation - Review the presentation
;;;               for a person or a conference

;;; Author: Inge Wallin


(def-kom-command kom-review-presentation (&optional text-or-conf-no)
  "Reviews the presentation for a person or a conference. If you give
a prefix argument it is treated as a text number prefix argument (see
`lyskom-read-text-no-prefix-arg'), and this command shows the
presentation of the author of that text.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (and current-prefix-arg ; only peek at textno:s when prefixed!
		    (list (lyskom-read-text-no-prefix-arg
			   'text-to-see-author-of))))
  (let ((conf-stat
         (if text-or-conf-no
             (blocking-do 'get-conf-stat
                          (if (lyskom-called-interactively-p 'any)
                              (text-stat->author
                               (blocking-do 'get-text-stat text-or-conf-no))
                            text-or-conf-no))
           (lyskom-read-conf-stat 'presentation-for-whom '(all)
                                  nil nil t))))
    (lyskom-review-presentation conf-stat)))

(def-kom-command kom-unread-presentation (&optional text-or-conf-no)
  "Marks the presentation for a person or a conference as unread. If
you give a prefix argument it is treated as a text number prefix
argument (see `lyskom-read-text-no-prefix-arg'), and this command
unreads the presentation of the author of that text.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (and current-prefix-arg ; only peek at textno:s when prefixed!
		    (list (lyskom-read-text-no-prefix-arg
			   'text-to-unread-author-of))))
  (let ((conf-stat
         (if text-or-conf-no
             (blocking-do 'get-conf-stat
                          (if (lyskom-called-interactively-p 'any)
                              (text-stat->author
                               (blocking-do 'get-text-stat text-or-conf-no))
                            text-or-conf-no))
           (lyskom-read-conf-stat 'unread-presentation-for-whom
                                  '(all) nil nil t))))
    (if (zerop (conf-stat->presentation conf-stat))
        (lyskom-format-insert 'has-no-presentation conf-stat)
      (lyskom-format-insert 'marking-text-unread
                            (conf-stat->presentation conf-stat))
      (lyskom-report-command-answer
       (lyskom-mark-unread (conf-stat->presentation conf-stat))))))

(defun lyskom-review-presentation (conf-stat)
  "Review the presentation of conference CONF-STAT."
  (if (null conf-stat)
      (lyskom-insert-string 'somebody-deleted-that-conf)
    (lyskom-format-insert 'review-presentation-of
                          conf-stat)
    (if (/= (conf-stat->presentation conf-stat) 0)
        (lyskom-view-text (conf-stat->presentation conf-stat))
      (lyskom-format-insert 'has-no-presentation
                            conf-stat))
    (lyskom-traverse faq (lyskom-get-aux-item 
                          (conf-stat->aux-items conf-stat)
                          14)
      (lyskom-print-comment-like-aux faq conf-stat))))  

(defun lyskom-print-comment-like-aux (item object)
  (when (lyskom-aux-item-validate (aux-item->data item) 'lyskom-string-to-int)
    (let* ((text-no (lyskom-string-to-number (aux-item->data item)))
           (text-stat nil)
           (text nil))
      (unless kom-deferred-printing
        (blocking-do-multiple ((x-text (get-text text-no))
                               (x-text-stat (get-text-stat text-no)))
          (setq text-stat x-text-stat text x-text)))

      (cond ((or text-stat (not kom-deferred-printing))
             (lyskom-insert-comment-like-aux item text-no text-stat text object))
            (t (let ((defer-info (lyskom-create-defer-info
                                  'get-text-stat
                                  text-no
                                  'lyskom-insert-deferred-comment-like-aux
                                  (point-max-marker)
                                  (length lyskom-defer-indicator)
                                  nil   ; Filled in later
                                  (list item object text-no))))
                 (lyskom-format-insert "%#1s\n" lyskom-defer-indicator)
                 (lyskom-defer-insertion defer-info)))))))

(defun lyskom-insert-comment-like-aux (item text-no text-stat text object)
  (let* ((author (if text-stat (text-stat->author text-stat) nil))
         (mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat)
                                            17)))
         (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat)
                                              16)))
         (formats (lyskom-aux-item-definition-field item 'text-header-line))
         (subject (lyskom-print-faq-format-subject text text-stat text-no))
         content-type
         )
    (if (and mx-from
             (setq content-type
                   (car (lyskom-get-aux-item (text-stat->aux-items 
                                              text-stat) 1))))
        (progn (string-match "^\\(\\S-+\\)" (aux-item->data content-type))
               (setq content-type (format "(%s) "
                                          (aux-item->data content-type))))
      (setq content-type ""))
    (setq author (or (lyskom-format-mx-author mx-from mx-author) author))

    (lyskom-format-insert (cond ((not (listp formats)) formats)
                                ((<= (length formats) 1) (car formats))
                                (author (elt formats 1))
                                (t (elt formats 0)))
                          text-no
                          author
                          content-type
                          (lyskom-aux-item-terminating-button item
                                                              object)
                          subject)
    (lyskom-insert "\n")))

;;; FIXME: This contains code that duplicates the code in 
;;; lyskom-insert-deferred-header-comm. That's a BAD THING.

(defun lyskom-insert-deferred-comment-like-aux (text-stat defer-info)
  (initiate-get-text 'deferred
                     'lyskom-insert-deferred-comment-like-aux-2
                     (elt (defer-info->data defer-info) 2)
                     text-stat
                     defer-info))

(defun lyskom-insert-deferred-comment-like-aux-2 (text text-stat defer-info)
  (let* ((author (if text-stat (text-stat->author text-stat) nil))
         (item (elt (defer-info->data defer-info) 0))
         (object (elt (defer-info->data defer-info) 1))
         (text-no (elt (defer-info->data defer-info) 2))
         (mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat)
                                            17)))
         (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat)
                                              16)))
         (formats (lyskom-aux-item-definition-field item 'text-header-line))
         (subject (lyskom-print-faq-format-subject text text-stat text-no))
         content-type
         )
    (if (and mx-from
             (setq content-type
                   (car (lyskom-get-aux-item (text-stat->aux-items 
                                              text-stat) 1))))
        (progn (string-match "^\\(\\S-+\\)" (aux-item->data content-type))
               (setq content-type (format "(%s) "
                                          (aux-item->data content-type))))
      (setq content-type ""))
    (setq author (or (lyskom-format-mx-author mx-from mx-author) author))

    (set-defer-info->format defer-info 
                            (cond ((not (listp formats)) formats)
                                  ((<= (length formats) 1) (car formats))
                                  (author (elt formats 1))
                                  (t (elt formats 0))))
    (lyskom-replace-deferred defer-info
                             text-no
                             author
                             content-type
                             (lyskom-aux-item-terminating-button item
                                                                 object)
                             subject)))






;;; ================================================================
;;;          Återse det kommenterade - View commented text

;;; Author: Inge Wallin
;;; Modified by: David Kågedal, Johan Sundström

(def-kom-command kom-view-commented-text (text-no)
  "Views the text that the selected text is a comment to. If the
current text is comment or footnote to several texts then the first
text is shown and the remaining texts are reviewed.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg').

See `kom-review-uses-cache'."
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-commented-q)))
  (if (not text-no)
      (lyskom-insert-string 'confusion-what-to-view)      
    (lyskom-tell-internat 'kom-tell-read)
    (unless kom-review-uses-cache
      (cache-del-text-stat text-no))
    (let ((kom-show-footnotes-immediately nil))
      (lyskom-view-commented-text
       (blocking-do 'get-text-stat text-no)))))

(def-kom-command kom-unread-commented-text (text-no)
  "MArkes the texts that the selected text is a comment to as unread.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'unread-commented-q)))
  (if text-no
      (lyskom-unread-commented-text 
       (blocking-do 'get-text-stat text-no))
    (lyskom-insert-string 'confusion-what-to-mark-unread)))


(def-kom-command kom-view-previous-commented-text (text-no)
  "Views the text that the selected text is a comment to. If the
current text is comment or footnote to several texts then the first
text is shown and the remaining texts are reviewed.

Without a prefix argument this will display the text that the
next-to-last text in the buffer is a comment to. With a prefix
argument this command is identical to `kom-view-commented-text'.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')

See `kom-review-uses-cache'."
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-commented-q)))
  (cond (text-no
         (lyskom-tell-internat 'kom-tell-read)
        (unless kom-review-uses-cache
          (cache-del-text-stat text-no))
         (lyskom-view-commented-text
          (blocking-do 'get-text-stat text-no)))
        (t (lyskom-insert-string 'confusion-what-to-view))))


(def-kom-command kom-unread-previous-commented-text (text-no)
  "Marks the texts that the selected text is a comment to as unread.

Without a prefix argument this will display the text that the
next-to-last text in the buffer is a comment to. With a prefix
argument this command is identical to `kom-unread-commented-text'.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')"
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-commented-q)))
  (if text-no
      (lyskom-unread-commented-text
       (blocking-do 'get-text-stat text-no))
    (lyskom-insert-string 'confusion-what-to-mark-unread)))


(defun lyskom-text-stat-commented-texts (text-stat &optional type)
  "Return a list of the text-nos that TEXT-STAT is a comment or footnote to.
If optional TYPE is non-nil, only return parents of that type."
  (cond ((null type) (setq type '(COMM-TO FOOTN-TO)))
	((atom type) (setq type (list type))))
  (delq nil (mapcar
	     (lambda (misc)
	       (when (memq (misc-info->type misc) type)
		 (cond ((eq (misc-info->type misc) 'COMM-TO) 
			(misc-info->comm-to misc))
		       ((eq (misc-info->type misc) 'FOOTN-TO)
			(misc-info->footn-to misc)))))
	     (text-stat->misc-info-list text-stat))))


(defun lyskom-view-commented-text (text-stat)
  "Handles the return from the initiate-get-text-stat, displays and builds list."
  (let ((text-nos (lyskom-text-stat-commented-texts text-stat)))
    (if text-nos
        (progn
          (lyskom-format-insert 'review-text-no 
                                (car text-nos))
          (if (cdr text-nos)
              (read-list-enter-read-info
               (lyskom-create-read-info
                'REVIEW nil (lyskom-review-get-priority)
                (lyskom-create-text-list (cdr text-nos))
                lyskom-current-text)
               lyskom-reading-list t))
          (unless kom-review-uses-cache
            (cache-del-text-stat (car text-nos)))
          (lyskom-view-text (car text-nos)
			    nil nil nil 
			    nil nil nil
			    t))
      (lyskom-insert-string 'no-comment-to))))

(defun lyskom-unread-commented-text (text-stat)
  "Marks the texts that TEXT-STAT is a comment to as unread."
  (let ((text-nos (lyskom-text-stat-commented-texts text-stat)))
    (if text-nos
        (lyskom-traverse text-no text-nos
          (lyskom-format-insert 'marking-text-unread text-no)
          (lyskom-report-command-answer (lyskom-mark-unread text-no)))
      (lyskom-insert-string 'no-comment-to-to-unread))))


(defun lyskom-misc-infos-from-list (type list)
  "Get all the misc-infos from the misc-info-list LIST with the same type
as TYPE. If no such misc-info, return NIL"
  (let ((result nil))
    (while list
      (when (eq type (misc-info->type (car list)))
        (setq result (cons (car list) result)))
      (setq list (cdr list)))
    (nreverse result)))


;;; ================================================================
;;;                         Brev - Send letter
;;; Author: Inge Wallin

(def-kom-command kom-send-letter (&optional pers-no)
  "Send a personal letter to a person. This command is similar to
`kom-write-text', but intended specifically for sending personal
messages.

Several settings affect writing texts in general.
`kom-write-texts-in-window' controls which window is used to write the
comment; `kom-confirm-multiple-recipients' affects how multiple
recipients are handled."
  (interactive)
  (lyskom-tell-internat 'kom-tell-write-letter)
  (lyskom-write-text pers-no 'who-letter-to t))


;;; ================================================================
;;;           Bli medlem i m|te - Become a member of a conference
;;;             Addera medlem - Add somebody else as a member

;;; Author: David Byers, David Kågedal

(def-kom-command kom-add-member ()
  "Invite a person as a member of a conference. Depending on the
server configuration this command may either create an invitation or
simply make the person a member of the conference without asking them
for confirmation."
  (interactive)
  (lyskom-add-member))

(def-kom-command kom-add-self (&optional conf)
  "Become a member of a conference. The default priority of the
membership is determined by `kom-membership-default-priority' and its
placement by `kom-membership-default-placement' You can also use this
command to change the priority of an existing membership and to
re-join a conference of which you are a passive member.

See `kom-membership-default-priority' and 
`kom-membership-default-placement'"
  (interactive)
  (lyskom-add-member (blocking-do 'get-conf-stat lyskom-pers-no)
                     conf))


(def-kom-command kom-change-priority (&optional conf)
  "Changes the priority of a single membership. If you want to change
the priority of several memberships, use `kom-prioritize' instead."
  (interactive)
  (let* ((conf-stat (if conf 
                        (blocking-do 'get-conf-stat conf)
                      (lyskom-read-conf-stat  'change-priority-for-q
                                              '(all) nil nil t)))
         (mship (and conf-stat (lyskom-get-membership (conf-stat->conf-no conf-stat) t))))
    (cond 
     ((null conf-stat) (lyskom-format-insert 'no-such-conf))
     ((null mship) (lyskom-format-insert 'not-member-of-conf conf-stat))
     ((membership-type->passive (membership->type mship))
      (lyskom-format-insert 'cant-change-priority-of-passive))
     (t (lyskom-add-member (blocking-do 'get-conf-stat lyskom-pers-no)
                           conf-stat
                           t
                           t
                           (list (membership->priority mship))
                           nil
                           t
                           'change-priority-for)))))


(defun lyskom-add-member-by-no (conf-no pers-no &optional no-of-unread mship-type thendo &rest data)
  "Fetch info to be able to add a person to a conf.
Get the conf-stat CONF-NO for the conference and the conf-stat and pers-stat 
for person PERS-NO and send them into lyskom-add-member."
  (blocking-do-multiple ((whereto (get-conf-stat conf-no))
                         (who (get-conf-stat pers-no)))
    (prog1 
        (lyskom-add-member who whereto mship-type no-of-unread)
      (and thendo (apply thendo data)))))


(defun lyskom-add-member (&optional pers-conf-stat conf-conf-stat
                                    mship-type no-of-unread priority 
                                    position force message-string)
  "Add PERS-CONF-STAT to CONF-CONF-STAT. 
Returns non-nil if person if person is a member of the conference
after this call has comleted.

Optional MSHIP-TYPE is the membership type to use. If it is t, keep
the old membership type (if one exists).

Optional NO-OF-UNREAD specifies how many unread texts to leave in 
the conference. If set to t, don't change the number of unread.

Optional PRIORITY is the priority of the membership. If it is a list,
this function will prompt with the car of the list as the default
priority.

Optional POSITION is the position of the new membership.

If optional FORCE is non-nil, add even if the user is already a
member of the conference.

If optional MESSAGE-STRING is non-nil, use it as the prompt
when adding the membership.

This function will prompt interactively for any information not
specified when calling it. This function blocks, so it may not
be called from a callback."
  (let* ((who (or pers-conf-stat (lyskom-read-conf-stat 'who-to-add '(pers) nil nil t)))
         (pers-stat (blocking-do 'get-pers-stat (conf-stat->conf-no who)))
         (self (eq (conf-stat->conf-no who) lyskom-pers-no))
         (whereto (or conf-conf-stat (lyskom-read-conf-stat (if self 
                                                                'where-to-add-self
                                                              'where-to-add) '(all) nil nil t)))
         (mship (lyskom-is-member (conf-stat->conf-no whereto) (conf-stat->conf-no who)))
         (mship-type (if (and (eq mship-type t) mship)
                         (membership->type mship)
                       mship-type))
         (became-member nil))

    (cond

     ;; Already a member
     ((and mship 
           (not (and self
                     (or force
                         (membership-type->passive (membership->type mship))
                         (membership-type->invitation (membership->type mship))))))
      (lyskom-format-insert (if self 'you-already-member 'add-already-member)
                            whereto who)
      (setq became-member t))


     ;; We're already passive members, so depassivate
     ((and mship self
           (membership-type->passive (membership->type mship)))
      (set-membership-type->passive (membership->type mship) nil)
      (set-membership-type->message-flag (membership->type mship) t)
      (lyskom-format-insert 'activate-mship whereto)
      (let ((res (blocking-do 'set-membership-type
                              lyskom-pers-no
                              (conf-stat->conf-no whereto)
                              (membership->type mship))))
        (lyskom-report-command-answer res)
        (when res
          (setq became-member t)
          (lyskom-format-insert 'activate-mship-done whereto)
          (lp--update-buffer (membership->conf-no mship))
          (lyskom-fetch-start-of-map whereto mship))))


     ;; We be adding someone for real
     (t (let* ((no-of-unread (cond ((eq no-of-unread t) nil)
                                   (no-of-unread)
                                   (t (when (and self
                                                 (not (membership-type->passive
                                                       (membership->type mship))))
                                        (lyskom-read-num-range-or-date 
                                         0 
                                         (conf-stat->no-of-texts whereto)
                                         (lyskom-format 'initial-unread)
                                         nil
                                         t
                                         nil)))))
               (priority (cond ((numberp priority) priority)
                               ((not self)
                                (lyskom-read-num-range 0 255 (lyskom-get-string 'other-priority-q) nil 100))
                               ((and (null priority)
                                     (numberp kom-membership-default-priority)
                                     (< kom-membership-default-priority 256)
                                     (>= kom-membership-default-priority 0))
                                kom-membership-default-priority)
                               (t (lyskom-read-num-range 0 255
                                                         (lyskom-get-string 'priority-q)
                                                         nil 
                                                         (cond (mship (membership->priority mship))
                                                               ((listp priority) (car priority)))))))
               (position (or position 
                             (cond ((not self) 1)
                                   ((and (numberp kom-membership-default-placement)
                                         (>= kom-membership-default-placement 0))
                                    kom-membership-default-placement)
                                   ((eq kom-membership-default-placement 'first) 0)
                                   ((eq kom-membership-default-placement 'last) (lyskom-membership-length))
                                   (t (lyskom-read-num-range
                                       0 (pers-stat->no-of-confs pers-stat)
                                       (lyskom-format 'where-on-list-q
                                                      (lyskom-membership-length)))))))
               (message-flag (or (when mship-type
				   (membership-type->message-flag mship-type))
				 (when (eq 'ask kom-membership-default-message-flag)
				   (lyskom-j-or-n-p (lyskom-format 'set-message-flag-q whereto)))
				 kom-membership-default-message-flag))

               (mship-type (or mship-type
                               (lyskom-create-membership-type (not self) nil nil message-flag
                                                              nil nil nil nil)))

               (no-of-unread-string (cond ((null no-of-unread) nil)
                                          ((numberp no-of-unread)
                                           (lyskom-format 'member-in-conf-with-unread no-of-unread))
                                          ((listp no-of-unread)
                                           (lyskom-format 'member-in-conf-with-unread-date
                                                          (elt no-of-unread 0)
                                                          (car (rassq (elt no-of-unread 1)
                                                                      lyskom-month-names))
                                                          (elt no-of-unread 2)))))
               )

          ;; Now add the member; we've got all the information we need

          ;; Adding ourselves. Adjust where so the membership
          ;; list remains sorted. Find the closest position to
          ;; where at which we can put the membership and keep
          ;; the membership lsit sorted.

          (when (eq lyskom-pers-no (conf-stat->conf-no who))
            (setq position (lyskom-query-membership-position
                            priority position)))


          ;; Print the prompt

          (lyskom-format-insert (cond (message-string)
                                      ((and self mship
                                            (membership-type->invitation
                                             (membership->type mship)))
                                       'accepting-invitation)
                                      (self 'member-in-conf)
                                      (t 'add-member-in))
                                who whereto no-of-unread-string)

          ;; Do the add thing

          (lyskom-ignoring-async (18 lyskom-pers-no (conf-stat->conf-no whereto))
            (let ((res (blocking-do 'add-member
                                    (conf-stat->conf-no whereto)
                                    (conf-stat->conf-no who)
                                    priority position mship-type)))

              (cond 

               ;; Failure 

               ((null res) 
                (lyskom-insert-string 'nope)
                (let* ((errno lyskom-errno)
                       (err-stat lyskom-err-stat)
                       (is-supervisor (lyskom-is-supervisor (conf-stat->conf-no whereto)
                                                            lyskom-pers-no))
                       (is-member (lyskom-is-member (conf-stat->conf-no whereto)
                                                    (conf-stat->conf-no who)))
                       (rd-prot (conf-type->rd_prot (conf-stat->conf-type whereto))))


                  (cond (is-member
                         (lyskom-format-insert 'add-already-member who whereto))
                        ((and rd-prot is-supervisor)
                         (lyskom-insert-error errno err-stat))
                        (rd-prot (let ((supervisorconf (blocking-do
                                                        'get-conf-stat
                                                        (conf-stat->supervisor whereto))))
                                   (if supervisorconf
                                       (lyskom-format-insert 'is-read-protected-contact-supervisor
                                                             whereto
                                                             supervisorconf)
                                     (lyskom-format-insert 'cant-find-supervisor
                                                           whereto))))
                        (t (lyskom-insert-error errno err-stat)))))

               (t
                (setq became-member t)

                ;; If we're re-adding a membership, then we need to set the
                ;; membership type explicity since add-member won't

                (when (and self mship)
                  (blocking-do 'set-membership-type
                               (conf-stat->conf-no who)
                               (conf-stat->conf-no whereto)
                               mship-type))

                (cache-del-pers-stat (conf-stat->conf-no who))
                (cache-del-conf-stat (conf-stat->conf-no whereto))

                (when self
                  (when no-of-unread
                    (cond ((listp no-of-unread)
                           (let* ((target-date (lyskom-create-time 0 0 0 (elt no-of-unread 2) (elt no-of-unread 1) (elt no-of-unread 0) 0 0 nil))
                                  (text (lyskom-find-text-by-date whereto target-date)))
                             (when text
                               (blocking-do 'set-last-read 
                                            (conf-stat->conf-no whereto)
                                            (car text)))))
                          ((numberp no-of-unread) 
                           (blocking-do 'set-unread (conf-stat->conf-no whereto)
                                        no-of-unread))))
                  (let ((mship (blocking-do 'query-read-texts
                                            lyskom-pers-no 
                                            (conf-stat->conf-no whereto)
                                            t 0)))
                    (read-list-delete-read-info 
                     (conf-stat->conf-no whereto) lyskom-to-do-list)
                    (if (lyskom-try-get-membership (conf-stat->conf-no whereto) t)
                        (lyskom-replace-membership mship)
                      (lyskom-add-membership mship whereto t))
                    (unless (membership->position mship)
                      (set-membership->position mship position))

                    (if (< (membership->priority mship) lyskom-session-priority)
                        (progn (lyskom-format-insert-before-prompt
                                'member-in-conf-with-low-priority
                                whereto)
                               (when (= (conf-stat->conf-no whereto) 
                                        lyskom-current-conf)
                                 (lyskom-leave-current-conf)))
                      (lyskom-fetch-start-of-map whereto mship))))
                (lyskom-insert-string 'done))))))))
    became-member))


(defun lyskom-add-membership (membership conf-no-or-stat &optional blocking)
  "Adds MEMBERSHIP to the sorted list of memberships.
If BLOCKING is non-nil, block while reading the conference map until at 
least one text has been seen. CONF-NO-OR-STAT is the conf-no to add 
unless BLOCKING is t, in which cast it is the conf-stat."
  (if membership
      (progn
	(lyskom-insert-membership membership)
        (if blocking
            (lyskom-fetch-start-of-map conf-no-or-stat membership)
          (lyskom-prefetch-map conf-no-or-stat membership))
        (lyskom-run-hook-with-args 'lyskom-add-membership-hook
                                   membership))
    (lyskom-insert-string 'conf-does-not-exist)))



;;; ================================================================
;;;     Uttr{d - Subtract yourself as a member from a conference
;;;     Uteslut medlem - Subtract somebody else as a member

;;; Author: David Byers
;;; Based on code by Inge Wallin


;; Subtract another person

(def-kom-command kom-sub-member ()
  "Remove a member from a conference. To do this you need to be the
supervisor of the conference or of the member being removed."
  (interactive)
  (lyskom-sub-member
   (lyskom-read-conf-stat 'who-to-exclude '(pers) nil nil t)
   (lyskom-read-conf-stat 'where-from-exclude '(all) nil nil t)))


(def-kom-command kom-sub-self (&optional conf-no)
  "Resign your membership of a conference. If
`kom-unsubscribe-makes-passive' is set, them resigning once converts
your membership to a passive membership and resigning twice removes
the membership completely (if the server supports passive
memberships).

See `kom-unsubscribe-makes-passive'."
  (interactive)
  (let* ((me (blocking-do 'get-conf-stat lyskom-pers-no))
	 (conf (if conf-no (blocking-do 'get-conf-stat conf-no)
                 (lyskom-read-conf-stat 'leave-what-conf
                                        '(membership) nil nil t))))
    (lyskom-sub-member me conf)))

(defun lyskom-sub-member (pers conf &optional have-message)
  "Remove the person indicated by PERS as a member of CONF."
  (let* ((reply nil)
         (self (= (conf-stat->conf-no pers) lyskom-pers-no))
         (mship (and self 
                     kom-unsubscribe-makes-passive
                     (lyskom-get-membership (conf-stat->conf-no conf))))
         (passivate (and mship
                         (lyskom-have-call 102) ; set-membership-type
                         (not (membership-type->passive
                               (membership->type mship))))))

    (cond ((null pers) (lyskom-insert-string 'error-fetching-person))
	  ((null conf) (lyskom-insert-string 'error-fetching-conf))
          (passivate 
           (lyskom-prefetch-cancel-prefetch-map (conf-stat->conf-no conf))
           (unless have-message
             (lyskom-format-insert 'unsubscribe-to conf))
           (set-membership-type->passive (membership->type mship) t)
           (set-membership-type->message-flag (membership->type mship) nil)
           (setq reply (blocking-do 'set-membership-type
                                    (conf-stat->conf-no pers)
                                    (conf-stat->conf-no conf)
                                    (membership->type mship)))
           (if (not reply)
               (lyskom-format-insert 'unsubscribe-failed
                                     (lyskom-get-string 'You)
                                     conf)
             (lyskom-insert-string 'done)
             (lyskom-format-insert 'passivate-done conf)
             (when (= (conf-stat->conf-no conf) lyskom-current-conf)
               (lyskom-leave-current-conf))
	     (read-list-delete-read-info (conf-stat->conf-no conf)
					 lyskom-to-do-list)
             (lp--update-buffer (conf-stat->conf-no conf))))
	  (t
           (when self
             (lyskom-prefetch-cancel-prefetch-map (conf-stat->conf-no conf)))

	   (if self
	       (lyskom-format-insert 'unsubscribe-to conf)
	     (lyskom-format-insert 'exclude-from pers conf))

           (lyskom-ignoring-async (8 (conf-stat->conf-no conf))
             (setq reply (blocking-do 'sub-member
                                      (conf-stat->conf-no conf)
                                      (conf-stat->conf-no pers))))
	   (if self
	       (lyskom-remove-membership (conf-stat->conf-no conf)))
	   (if (not reply)
	       (lyskom-format-insert 'unsubscribe-failed
				     (if self
					 (lyskom-get-string 'You)
				       (conf-stat->name pers))
				     conf)
	     (lyskom-insert-string 'done)
	     (when (and self (= (conf-stat->conf-no conf)
                              lyskom-current-conf))
               (lyskom-leave-current-conf))
	     (read-list-delete-read-info (conf-stat->conf-no conf)
					 lyskom-to-do-list))))))




;;; ================================================================
;;;                 Skapa m|te - Create a conference

;;; Author: ???


(def-kom-command kom-create-conf (&optional name)
  "Create a new conference. Use this command when you want to create a
new conference. It will ask for all required information.

Before creating a conference, check that there isn't already one
covering the intended topics. There may be a conference for discussing
converence creation where you can ask. If there isn't, there is
probably a general Q&A conference where you can ask."
  (interactive)
  (let* ((conf-name (or name
			(lyskom-read-string
			 (lyskom-get-string 'name-of-conf))))
	 (open (j-or-n-p (lyskom-get-string 'anyone-member)))
	 (secret (if (not open)
		     (j-or-n-p (lyskom-get-string 'secret-conf))))
	 (orig (j-or-n-p (lyskom-get-string 'comments-allowed)))
         (anarchy (j-or-n-p (lyskom-get-string 'anonymous-allowed)))
         (secmem (and (lyskom-have-feature long-conf-types)
                      (not (lyskom-j-or-n-p (lyskom-get-string 'secret-members-allowed)))))
	 (life-expectancy (lyskom-read-number 'new-garb-nice-q))
	 (keep-commented (lyskom-read-number 'new-keep-commented-q))
	 (conf-no (blocking-do 'create-conf
			       conf-name
			       (lyskom-create-conf-type (not open)
							(not orig)
							secret
							nil
                                                        anarchy
                                                        secmem
                                                        nil
                                                        nil)
                               nil)))
    (if (null conf-no)
	(progn
	  (lyskom-format-insert 'could-not-create-conf
				conf-name)
	  (lyskom-insert-error))
      (progn
	(let ((conf-stat (blocking-do 'get-conf-stat conf-no)))
	  (lyskom-format-insert 'created-conf-no-name 
				(or conf-stat conf-no)
				(or conf-stat conf-name)
				(if conf-stat
				    (lyskom-default-button 'conf conf-stat)
				  nil)))
	(lyskom-scroll)
	(lyskom-add-member-by-no conf-no 
                                 lyskom-pers-no
                                 nil
                                 nil
				 (if secret
				     nil ; Don't write a presentation
				   'lyskom-create-conf-handler-2)
				 conf-no conf-name)
	(blocking-do 'set-garb-nice conf-no life-expectancy)
	(blocking-do 'set-keep-commented conf-no keep-commented)))))


(defun lyskom-create-conf-handler-2 (conf-no conf-name)
  "Starts editing a presentation for the newly created conference.
This does lyskom-end-of-command"
  (lyskom-tell-internat 'kom-tell-conf-pres)
  (let ((conf (blocking-do 'get-conf-stat conf-no)))
    (if conf
        (lyskom-dispatch-edit-text lyskom-proc
                                   (lyskom-create-misc-list
                                    'RECPT
                                    (server-info->conf-pres-conf 
                                     lyskom-server-info))
                                   conf-name ""
                                   'lyskom-set-presentation conf-no))))


(defun lyskom-set-presentation (text-no conf-no)
  "Set presentation of a conference. Args: text-no conf-no."
  (initiate-set-presentation 'background nil conf-no text-no)
  (cache-del-conf-stat conf-no))	;+++Borde {ndra i cachen i st{llet.
					;+++ Kan tas bort n{r det existerar 
					;asynkrona meddelanden som talar om att
					;n}got {r {ndrat.
    

(defun lyskom-set-conf-motd (text-no conf-no)
  "Set motd of a conference. Args: text-no conf-no."
  (initiate-set-conf-motd 'background nil conf-no text-no)
  (cache-del-conf-stat conf-no))	;+++Borde {ndra i cachen i st{llet.
					;+++ Kan tas bort n{r det existerar 
					;asynkrona meddelanden som talar om att
					;n}got {r {ndrat.
    

;;; ================================================================
;;;                  Kommentera - write comment
;;; Author: ???
;;; FIXME: Does not use def-kom-command


(def-kom-command kom-write-comment (text-no)
  "Write a comment to the selected text.

Several settings affect writing texts in general.
`kom-write-texts-in-window' controls which window is used to write the
comment; `kom-confirm-multiple-recipients' affects how multiple
recipients are handled.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  :prompt-format (concat "%#1C " (if text-no
                                     (lyskom-format " (%#1n)" text-no)
                                   ""))
  (interactive (list (let ((lyskom-current-command 'kom-write-comment))
                       (lyskom-read-text-no-prefix-arg 'what-comment-no))))
  (if text-no
      (progn
        (lyskom-nag-about-presentation)
        (blocking-do-multiple ((text (get-text text-no))
                               (text-stat (get-text-stat text-no)))
          (when (or (null (text-stat-find-aux text-stat 4))
                    (lyskom-j-or-n-p 
                     (lyskom-get-string 'no-comments-q)))
            (if (and (text-stat-find-aux text-stat 5)
                     (lyskom-j-or-n-p
                      (lyskom-get-string 'private-answer-q)))
                (lyskom-private-answer-soon
                 text-stat
                 text
                 text-no)
              (lyskom-write-comment-soon text-stat
                                         text
                                         text-no
                                         'comment)))))
    (lyskom-insert-string 'confusion-who-to-reply-to)))



(def-kom-command kom-write-footnote (text-no)
  "Write a footnote to the selected text. In order to write a footnote
you must be the author, or superviser of the author, of the selected
text.

Several settings affect writing texts in general.
`kom-write-texts-in-window' controls which window is used to write the
comment; `kom-confirm-multiple-recipients' affects how multiple
recipients are handled.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg'). Without a prefix argument this
command will write a footnote to the most recently read or written
applicable text."
  :prompt-format (concat "%#1C " (if text-no
                                     (lyskom-format " (%#1n)" text-no)
                                   ""))
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-footnote-no)))
  (if text-no
      (lyskom-write-comment-soon
       (blocking-do 'get-text-stat text-no)
       (blocking-do 'get-text text-no)
       text-no 'footnote)
    (lyskom-insert-string 'confusion-what-to-footnote)))


(def-kom-command kom-comment-previous (text-no)
  "Write a comment to the selected text. Without a prefix argument
the selected text is the next-to-last text in the buffer.

Several settings affect writing texts in general.
`kom-write-texts-in-window' controls which window is used to write the
comment; `kom-confirm-multiple-recipients' affects how multiple
recipients are handled.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  :prompt-format (concat "%#1C " (if text-no
                                     (lyskom-format " (%#1n)" text-no)
                                   ""))
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-comment-no)))
  (if text-no
      (blocking-do-multiple ((text-stat (get-text-stat text-no))
                             (text (get-text text-no)))
        (when (or (null text-stat)
                  (null text)
                  (null (text-stat-find-aux text-stat 4))
                  (lyskom-j-or-n-p 
                   (lyskom-get-string 'no-comments-q)))
          (if (and (text-stat-find-aux text-stat 5)
                   (lyskom-j-or-n-p
                    (lyskom-get-string 'private-answer-q)))
              (lyskom-private-answer-soon
               text-stat
               text
               text-no)
            (lyskom-write-comment-soon
             text-stat
             text
             text-no
             'comment))))
    (lyskom-insert-string 'confusion-what-to-comment)))


(defun lyskom-write-comment-soon (text-stat text text-no type)
  "Write a comment to the text with TEXT-STAT, TEXT and, TEXT-NO.
TYPE is either 'comment or 'footnote."
  (let ((str (and text-stat text (text->decoded-text-mass text text-stat))))
    (cond
     ;; Text not found?
     ((or (null text-stat)
          (null text))
      (lyskom-format-insert 'cant-read-textno text-no))
     ;; Give header.
     ((string-match "\n" str)
      (lyskom-write-comment text-stat
                            (substring str 0 (match-beginning 0))
                            type))
     ;; The commented text had no header.
     (t (lyskom-write-comment text-stat "" type)))))


(defun lyskom-write-comment (text-stat subject type)
  "Write a comment to the text associated with TEXT-STAT.
The default subject is SUBJECT. TYPE is either 'comment or 'footnote."
  (if (null text-stat)
      (progn
        (lyskom-insert-string 'confusion-what-to-comment))
    (let ((ccrep nil)
          (bccrep nil))
      (lyskom-tell-internat (if (eq type 'comment)
                                'kom-tell-write-comment
                              'kom-tell-write-footnote))
      (let (data)
        (mapc
	 (lambda (misc-info)
	   (cond
	    ((eq 'RECPT (misc-info->type misc-info))
	     (setq data 
		   (cons (blocking-do 'get-conf-stat 
				      (misc-info->recipient-no misc-info))
			 data)))
	    ((and (eq type 'footnote)
		  (eq 'CC-RECPT (misc-info->type misc-info)))
	     (setq ccrep (cons (misc-info->recipient-no misc-info) 
			       ccrep))
	     (setq data (cons (blocking-do 'get-conf-stat
					   (misc-info->recipient-no misc-info))
			      data)))
	    ((and (eq type 'footnote)
		  (eq 'BCC-RECPT (misc-info->type misc-info)))
	     (setq bccrep (cons (misc-info->recipient-no misc-info) 
				bccrep))
	     (setq data (cons (blocking-do 'get-conf-stat
					   (misc-info->recipient-no misc-info))
			      data)))))
         (text-stat->misc-info-list text-stat))
        (lyskom-comment-recipients (nreverse data)
                                   lyskom-proc 
                                   text-stat
                                   subject 
                                   type 
                                   (nreverse ccrep) 
                                   (nreverse bccrep))))))


(defun lyskom-comment-recipients (data lyskom-proc text-stat
				       subject type ccrep bccrep)
  "Compute recipients to a comment to a text.
Args: DATA, LYSKOM-PROC TEXT-STAT SUBJECT TYPE CCREP BCCREP.
DATA is a list of all the recipients that should receive this text.
If DATA contains more than one conference the user is asked (using y-or-n-p)
if all conferences really should receive the text.
The call is continued to the lyskom-edit-text.
TYPE is info whether this is going to be a comment of footnote.
CCREP is a list of all recipients that are going to be cc-recipients.
BCCREP is a list of all recipient that are going to be bcc-recipients."

  (condition-case nil
      ;; Catch any quits
      (progn
	;; Filter multiple recipients through y-or-n-p.
	(if (and (eq kom-confirm-multiple-recipients 'before)
                 (> (length data) 1)
		 (not (and (= (length data) 2)
			   (or (= lyskom-pers-no (conf-stat->conf-no
						  (car data)))
			       (= lyskom-pers-no (conf-stat->conf-no
						  (car (cdr data))))))))
	    (let ((new-data nil))
	      (while data
		(if (lyskom-j-or-n-p (lyskom-format 'comment-keep-recpt-p
					     (conf-stat->name (car data))))
		    (setq new-data (cons (car data) new-data)))
		(setq data (cdr data)))
	      (setq data (nreverse new-data))))	    

	(let* ((member nil)
	       (recver (lyskom-create-misc-list
			(cond 
			 ((eq type 'comment) 'COMM-TO)
			 ((eq type 'footnote) 'FOOTN-TO)
			 (t (signal 'lyskom-internal-error
				    (list "Unknown comment type" type))))
			(text-stat->text-no text-stat)))
	       (recpts nil))
	  (while data
	    (let* ((conf-stat (car data))
                   (confno (conf-stat->conf-no conf-stat))
                   (commno (if (eq type 'footnote)
                               confno
                             (conf-stat->comm-conf conf-stat))))
	      (if (memq commno recpts)
		  nil
		(setq recver
		      (append recver
			      (list
			       (cons (cond
                                      ((memq confno ccrep) 'CC-RECPT)
                                      ((memq confno bccrep) 'BCC-RECPT)
                                      (t 'RECPT))
                                     commno))))
		(if (lyskom-get-membership commno)
		    (setq member t))
		(setq recpts (cons commno recpts))))
	    (setq data (cdr data)))
	  ;; Add the user to the list of recipients if he isn't a member in
	  ;; any of the recipients.
	  (when (not member)
	      (setq recver (append recver
				   (list (cons 'RECPT
                                               (or (lyskom-get-send-comments-to lyskom-pers-no)
                                                   lyskom-pers-no))))))
	  (lyskom-edit-text lyskom-proc 
			    recver
			    subject "")))

    (quit (signal 'quit nil))))


;;; ================================================================
;;;                Personligt svar - personal answer
;;;
;;; Author: ???
;;; Rewritten using blocking-do by: Linus Tolke


(def-kom-command kom-private-answer (text-no)
  "Write a private answer to the selected text.

Several settings affect writing texts in general.
`kom-write-texts-in-window' controls which window is used to write the
comment; `kom-confirm-multiple-recipients' affects how multiple
recipients are handled.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  :prompt-format (concat "%#1C " (if text-no
                                     (lyskom-format " (%#1n)" text-no)
                                   ""))
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-private-no)))
  (if text-no
      (blocking-do-multiple ((text-stat (get-text-stat text-no))
                             (text (get-text text-no)))
        (when (or (null (text-stat-find-aux text-stat 4))
                  (lyskom-j-or-n-p 
                   (lyskom-get-string 'no-comments-q)))
          (lyskom-private-answer-soon text-stat text text-no)))
    (lyskom-insert-string 'confusion-who-to-reply-to)))


(defun lyskom-private-answer-soon (text-stat text text-no)
  "Write a private answer to TEXT-STAT, TEXT."
  (if (and text-stat text)
      (let ((str (text->decoded-text-mass text text-stat)))
        (if (string-match "\n" str)
            (lyskom-private-answer text-stat
                                   (substring str
                                              0 (match-beginning 0)))
          (lyskom-private-answer text-stat "")))
    (lyskom-format-insert 'no-such-text-no text-no)))


(defun lyskom-private-answer (text-stat subject)
  "Write a private answer. Args: TEXT-STAT SUBJECT."
  (cond ((null text-stat)
         (lyskom-insert-string 'confusion-what-to-answer-to))
        ((= (text-stat->author text-stat) 0)
         (lyskom-insert-string 'personal-comment-to-anonymous))
        (t
         (lyskom-tell-internat 'kom-tell-write-reply)
         (cache-del-conf-stat (text-stat->author text-stat))
         (let* ((conf-stat (blocking-do 'get-conf-stat
                                        (text-stat->author text-stat))))
           (if (if (zerop (conf-stat->msg-of-day conf-stat))
                   t
                 (progn
                   (recenter 1)
                   (lyskom-format-insert 'has-motd
                                         conf-stat)
                   (lyskom-view-text (conf-stat->msg-of-day conf-stat))
                   (if (lyskom-j-or-n-p (lyskom-get-string 'motd-persist-q))
                       t
                     nil)))

               (lyskom-edit-text lyskom-proc
                                 (if (= (text-stat->author text-stat) lyskom-pers-no)
                                     (lyskom-create-misc-list
                                      'COMM-TO (text-stat->text-no text-stat)
                                      'RECPT (text-stat->author text-stat))
                                   (lyskom-create-misc-list
                                    'COMM-TO (text-stat->text-no text-stat)
                                    'RECPT (text-stat->author text-stat)
                                    'RECPT lyskom-pers-no))
                                 subject ""))))))


;;; ================================================================
;;;    Personligt svar p} f|reg}ende - kom-private-answer-previous
;;;
;;; Author: ceder
;;; Rewritten using blocking-do by: Linus Tolke

(def-kom-command kom-private-answer-previous (text-no)
  "Write a private answer to the selected text. Without a prefix
argument the next-to-last text in the buffer will be selected.


Several settings affect writing texts in general.
`kom-write-texts-in-window' controls which window is used to write the
comment; `kom-confirm-multiple-recipients' affects how multiple
recipients are handled.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  :prompt-format (concat "%#1C " (if text-no
                                     (lyskom-format " (%#1n)" text-no)
                                   ""))
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-private-no)))
  (if text-no
      (blocking-do-multiple ((text-stat (get-text-stat text-no))
                             (text (get-text text-no)))
        (when (or (null (text-stat-find-aux text-stat 4))
                  (lyskom-j-or-n-p 
                   (lyskom-get-string 'no-comments-q)))
          (lyskom-private-answer-soon text-stat text text-no)))
    (lyskom-insert-string 'confusion-who-to-reply-to)))


;;; ================================================================
;;;                         Sluta - quit

;;; Author: ???


(defun kom-quit (&optional arg)
  "Exit LysKOM. With prefix argument, don't ask for confirmation."
  (interactive "P")
  (lyskom-start-of-command 'kom-quit t)
  (let ((do-end-of-command t))
    (unwind-protect
        (setq do-end-of-command
              (cond
               ((and (lyskom-buffers-of-category 'write-texts)
                     (display-buffer 
                      (car (lyskom-buffers-of-category 'write-texts)))
                     (not (lyskom-ja-or-nej-p
                           (lyskom-get-string 'quit-in-spite-of-unsent))))
                t)
               ((or arg (lyskom-ja-or-nej-p (lyskom-get-string 'really-quit)))
                (lyskom-quit) nil)
               (t t)))
      (if do-end-of-command (lyskom-end-of-command)))))


(defun lyskom-quit ()
  "Quit a session. Kill process and buffer-local variables.
Don't ask for confirmation."
    (initiate-logout 'main nil)
    (lyskom-remove-unread-buffer lyskom-buffer)
    (set-process-sentinel lyskom-proc nil)
    (delete-process lyskom-proc)
    (lyskom-insert-string (lyskom-get-string-sol 'session-ended))
    (lyskom-scroll)
    (setq mode-line-process (lyskom-get-string 'mode-line-down))
    (run-hooks 'kom-quit-hook)

    (when (and (boundp 'lyskom-ssh-proxy)
               lyskom-ssh-proxy)
      (let* ((numleft (1- (get lyskom-ssh-proxy 'num-connected)))
             (procname (symbol-name lyskom-ssh-proxy))
             (bufname (concat " *" procname "*"))
             (proc (get-process procname)))
        (put lyskom-ssh-proxy 'num-connected numleft)
        (when (and (<= numleft 0) proc)
          (string-match "^ssh<\\([^:]*\\)" procname)
          (lyskom-message 
           "%s"
           (lyskom-format 'ssh-closing
                          (or (match-string 1 procname) 
                              (lyskom-get-string 'ssh-unknown-host))))
          (delete-process proc)
          (when (get-buffer bufname)
            (save-current-buffer
              (set-buffer bufname)
              (goto-char (point-max))
              (insert "\n--- closed connection ---\n")))))))
  

;;; ================================================================
;;;     	[ndra presentation - Change presentation 
;;;	         S{tta lapp p} d|rren - Change conf-motd

;;; Author: Inge Wallin
;;; Changed by Linus Tolke


(def-kom-command kom-change-presentation ()
  "Change presentation for a person or a conference. If the person
or conference doesn't have a presentation, a new presentation will 
be created."
  (interactive)
  (lyskom-change-pres-or-motd-2
   (let ((no (lyskom-read-conf-no 'what-to-change-pres-you '(all) nil nil t)))
     (blocking-do 'get-conf-stat no))
   'pres))



(def-kom-command kom-change-conf-motd ()
  "Change the notice for a person or a conference. If the person
or conference doesn't have a notice, a new notice till be created."
  (interactive)
  (lyskom-change-pres-or-motd-2
   (let ((no (lyskom-read-conf-no 'who-to-put-motd-for '(all) nil nil t)))
     (blocking-do 'get-conf-stat no))
   'motd))


(defun lyskom-get-recipients-from-misc-list (misc-list &optional get-types)
  "Return a misc-info-list containing only the recipients.
If optional GET-TYPES is non-nil, then return only recipients of that type."
  (unless get-types (setq get-types lyskom-recpt-types-list))
  (let ((result nil))
    (lyskom-traverse misc misc-list
      (when (memq (misc-info->type misc) get-types)
	(setq result (cons (misc-info->recipient-no misc)
			   (cons (misc-info->type misc) 
				 result)))))
    (nreverse result)))



(defun lyskom-change-pres-or-motd-2 (conf-stat type)
  "Change the presentation or motd of CONF-STAT.
TYPE is either 'pres or 'motd, depending on what should be changed."
  (cond
   ((null conf-stat)			;+++ annan felhantering
    (lyskom-insert-string 'cant-get-conf-stat))
   ((or lyskom-is-administrator
	(lyskom-get-membership (conf-stat->supervisor conf-stat) t)
	(= lyskom-pers-no (conf-stat->conf-no conf-stat)))
    (blocking-do-multiple ((text-stat (get-text-stat
                                       (conf-stat->presentation conf-stat)))
                           (text-mass (get-text 
                                       (cond
                                        ((eq type 'pres)
                                         (conf-stat->presentation conf-stat))
                                        ((eq type 'motd)
                                         (conf-stat->msg-of-day conf-stat))))))
      (let ((str (and text-mass (text->decoded-text-mass
                                 text-mass text-stat))))
        (lyskom-dispatch-edit-text
         lyskom-proc
         (apply
          'lyskom-create-misc-list
          (if (and (eq type 'pres)
                   (not (or (zerop (conf-stat->presentation conf-stat))
                            (null text-stat))))
              (append
               (lyskom-get-recipients-from-misc-list
                (text-stat->misc-info-list text-stat))
               (list 'COMM-TO
                     (conf-stat->presentation conf-stat)))
            (list 'RECPT
                  (cond
                   ((eq type 'motd)
                    (server-info->motd-conf lyskom-server-info))
                   ((eq type 'pres)
                    (if (conf-type->letterbox
                         (conf-stat->conf-type conf-stat))
                        (server-info->pers-pres-conf 
                         lyskom-server-info)
                      (server-info->conf-pres-conf
                       lyskom-server-info)))))))
         (conf-stat->name conf-stat)
         (if (and text-mass (string-match "\n" str))
             (substring str (match-end 0))
           (if (and (eq type 'pres)
                    (conf-type->letterbox (conf-stat->conf-type conf-stat)))
               (lyskom-get-string 'presentation-form)
             ""))
         (cond
          ((eq type 'pres) 'lyskom-set-presentation)
          ((eq type 'motd) 'lyskom-set-conf-motd))
         (conf-stat->conf-no conf-stat)))))
   (t
    (lyskom-format-insert 'not-supervisor-for
			  conf-stat))))


(def-kom-command kom-set-presentation (arg)
  "Set the presentation of a conference or person to the selected text.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive "P")
   (let ((conf-no (lyskom-read-conf-no 'what-to-set-pres-you '(all) nil nil t))
         (text-no (lyskom-read-text-no-prefix-arg 'what-text-to-set-as-pres-no)))
     (lyskom-set-pres-or-motd-2
      conf-no
      text-no
      'pres)))

(def-kom-command kom-set-motd-text (arg)
  "Set the notice for a person or conference to the selected text.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive "P")
   (let ((conf-no (lyskom-read-conf-no 'what-to-set-motd-you '(all) nil nil t))
         (text-no (lyskom-read-text-no-prefix-arg 'what-text-to-set-as-motd-no)))
     (lyskom-set-pres-or-motd-2 conf-no text-no 'motd)))

(defun lyskom-set-pres-or-motd-2 (conf-no text-no what)
  (let ((set-pres (eq what 'pres)))
    (blocking-do-multiple ((conf-stat (get-conf-stat conf-no))
                           (text-stat (get-text-stat text-no)))
      (cond ((null conf-stat)
             (lyskom-format-insert 'no-such-conf-or-pers)) 
            ((null text-stat)
             (lyskom-format-insert 'no-such-text-no text-no))
            (t
             (let ((existing (if set-pres
                                 (conf-stat->presentation conf-stat)
                               (conf-stat->msg-of-day conf-stat))))
               (when (or (zerop existing)
                         (progn (lyskom-view-text existing)
                                (lyskom-j-or-n-p 
                                 (lyskom-get-string (if set-pres
                                                        'conf-already-has-pres
                                                      'conf-already-has-motd)))))
                 (cache-del-conf-stat conf-no)
                 (cache-del-text-stat text-no)
                 (lyskom-format-insert (if set-pres
                                           'setting-conf-pres
                                         'setting-conf-motd)
                                       conf-stat text-no)
                 (lyskom-report-command-answer 
                  (cond ((eq what 'pres)
                         (blocking-do 'set-presentation conf-no text-no))
                        ((eq what 'motd)
                         (blocking-do 'set-conf-motd conf-no text-no)))))))))))

(def-kom-command kom-remove-presentation ()
  "Removes the presentation for a person or a conference. Please don't
remove a presentation without adding a new one. This can be accomplished
with the `kom-change-presentation' command."
  (interactive)
  (let ((conf-stat (lyskom-read-conf-stat 'who-to-remove-pres-for
                                          '(all) nil nil t)))
    (cond ((null conf-stat)
           (lyskom-insert-string 'cant-get-conf-stat))
          ((zerop (conf-stat->presentation conf-stat))
           (lyskom-format-insert 'has-no-presentation conf-stat))
          (t
           (lyskom-format-insert 'removing-pres-for-conf conf-stat 
                                 (conf-stat->presentation conf-stat))
           (lyskom-report-command-answer
            (blocking-do 'set-presentation (conf-stat->conf-no conf-stat) 0))))))

;;; ================================================================
;;;           Ta bort lapp p} d|rren - delete conf-motd

;;; Author: Linus Tolke (& Inge Wallin)


(def-kom-command kom-unset-conf-motd ()
  "Removes the notice for a person or conference."
  (interactive)
  (let ((conf-stat (or (lyskom-read-conf-stat 'who-to-remove-motd-for
                                              '(all) nil nil t)
		       (blocking-do 'get-conf-stat lyskom-pers-no))))
    (cond
     ((null conf-stat)
      (lyskom-insert-string 'cant-get-conf-stat))
     ((or lyskom-is-administrator
	  (lyskom-get-membership (conf-stat->supervisor conf-stat) t))
      ;; This works like a dispatch. No error handling.
      (lyskom-set-conf-motd 0 (conf-stat->conf-no conf-stat)))
     (t
      (lyskom-format-insert 'not-supervisor-for
			    conf-stat)))))
  

;;; ================================================================
;;;               G} till M|te - Go to a conference.

;;; Author: ???

(def-kom-command kom-go-to-conf (&optional conf-no)
  "Go to a conference and start reading texts there.

Changing conferences runs `kom-change-conf-hook' and
`kom-after-change-conf-hook'."
  (interactive)
  (let ((conf (if conf-no
                  (blocking-do 'get-conf-stat conf-no)
                (lyskom-read-conf-stat 'go-to-conf-p '(all) nil nil t))))
    (when (lyskom-check-go-to-conf conf)
      (lyskom-go-to-conf conf))))


(defun lyskom-go-to-conf (conf &optional no-prompt)
  "Go to the conference in CONF. CONF can be conf-no of conf-stat.
Allowed conferences are conferences and the mailboxes you are 
member of.
If NO-PROMPT is non-nil, don't print message that we have gone to conf."
  (if (numberp conf) (setq conf (blocking-do 'get-conf-stat conf)))
  (let ((membership (lyskom-get-membership (conf-stat->conf-no conf) t)))
    (unless no-prompt
      (lyskom-format-insert 'go-to-conf conf))

    ;; FIXME: DEBUG+++
    (let ((lyskom-inhibit-prefetch t))

      (cond
       (membership
	(lyskom-do-go-to-conf conf membership))
       ((conf-type->letterbox (conf-stat->conf-type conf))
	(lyskom-format-insert 'cant-go-to-his-mailbox
			      conf))
       (t
	(progn
	  (lyskom-format-insert 'not-member-of-conf
				conf)
	  (lyskom-scroll)
	  (if (lyskom-j-or-n-p (lyskom-get-string 'want-become-member))
              (if (lyskom-add-member-by-no (conf-stat->conf-no conf) lyskom-pers-no)
                  (lyskom-do-go-to-conf conf
                                        (lyskom-get-membership
                                         (conf-stat->conf-no conf) t))
                (lyskom-report-command-answer nil))
	    (lyskom-insert-string 'no-ok))))))

    ;; DEBUG+++
    (lyskom-continue-prefetch)
    ))
  

(defun lyskom-do-go-to-conf (conf-stat membership)
  "Go to a conference. Args: CONF-STAT MEMBERSHIP.
 Put a read-info of type CONF first on lyskom-reading-list.
Args: CONF-STAT MEMBERSHIP"
  (let ((priority (lyskom-get-current-priority)))
    (lyskom-maybe-move-unread nil)
    (if conf-stat
	(lyskom-set-mode-line conf-stat))
    (let ((r 0)
	  (len (read-list-length lyskom-to-do-list)) 
	  (found nil))
      (while (and (not found)
		  (< r len))
	(if (and (read-info->conf-stat (read-list->nth lyskom-to-do-list r))
		 (= (conf-stat->conf-no conf-stat)
		    (conf-stat->conf-no 
		     (read-info->conf-stat 
		      (read-list->nth lyskom-to-do-list r)))))
	    (setq found t)
	  (++ r)))

      (cond (found
             (let ((read-info (read-list->nth lyskom-to-do-list r)))
               (read-list-enter-first read-info lyskom-reading-list)
               (read-list-delete-read-info (conf-stat->conf-no conf-stat)
                                           lyskom-to-do-list)
               (read-list-enter-first read-info lyskom-to-do-list)
               (set-read-info->priority read-info priority)
               (lyskom-enter-conf conf-stat read-info)))
	    (t
	     (lyskom-go-to-empty-conf conf-stat))))))



(defun lyskom-go-to-empty-conf (conf-stat)
  "Go to a conference with no unseen messages. Args: CONF-STAT."
  (let ((old-current-conf lyskom-current-conf))
    (unless lyskom-is-anonymous
      (blocking-do 'pepsi (conf-stat->conf-no conf-stat)))
    (lyskom-run-hook-with-args 'kom-change-conf-hook 
                               lyskom-current-conf
                               (conf-stat->conf-no conf-stat))
    (setq lyskom-current-conf (conf-stat->conf-no conf-stat))
    (lyskom-enter-conf-print-unread conf-stat 0)
    (lp--update-buffer old-current-conf)
    (lp--update-buffer lyskom-current-conf)
    (lyskom-run-hook-with-args 'kom-after-change-conf-hook 
                               lyskom-current-conf
                               (conf-stat->conf-no conf-stat))))


(defun lyskom-get-current-priority ()
  "Return the current priority level."
  (or (and (read-list->first lyskom-reading-list)
           (read-info->priority (read-list->first lyskom-reading-list)))
      (and (read-list->first lyskom-to-do-list)
           (read-info->priority (read-list->first lyskom-to-do-list)))
      -1))


;;; ================================================================
;;;                 Skriva inl{gg - write text

;;; Author: ???


(def-kom-command kom-write-text (&optional arg)
  "Start writing a new text that isn't a comment to any existing text.
With a prefix argument don't ask for a recipient.

Several settings affect writing texts in general.
`kom-write-texts-in-window' controls which window is used to write the
comment; `kom-confirm-multiple-recipients' affects how multiple
recipients are handled."
  (interactive "P")
  (lyskom-tell-internat 'kom-tell-write-text)
  (lyskom-write-text arg 'who-send-text-to))

(defun lyskom-write-text (arg prompt &optional add-self)
  "Start writing a new text."
  (lyskom-nag-about-presentation)
  (let* ((tono (cond ((and arg (listp arg)
                           lyskom-current-conf
                           (not (eq 0 lyskom-current-conf)))
                      lyskom-current-conf)

                     ((numberp arg) arg)

                     (t (lyskom-read-conf-no prompt '(pers conf) nil nil t))))
         (conf-stat (blocking-do 'get-conf-stat tono)))
    (cache-del-conf-stat tono)
    (if (if (zerop (conf-stat->msg-of-day conf-stat))
            t
          (progn
            (recenter 1)
            (lyskom-format-insert 'has-motd 
                                  conf-stat)
            (lyskom-view-text (conf-stat->msg-of-day conf-stat))
            (if (lyskom-j-or-n-p (lyskom-get-string 'motd-persist-q))
                t
              nil)))
        (if (= tono lyskom-pers-no)
            (lyskom-edit-text lyskom-proc
                              (lyskom-create-misc-list 'RECPT tono)
                              "" "")
          (lyskom-edit-text lyskom-proc
                            (if (and (lyskom-get-membership tono)
                                     (not add-self))
                                (lyskom-create-misc-list 'RECPT tono)
                              (lyskom-create-misc-list 
                               'RECPT tono
                               'RECPT lyskom-pers-no))
                            "" "")))))


;;; ================================================================
;;;                 Lista Personer - List persons
;;;
;;; Author: ceder
;;; Rewritten: linus

(def-kom-command kom-list-persons (match)
  "List all persons whose names start with a particular string.
For more flexible listing, use kom-list-re instead."
  (interactive (list (lyskom-read-string
		      (lyskom-get-string 'search-for-pers))))
  (let ((result (blocking-do 'lookup-z-name match 1 0)))
    (if result 
        (if (conf-z-info-list->conf-z-infos result)
            (lyskom-traverse info (conf-z-info-list->conf-z-infos result)
              (lyskom-list-pers-print info))
          (lyskom-format-insert 'no-matching-perss match))
      (lyskom-insert (lyskom-current-error)))))


(defun lyskom-list-pers-print (conf-z)
  "Print name of the person CONF-NO for kom-list-persons."
  (lyskom-format-insert "%[%#1@%5#2:p %#2P%]\n"
			(lyskom-default-button 'pers (conf-z-info->conf-no conf-z))
			conf-z))



;;; ================================================================
;;;              Lista M|ten - List conferences

;;; Author: ceder
;;; Rewritten: linus

(def-kom-command kom-list-conferences (&optional match)
  "List all conferences whose names start with a particular string.
Those that you are not a member in will be marked with an asterisk.
For more flexible searching, use `kom-list-re'."
  (interactive)

  (unless kom-allow-incompleteness
    (sit-for 0)
    (lyskom-prefetch-all-confs))

  (setq match (or match
                  (lyskom-read-string
                   (lyskom-get-string 'search-for-conf))))
  (let ((result (blocking-do 'lookup-z-name match 0 1)))
    (if result 
        (if (conf-z-info-list->conf-z-infos result)
            (progn 
              (lyskom-traverse info (conf-z-info-list->conf-z-infos result)
                (lyskom-list-conf-print info))
              (lyskom-insert 'conf-list-legend))
          (lyskom-format-insert 'no-matching-confs match))
      (lyskom-insert (lyskom-current-error)))))


(def-kom-command kom-list-created-conferences (arg)
  "List all conferences created by a particular person. With a prefix
argument, also list persons created by that person.

See `kom-allow-incompleteness'."
  (interactive "P")

  (unless kom-allow-incompleteness
    (sit-for 0)
    (lyskom-prefetch-all-confs))

  (blocking-do 'get-uconf-stat lyskom-pers-no)
  (let ((pers (lyskom-read-conf-stat
                  (if arg 'list-pers-confs-created-by 'list-confs-created-by)
                  '(all) nil nil t)))
    (lyskom-format-insert 'listing-confs-created-by (conf-stat->conf-no pers))
    (lyskom-message (lyskom-get-string (if arg 'getting-all-pers-confs 'getting-all-confs)))
    (let ((result (blocking-do 'lookup-z-name "" (if arg 1 0) 1)))
      (lyskom-message (lyskom-get-string (if arg 'getting-all-pers-confs-done 'getting-all-confs-done)))
      (if result
          (if (conf-z-info-list->conf-z-infos result)
              (let ((counter (vector nil
                                     1
                                     (length (conf-z-info-list->conf-z-infos result))
                                     0))
                    (calls nil)
                    (was-at-max (= (save-excursion (end-of-line) (point)) (point-max))))
                (condition-case arg
                    (progn
                      (lyskom-traverse conf-z (conf-z-info-list->conf-z-infos result)
                        (setq calls (cons
                                     (initiate-get-conf-stat 'main
                                                             'lyskom-list-created-conferences-2
                                                             (conf-z-info->conf-no conf-z)
                                                             counter
                                                             (conf-stat->conf-no pers)
                                                             arg
                                                             )
                                     calls)))
                      (lyskom-wait-queue 'main)
                      (if (eq 0 (elt counter 3))
                          (lyskom-format-insert 'no-created-confs pers)
                        (let ((window (get-buffer-window (current-buffer))))
                          (if (and window was-at-max)
                              (if (lyskom-pos-visible-in-window-p (point-max) window)
                                  (goto-char (point-max))
                                (and kom-continuous-scrolling (lyskom-scroll)))))))
                  (quit (aset counter 0 t)
                        (lyskom-cancel-call 'main calls)
                        (signal 'quit nil))))
            (lyskom-insert (lyskom-get-string (if arg 'no-pers-confs-exist 'no-confs-exist))))
        (lyskom-format-insert (lyskom-current-error))))))

(defun lyskom-list-conf-membership-char (conf-no)
  (if lyskom-membership-is-read
      (cond ((lyskom-get-membership conf-no) ?\ )
            ((lyskom-get-membership conf-no t) ?-)
            (t ?*))
    (cond ((lyskom-try-get-membership conf-no) ?\ )
          ((lyskom-try-get-membership conf-no t) ?-)
          (t ?\?))))

(defun lyskom-list-created-conferences-2 (cs counter pers-no arg)
  (unless (elt counter 0)
    (aset counter 1 (1+ (elt counter 1)))
    (lyskom-message (lyskom-format (if arg 'finding-created-pers-confs 'finding-created-confs)
                                   (elt counter 1)
                                   (elt counter 2)))
    (when (and cs (memq pers-no (list (conf-stat->creator cs)
					(conf-stat->supervisor cs)
					(conf-stat->super-conf cs))))
      (aset counter 3 (1+ (elt counter 3)))
      (lyskom-format-insert "%5#2m %5#6d %#3c %4#4s %#5s %#2M\n"
                            (lyskom-default-button 'conf (conf-stat->conf-no cs))
                            cs
                            (lyskom-list-conf-membership-char (conf-stat->conf-no cs))
                            (concat (if (eq pers-no (conf-stat->creator cs)) (lyskom-get-string 'created-conf-letter) " ")
                                    (if (eq pers-no (conf-stat->supervisor cs)) (lyskom-get-string 'supervisor-conf-letter) " ")
                                    (if (and (conf-type->original (conf-stat->conf-type cs))
                                             (eq pers-no (conf-stat->super-conf cs)))
                                        (lyskom-get-string 'superconf-conf-letter) " "))
                            (cond ((conf-type->secret (conf-stat->conf-type cs)) (lyskom-get-string 'secret-conf-letter))
                                  ((conf-type->rd_prot (conf-stat->conf-type cs)) (lyskom-get-string 'protected-conf-letter))
                                  (t " "))
                            (conf-stat->garb-nice cs))
      (sit-for 0))))


(defun lyskom-list-conf-print (conf-z)
  "Print a line of info about CONF-NO.
If you are not member in the conference it will be flagged with an asterisk."
  (lyskom-format-insert "%5#1m %#2c %#1M\n"
			conf-z
                        (lyskom-list-conf-membership-char (conf-z-info->conf-no conf-z))))

;;; ================================================================
;;;                Lista med regexpar - List regexp


(defun lyskom-make-re-case-insensitive (re)
  "Convert the regexp RE to a case insensitive regexp."
  (unless lyskom-char-classes
    (setq lyskom-char-classes 
	  (lyskom-compute-char-classes lyskom-collate-table)))
  (let ((res nil)
	(input (listify-vector re))
	val)
    (while input
      (cond

       ;; Copy "[]" character sets literally.
       ((eq (car input) ?\[)
	(setq res (cons "[" res))
	(setq input (cdr input))
	(when input			;Handle "[]asdf]" properly.
	  (setq res (cons (make-string 1 (car input)) res))
	  (setq input (cdr input)))
	(while (and input (not (eq (car input) ?\])))
	  (setq res (cons (make-string 1 (car input)) res))
	  (setq input (cdr input)))
	(when input			;Don't forget the terminating "]".
	  (setq res (cons (make-string 1 (car input)) res))
	  (setq input (cdr input))))

       ;; Copy backslashed sequences literally.
       ((eq (car input) ?\\)
	(setq res (cons "\\" res))
	(setq input (cdr input))
	(when input
	  (setq res (cons (make-string 1 (car input)) res))
	  (setq input (cdr input))))

       ;; Copy special characters literally.
       ((memq (car input) '(?\( ?\) ?\| ?+ ?\* ?\?))
	(setq res (cons (make-string 1 (car input)) res))
	(setq input (cdr input)))

       ;; Create "[]" character sets for equivalent characters.
       ((setq val (cdr-safe (assoc (car input) lyskom-char-classes)))
	(setq res (cons "[" res))
	(if (member "]" val)		;"]" must come first.
	    (setq res (cons "]" res)))
	(while val
	  (cond
	   ((string= "]" (car val)))		;already handled
	   ((string= "-" (car val))
	    (setq res (cons "---" res)))
	   (t
	    (setq res (cons (car val) res))))
	  (setq val (cdr val)))
	(setq res (cons "]" res))
	(setq input (cdr input)))

       ;; Copy other characters literally.
       (t
	(setq res (cons (make-string 1 (car input)) res))
	(setq input (cdr input)))))

    (apply 'concat (nreverse res))))


(def-kom-command kom-list-re (regexp &optional case-sensitive what)
  "List all persons and conferences whose names match a particular
regular expression. Regular expressions are case insensitive. You 
will be asked whether to look for persons, conferences, or both.

Lisp documentation:
By default, the regexp will be converted so that the match is
performed in a case insensitive way.  If the optional argument
CASE-SENSITIVE is true, that conversion will not be performed.

If the optional argument WHAT is 'person, only persons will be listed.
If it is 'conf, only conferences will be listed."
  (interactive (let ((xwhat (let ((sel (lyskom-a-or-b-or-c-p 'search-re-for-what
                                                             '(search-re-confs
                                                               search-re-persons
                                                               search-re-all)
                                                             'search-re-all)))
                              (cond
                               ((eq sel 'search-re-all) nil)
                               ((eq sel 'search-re-persons) 'pers)
                               ((eq sel 'search-re-confs) 'conf)
                               (t nil)))))
                 (list (lyskom-read-string
                        (lyskom-get-string 'search-re))
                       current-prefix-arg
                       xwhat)))
  (unless case-sensitive
    (setq regexp (lyskom-make-re-case-insensitive regexp)))
  (lyskom-format-insert  (cond
			  ((eq what 'pers) 'matching-regexp-perss)
			  ((eq what 'conf) 'matching-regexp-confs)
			  (t 'matching-regexp))
			 regexp)
  (let ((conf-list (blocking-do 're-z-lookup regexp
				(if (eq what 'conf) 0 1)
				(if (eq what 'pers) 0 1))))
    (if conf-list
        (if (conf-z-info-list->conf-z-infos conf-list)
            (lyskom-traverse czi (conf-z-info-list->conf-z-infos conf-list)
              (lyskom-format-insert
               "%[%#1@%5#2:m %#3c %#2:M%]\n"
               (lyskom-default-button
                'conf (conf-z-info->conf-no czi))
               czi
               (if (conf-type->letterbox 
                    (conf-z-info->conf-type czi))
                   ?P ?M)
               ))
          (lyskom-format-insert (cond
				 ((eq what 'pers) 'no-matching-perss)
				 ((eq what 'conf) 'no-matching-confs)
				 (t 'no-matching-anys))
				regexp))
      (lyskom-format-insert (lyskom-current-error)))))


;;; ================================================================
;;;                 [ndra namn - Change name

;;; Author: Inge Wallin
;;; Changed by: Peter Eriksson(?)
;;; Changed again: Inge Wallin
;;; Rewritten: linus


(def-kom-command kom-change-name ()
  "Change the name of a person or conference. In order to change the
name of a conference or person, you need to be the supervisor of that
conference or person."
  (interactive)
  (let ((conf-stat (lyskom-read-conf-stat 'name-to-be-changed
                                          '(all) nil nil t)))
    (if (null conf-stat)
	(lyskom-insert-string 'no-such-conf-or-pers)
      (let (name)
	(lyskom-format-insert 'about-to-change-name-from
			      conf-stat)
	(lyskom-scroll)
	(lyskom-tell-internat 'kom-tell-change-name)
	(setq name (lyskom-read-string (lyskom-get-string 'new-name)
				       (conf-stat->name conf-stat)))
	(if (blocking-do 'change-name (conf-stat->conf-no conf-stat) name)
            (progn
              (lyskom-format-insert 'change-name-done name
                                    (lyskom-default-button 'conf conf-stat))
              (cache-del-conf-stat (conf-stat->conf-no conf-stat)))
	  (lyskom-format-insert 'change-name-nope name 
				(lyskom-get-error-text lyskom-errno)
				lyskom-errno))))))

;;; ================================================================
;;;                 [ndra parentes - Change parenthesis

;;; Author: Per Cederqvist (template stolen from kom-change-name)

(def-kom-command kom-change-parenthesis ()
  "Change parenthsized contents in the  name of a person or conference.
In some LysKOM communities it is popular to put a witticism enclosed in
parenthesis at the end of one's name. This command is intended to make
it easy to change this information."
  (interactive)
  (let ((conf-stat (lyskom-read-conf-stat 'name-to-be-changed
                                          '(all) nil nil t)))
    (if (null conf-stat)
	(lyskom-insert-string 'no-such-conf-or-pers)
      (cond 
       ((string-match ".*(.*).*(.*)" (conf-stat->name conf-stat))
        (lyskom-insert-string 'too-many-parens-in-name))
       ((string-match "^\\(.*\\)(\\(.*\\))\\(.*\\)$" (conf-stat->name conf-stat))
        (let* ((pre-paren (match-string 1 (conf-stat->name conf-stat)))
               (post-paren (match-string 3 (conf-stat->name conf-stat)))
               (old-paren (match-string 2 (conf-stat->name conf-stat)))
               (paren (lyskom-read-string (lyskom-get-string 'new-paren)
                                          old-paren))
               (name (concat pre-paren "(" paren ")" post-paren)))
          (if (blocking-do 'change-name (conf-stat->conf-no conf-stat) name)
              (progn
                (lyskom-format-insert 'change-name-done name
                                      (lyskom-default-button 'conf conf-stat))
                (cache-del-conf-stat (conf-stat->conf-no conf-stat)))
            (lyskom-format-insert 'change-name-nope name
                                  (lyskom-get-error-text lyskom-errno)
                                  lyskom-errno))))
       (t (lyskom-insert-string 'no-paren-in-name))))))
	    


;;; ================================================================
;;;                [ndra organisat|r - Change supervisor

;;; Author: Inge Wallin
;;; Rewritten: linus

(def-kom-command kom-change-supervisor ()
  "Change the supervisor of a person or conference. You need to be the
supervisor of the person or conference to perform this operation."
  (interactive)
  (let ((supervisee (lyskom-read-conf-stat 'who-to-change-supervisor-for
                                           '(all) nil nil t)))
    (if (null supervisee)
	(lyskom-insert-string 'no-such-conf-or-pers)
      (lyskom-tell-internat 'kom-tell-change-supervisor)
      (let ((supervisor (lyskom-read-conf-stat 'new-supervisor
                                               '(all) nil nil t)))
	(lyskom-format-insert 'change-supervisor-from-to
			      supervisee
			      supervisor)
	(if (blocking-do 'set-supervisor 
			 (conf-stat->conf-no supervisee) 
			 (conf-stat->conf-no supervisor))
	    (progn
	      (lyskom-insert-string 'done)
	      (cache-del-conf-stat (conf-stat->conf-no supervisee)))
	  (lyskom-format-insert
	   'change-supervisor-nope
	   supervisee))))))


;;; ================================================================
;;;         Markera och Avmarkera - Mark and Unmark a text

;;; Author: Inge Wallin
;;; Modified by: Linus Tolke, Johan Sundström, Joel Rosdahl

(def-kom-command kom-mark-text (&optional text-no)
  "Mark a text.
The settings `kom-symbolic-marks-alist' and `kom-default-mark' control
what mark to set."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-mark)))
  (if text-no
      (lyskom-mark-text text-no)
    (lyskom-insert 'confusion-what-to-mark)))


(def-kom-command kom-unmark-text (&optional text-no)
  "Unmark a text that was previously marked."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-unmark)))
  (if text-no
      (lyskom-unmark-text text-no)
    (lyskom-insert 'confusion-what-to-unmark)))


(defun lyskom-unmark-text (text-no)
  "Do the actual unmarking of the text TEXT-NO."
  (lyskom-format-insert 'unmarking-textno text-no)

  (if (blocking-do 'unmark-text text-no)
      (progn
	(lyskom-insert-string 'done)
	(cache-del-marked-text text-no))
    (lyskom-insert-string 'nope))     ;+++ lyskom-errno?
  (cache-del-text-stat text-no))


(defun lyskom-mark-text (text-no &optional mark)
  "Mark TEXT-NO using kom-default-mark (if non-nil) or prompt
the user for what mark to use."
  (let ((mark
	 (or mark
	     kom-default-mark
	     (lyskom-read-mark-type (lyskom-get-string 'what-mark) nil t))))
    (lyskom-format-insert 'marking-textno text-no 
                          (lyskom-symbolic-mark-type-string mark t)
                          mark)

    (if (blocking-do 'mark-text text-no mark)
        (progn
          (lyskom-insert-string 'done)
          (cache-add-marked-text text-no mark))
      (lyskom-insert-string 'nope))     ;+++ lyskom-errno?
    (cache-del-text-stat text-no)))

(defun lyskom-read-mark-type (prompt &optional nildefault create-nonexistent)
  "Ask user about symbolic mark type and return the (integer)
mark-type.  Prompt with PROMPT.  If NILDEFAULT is non-nil, nil is
returned if the user enters the empty string, otherwise the user is
prompted again. If CREATE-NONEXISTENT is t, the user is asked whether
the symbolic mark association should be created if it doesn't already
exist."
  (let ((mark-type nil) ; nil: not yet set, 'default: default chosen
        (completion-ignore-case t)
        (completions kom-symbolic-marks-alist))
    (while (and (not (eq mark-type 'default))
                (or (not (integerp mark-type))
                    (< mark-type 0)
                    (> mark-type 255)))
      (let* ((mark (lyskom-completing-read
                    prompt
                    (lyskom-maybe-frob-completion-table completions)))
             (mark-assoc (lyskom-string-assoc mark completions)))
        (when (null mark)
          (setq mark ""))
        (cond
         ;; Default completion.
         ((and nildefault (string-equal "" mark))
          (setq mark-type 'default))

         ((null mark))

         ;; Correct completion.
         (mark-assoc
          (setq mark-type (cdr mark-assoc)))

         ;; Incorrect completion, integer entered.
         ((string-match "\\`\\s-*[0-9]+\\s-*\\'" mark)
          (setq mark-type (lyskom-string-to-number mark)))

         ;; Incorrect completion; create new symbolic mark type.
         ((and create-nonexistent
               (not (string= mark ""))
               (lyskom-j-or-n-p
                (lyskom-format (lyskom-get-string
                                'want-to-create-symbolic-mark)
                               mark)))
          (let ((new-mark-type (lyskom-allocate-mark-type)))
            (if (not new-mark-type)
                (lyskom-insert 'no-mark-types-left)
              (lyskom-format-insert 'creating-symbolic-mark-type
                                    mark
                                    new-mark-type)
              (setq kom-symbolic-marks-alist
                    (cons (cons mark new-mark-type)
                          kom-symbolic-marks-alist))
              (lyskom-save-options
               (current-buffer)
               (lyskom-get-string 'saving-settings)
               (lyskom-get-string 'saving-settings-done)
               (lyskom-get-string 'could-not-save-options))
              (setq mark-type new-mark-type))))

         ;; Incorrect completion.
         (t (lyskom-insert 'erroneous-mark)))))

    (if (eq mark-type 'default)
        nil
      mark-type)))


(defun lyskom-allocate-mark-type ()
  "Returns the first mark type available that is neither named nor used.
If no such type existed, the least used non-named mark type is returned.
If no such existed either, nil is returned."
  (setq kom-symbolic-marks-alist
        (sort kom-symbolic-marks-alist
              (function (lambda (x y) (< (cdr x) (cdr y))))))
  (let ((i 0)
        (list kom-symbolic-marks-alist)
	(used (lyskom-get-least-used-mark-types-alist))
        (found nil))
    (while (and (not found)
                (< i 256))
      (when (not (assq i used)) ; mark type i not presently used?
	(when list
	  (if (= i (cdr (car list))) ; already named?
	      (setq list (cdr list))
	    (setq found t)))) ; neither used nor named!
      (when (not found)
	(++ i)))
    (if (and found (< i 256))
        i
      ; no unused and unnamed mark type available; fall back
      ; to the least used not-yet-named type, if available:
      (when (and used
		 (< (length used) 256)
		 (< (length list) 256))
	(cdr (car used))))))


(defun lyskom-get-least-used-mark-types-alist ()
  "Returns an alist from mark type to number of such marks, ordered by
increasing number of marks per mark type (and, when equal, by mark type)."
  (let ((mark-list (cache-get-marked-texts))
	(cnt-alist nil)) ; the number of texts marked by each mark type
    ;; Count the number of texts marked per mark type:
    (while (not (null mark-list))
      (let ((mark (car mark-list)))
        (when mark
          (let* ((type (mark->mark-type mark))
                 (tcnt (assq type cnt-alist)))
            (when tcnt (setq tcnt (cdr tcnt)))
            (setq cnt-alist
                  (lyskom-set-alist
                   cnt-alist type (if (null tcnt) 1 (1+ tcnt)))))))
      (setq mark-list (cdr mark-list)))

    ;; Sort the list, least-used, lowest number of mark type first:
    (sort cnt-alist
	  (function (lambda (x y)
		      (cond
		       ((< (cdr x) (cdr y)) t)
		       ((= (cdr x) (cdr y)) (< (car x) (car y)))
		       (t nil)))))))



;;; ================================================================
;;;          ]terse alla markerade - Review marked texts

;;; Author: Inge Wallin
;;; Modified by: Joel Rosdahl

(def-kom-command kom-review-marked-texts ()
  "Review all texts you have marked with a particular mark."
  (interactive)
  (lyskom-review-marked-texts
   (lyskom-read-mark-type (lyskom-get-string 'what-mark-to-view) t)))


(def-kom-command kom-review-all-marked-texts ()
  "Review all texts you have marked, regardless of mark."
  (interactive)
  (lyskom-review-marked-texts nil))

(def-kom-command kom-unread-marked-texts ()
  "Mark all texts you have marked with a particular mark as unread."
  (interactive)
  (lyskom-unread-marked-texts
   (lyskom-read-mark-type (lyskom-get-string 'what-mark-to-unread) t)))


(def-kom-command kom-unread-all-marked-texts ()
  "Mark all texts you have marked, regardless of mark, as unread."
  (interactive)
  (lyskom-unread-marked-texts nil))
  

(defun lyskom-unread-marked-texts (mark-no)
  "Mark all marked texts with the mark equal to MARK-NO unread. 
If MARK-NO is nil, review all marked texts."
  (let ((text-list (lyskom-get-marked-texts mark-no)))
    (if (null text-list)
	(lyskom-insert (if (null mark-no)
			   (lyskom-get-string 'no-marked-texts)
			 (lyskom-format 'no-marked-texts-mark
                                        (lyskom-symbolic-mark-type-string
                                         mark-no))))
      (lyskom-traverse text-no text-list
        (lyskom-format-insert 'marking-text-unread text-no)
        (lyskom-report-command-answer
         (lyskom-mark-unread text-no))))))
  

(defun lyskom-review-marked-texts (mark-no)
  "Review all marked texts with the mark equal to MARK-NO. 
If MARK-NO is nil, review all marked texts."
  (let ((text-list (lyskom-get-marked-texts mark-no)))
    (if (null text-list)
	(lyskom-insert (if (null mark-no)
			   (lyskom-get-string 'no-marked-texts)
			 (lyskom-format 'no-marked-texts-mark
                                        (lyskom-symbolic-mark-type-string
                                         mark-no))))
      (let ((read-info (lyskom-create-read-info
			'REVIEW-MARK nil 
			(lyskom-review-get-priority)
			(lyskom-create-text-list text-list)
			nil t)))
	(read-list-enter-read-info read-info lyskom-reading-list t)
	(read-list-enter-read-info read-info lyskom-to-do-list t)))))

(defun lyskom-get-marked-texts (mark-no)
  "Return a list of all texts marked with MARK-NO."
  (let ((text-list nil))
    (lyskom-traverse mark (cache-get-marked-texts)
      (if (and mark
               (or (null mark-no)
                   (eq mark-no (mark->mark-type mark))))
          (setq text-list (cons (mark->text-no mark)
                                text-list))))
    text-list))


;;; ================================================================
;;;                 [ndra L|senord - Change password

;;; Author: Inge Wallin


(def-kom-command kom-change-password ()
  "Change the password for a person. To change the password for a
person you need either the old password for the person, or have
administrative privileges enabled."
  (interactive)
  (let ((pers-no (lyskom-read-conf-no 'whos-passwd '(pers) nil nil t))
	(old-pw (lyskom-silent-read (lyskom-get-string 'old-passwd)))
	(new-pw1 (lyskom-silent-read (lyskom-get-string 'new-passwd)))
	(new-pw2 (lyskom-silent-read (lyskom-get-string 'new-passwd-again))))

    (if (lyskom-string= new-pw1 new-pw2)
	(progn
	  (lyskom-insert-string 'changing-passwd)
	  (lyskom-report-command-answer 
	   (blocking-do 'set-passwd (if (zerop pers-no)
					lyskom-pers-no
				      pers-no)
			old-pw new-pw1)))
      (lyskom-insert-string 'retype-dont-match))))



;;; ================================================================
;;;               (Se) Tiden - display time and date.

(defconst lyskom-times
  '(((nil  1  1 nil nil nil) . newyearday)
    ((nil  1  6 nil nil nil) . 13dayxmas)
    ((nil  1 13 nil nil nil) . 20dayxmas)
    ((nil  2  2 nil nil nil) . kyndeldag)
    ((nil  2 29 nil nil nil) . skottdag)
    ((nil  3  8 nil nil nil) . intwomday)
    ((nil  3 25 nil nil nil) . mariebdag)
    ((nil  3 29 nil nil nil) . lysbday)
    ((nil  4 30 nil nil nil) . cgdag)
    ((nil  5 25 nil nil nil) . towelday)
    ((nil  6  6 nil nil nil) . sixjune)
    ((nil  6 24 nil nil nil) . johannesdday)
    ((nil  8 15 nil nil nil) . holdnose)
    ((nil 10 24 nil nil nil) . fnday)
    ((nil 11  1 nil nil nil) . allhelgonadag)
    ((nil 12 13 nil nil nil) . luciaday)
    ((nil 12 24 nil nil nil) . xmaseve)
    ((nil 12 25 nil nil nil) . xmasday)
    ((nil 12 28 nil nil nil) . varnlosdag)
    ((nil 12 31 nil nil nil) . newyeareve)
    ((nil 12 31  23 nil nil) . newyearevelate)
))


(lyskom-with-external-functions (calendar-iso-from-absolute
                                 calendar-absolute-from-gregorian)
  (def-kom-command kom-display-time ()
    "Display the current date and time, according to the server. If
`kom-show-week-number', include the ISO week number. Display of
today's name is controlled by `kom-show-namedays'."
    (interactive)
    (let ((time (lyskom-current-server-time))
          (lyskom-last-text-format-flags nil)
          (weekno nil))
      (lyskom-format-insert
       (if kom-show-week-number
           (condition-case nil
               (progn (require 'calendar)
                      (require 'cal-iso)
                      (setq weekno
                            (car (calendar-iso-from-absolute
                                  (calendar-absolute-from-gregorian 
                                   (list (time->mon time)
                                         (time->mday time)
                                         (time->year time))))))
                      'time-is-week)
             (error 'time-is))
         'time-is)
       (lyskom-format-time 'timeformat-day-yyyy-mm-dd-hh-mm-ss time)
       ;; Kult:
       (if (and (= (time->hour time)
                   (+ (/ (time->sec time) 10)
                      (* (% (time->sec time) 10) 10)))
                (= (/ (time->min time) 10)
                   (% (time->min time) 10)))
           (lyskom-get-string 'palindrome)
         "")
       weekno)
      ;; Mera kult
      (mapc (function 
               (lambda (el)
                 (let ((when (car el))
                       (event (cdr el)))
                   (if (and (or (null (elt when 0))
                                (= (time->year time) (elt when 0)))
                            (or (null (elt when 1))
                                (= (time->mon time) (elt when 1)))
                            (or (null (elt when 2))
                                (= (time->mday time) (elt when 2)))
                            (or (null (elt when 3))
                                (= (time->hour time) (elt when 3)))
                            (or (null (elt when 4))
                                (= (time->min time) (elt when 4)))
                            (or (null (elt when 5))
                                (= (time->sec time) (elt when 5))))
                       (condition-case nil
                           (progn
                             (lyskom-insert " ")
                             (lyskom-format-insert 
                              "%#1t"
                              (lyskom-format event
                                             (time->year time)
                                             (time->mon  time)
                                             (time->mday time)
                                             (time->hour time)
                                             (time->min  time)
                                             (time->sec  time))))
                         (error nil))))))
              lyskom-times)

      (when kom-show-namedays
        (let ((tmp (lyskom-nameday time)))
          (when tmp
            (lyskom-insert "\n")
            (lyskom-insert tmp)))))
    (lyskom-insert "\n"))

)




;;; ================================================================
;;;                Vilka ({r inloggade) - Who is on?
;;;
;;; Author: ???
;;; Rewritten by: David K}gedal


(put 'lyskom-no-users 'error-conditions
     '(error lyskom-error lyskom-no-users))

(def-kom-command kom-who-is-on (&optional arg)
  "Display a list of active connected users.

With a positive prefix argument ARG, list sessions who have been
active in the last ARG minutes. With a positive zero prefix argument
\(i.e. \"0\", not \"-0\"), list all visible sessions. With a negative
nonzero prefix argument ARG, list both visible and invisible sessions
who have been active in the last -ARG minutes. With a negative zero
prefix argument (C-u -), list all sessions.

Several variables affect display. See `kom-show-where-and-what', 
`kom-show-since-and-when' and `kom-idle-hide' for more information."
  (interactive "P")
  (condition-case nil
      (if (lyskom-have-feature dynamic-session-info)
	  (lyskom-who-is-on-9 arg)
	(lyskom-who-is-on-8))
    (lyskom-no-users
     (lyskom-insert (lyskom-get-string 'null-who-info)))))

;;; ================================================================
;;;                Vilka ({r inloggade i) möte - Who is on in a conference?

;;; Author: petli

(def-kom-command kom-who-is-on-in-conference (&optional arg)
  "Display a list of all connected users who are members of a
particular conference.

With a positive prefix argument ARG, list sessions who have been
active in the last ARG minutes. With a positive zero prefix argument
\(i.e. \"0\", not \"-0\"), list all visible sessions. With a negative
nonzero prefix argument ARG, list both visible and invisible sessions
who have been active in the last -ARG minutes. With a negative zero
prefix argument \(C-u -), list all sessions.

Several variables affect display. See `kom-show-where-and-what',
`kom-show-since-and-when' and `kom-idle-hide' for more information."
  (interactive "P")
  (let ((conf-stat 
	 (lyskom-read-conf-stat 'who-is-on-in-what-conference
                                '(all) nil nil t)))
    (condition-case nil
	(if (lyskom-have-feature dynamic-session-info)
	    (lyskom-who-is-on-9 arg conf-stat)
	  (lyskom-who-is-on-8 conf-stat))
      (lyskom-no-users
       (lyskom-insert (lyskom-get-string 'null-who-info))))))

;;; ================================================================
;;;                Vilka ({r n{rvarande i) möte - Who is present in a conference?

;;; Author: Christer Ekholm
;;; Copied from kom-who-is-on-in-conference by petli

(def-kom-command kom-who-is-present-in-conference (&optional arg)
  "Display a list of all connected users currently present in a
particular conference.

With a positive prefix argument ARG, list sessions who have been
active in the last ARG minutes. With a positive zero prefix argument
\(i.e. \"0\", not \"-0\"), list all visible sessions. With a negative
nonzero prefix argument ARG, list both visible and invisible sessions
who have been active in the last -ARG minutes. With a negative zero
prefix argument \(C-u -), list all sessions.

Several variables affect display. See `kom-show-where-and-what',
`kom-show-since-and-when' and `kom-idle-hide' for more information."
  (interactive "P")
  (let ((conf-stat 
	 (lyskom-read-conf-stat 'who-is-present-in-what-conference
				'(all) nil nil t)))
    (condition-case nil
	(if (lyskom-have-feature dynamic-session-info)
	    (lyskom-who-is-on-9 arg conf-stat t)
	  (lyskom-who-is-on-8 conf-stat t))
      (lyskom-no-users
       (lyskom-insert (lyskom-get-string 'null-who-info))))))

;;; ================================================================
;;;                Vilka vänner (är inloggade) - What friends are on

;;; Author: Ulrik Haugen

(def-kom-command kom-who-is-on-and-friend (&optional arg)
  "Display a list of all connected users in `kom-friends'.

With a positive prefix argument ARG, list sessions who have been
active in the last ARG minutes. With a positive zero prefix argument
\(i.e. \"0\", not \"-0\"), list all visible sessions. With a negative
nonzero prefix argument ARG, list both visible and invisible sessions
who have been active in the last -ARG minutes. With a negative zero
prefix argument \(C-u -), list all sessions.

Several variables affect display. See `kom-show-where-and-what',
`kom-show-since-and-when' and `kom-idle-hide' for more information."
  (interactive "P")
  (condition-case nil
      (if (lyskom-have-feature dynamic-session-info)
	  (lyskom-who-is-on-9 arg nil nil t)
	(lyskom-who-is-on-8 nil nil t))
    (lyskom-no-users
     (lyskom-insert (lyskom-get-string 'null-who-info)))))

(defun lyskom-who-is-on-8 (&optional conf-stat show-present-only
				     show-friends-only)
  "Display a list of all connected users.
Uses Protocol A version 8 calls"
  (let* ((who-info-list (blocking-do 'who-is-on))
	 (who-list (sort
		    (funcall (if show-friends-only
				 #'lyskom-select-friends-from-who-list
			       #'identity)
			     (cond (show-present-only
				    (lyskom-who-is-present-check-membership-8
				     who-info-list conf-stat))
				   (conf-stat
				    (lyskom-who-is-on-check-membership-8
				     who-info-list conf-stat))
				   (t
				    (listify-vector who-info-list))))
			     (function (lambda (who1 who2)
					 (< (who-info->connection who1)
					    (who-info->connection who2))))))
	 (total-users (length who-list))
	 (session-width (1+ (length (int-to-string
				     (who-info->connection
				      (nth (1- total-users) who-list))))))
	 (format-string-1 (lyskom-info-line-format-string
			   session-width "P" "M"))
	 (format-string-2 (lyskom-info-line-format-string
			   session-width "s" "s"))
	 (lyskom-default-conf-string 'not-present-anywhere)
         (lyskom-default-pers-string 'unknown-person))

    (cond (show-friends-only
           (lyskom-format-insert 'who-is-friend))
          (show-present-only
	   (lyskom-format-insert 'who-is-active-and-present conf-stat))
	  (conf-stat
	   (lyskom-format-insert 'who-is-active-and-member conf-stat)))
	  
    (lyskom-format-insert format-string-2
			  ""
			  (lyskom-get-string 'lyskom-name)
			  (lyskom-get-string 'is-in-conf))
    (if kom-show-where-and-what
	(lyskom-format-insert format-string-2
			      ""
			      (lyskom-get-string 'from-machine)
			      (lyskom-get-string 'is-doing)))
    
    (lyskom-insert
     (concat (make-string (- (lyskom-window-width) 1) ?-) "\n"))
    
    (while who-list
      (let* ((who-info (car who-list))
	     (session-no (int-to-string (who-info->connection who-info)))
	     (my-session (if (= lyskom-session-no
				(who-info->connection who-info))
			     "*"
			   " ")))
	(lyskom-format-insert
	 format-string-1
	 (concat session-no my-session)
	 (who-info->pers-no who-info)
	 (or (who-info->working-conf who-info)
	     (lyskom-get-string 'not-present-anywhere)))
	(if kom-show-where-and-what
	    (lyskom-format-insert
	     format-string-2
	     ""
	     (lyskom-return-username who-info)
	     (concat "(" (or (string-replace-match "\n" (who-info->doing-what who-info) " " t t)
                             (who-info->doing-what who-info)")")))))
      (setq who-list (cdr who-list)))
      
      (lyskom-insert (concat (make-string (- (lyskom-window-width) 1) ?-)
			     "\n"))
      (lyskom-insert (lyskom-format 'total-visible-users total-users
                                    (lyskom-format-time
                                     'timeformat-day-yyyy-mm-dd-hh-mm-ss)))))


(defun lyskom-who-is-on-9 (arg &optional conf-stat show-present-only
			       show-friends-only)
  "Display a list of all connected users.
Uses Protocol A version 9 calls"
  (let* ((wants-invisibles (or (and (numberp arg) (< arg 0))
                               (and (symbolp arg) (eq '- arg))))
	 (idle-hide (if (numberp arg) (abs arg) 
                      (cond ((eq '- arg) 0)
                            ((numberp kom-idle-hide) kom-idle-hide)
                            (kom-idle-hide 30)
                            (t 0))))
	 (who-info-list (blocking-do 'who-is-on-dynamic
				     't wants-invisibles (* idle-hide 60)))
	 (who-list (sort 
		    (funcall (if show-friends-only
				 #'lyskom-select-friends-from-who-list
			       #'identity)
			     (cond (show-present-only
				    (lyskom-who-is-present-check-membership-9
				     who-info-list conf-stat))
				   (conf-stat
				    (lyskom-who-is-on-check-membership-9
				     who-info-list conf-stat))
				   (t
				    (listify-vector who-info-list))))
			 (function
			  (lambda (who1 who2)
			    (< (dynamic-session-info->session who1)
			       (dynamic-session-info->session who2))))))
	 (total-users (length who-list))
	 (session-width (if (null who-list)
			    (signal 'lyskom-no-users nil)
			  (1+ (length (int-to-string
				       (dynamic-session-info->session
					(nth (1- total-users) who-list)))))))
	 (format-string-1 (lyskom-info-line-format-string
			   session-width "P" "M"))
	 (format-string-2 (lyskom-info-line-format-string
			   session-width "D" "s"))
	 (format-string-3 (lyskom-info-line-format-string
			   session-width "D" "s"))
	 (lyskom-default-conf-string 'not-present-anywhere)
         (lyskom-default-pers-string 'unknown-person))

    (if (zerop idle-hide)
	(lyskom-insert (lyskom-get-string 'who-is-active-all))
      (lyskom-format-insert 'who-is-active-last-minutes idle-hide))

    (if wants-invisibles
	(lyskom-insert (lyskom-get-string 'showing-invisibles)))

    (cond (show-friends-only
           (lyskom-format-insert 'who-is-friend))
          (show-present-only
	   (lyskom-format-insert 'who-is-active-and-present conf-stat))
	  (conf-stat
	   (lyskom-format-insert 'who-is-active-and-member conf-stat)))
			  
    (lyskom-format-insert format-string-2
			  ""
			  (lyskom-get-string 'lyskom-name)
			  (lyskom-get-string 'is-in-conf))
    (if kom-show-where-and-what
	(lyskom-format-insert format-string-2
			      ""
			      (lyskom-get-string 'from-machine)
			      (lyskom-get-string 'is-doing)))

    (if kom-show-since-and-when
	(lyskom-format-insert format-string-3
			      ""
			      (lyskom-get-string 'connection-time)
			      (lyskom-get-string 'active-last)))
    
    (lyskom-insert
     (concat (make-string (- (lyskom-window-width) 1) ?-) "\n"))
    
    (while who-list
      (let* ((who-info (car who-list))
	     (session-no (dynamic-session-info->session who-info))
	     (session-no-s (int-to-string session-no))
	     (my-session (if (= lyskom-session-no session-no)
			     "*"
			   " ")))
	(lyskom-format-insert
	 format-string-1
	 (concat session-no-s my-session)
         (dynamic-session-info->person who-info)
	 (or (dynamic-session-info->working-conference who-info)
	     (lyskom-get-string 'not-present-anywhere)))
	(if kom-show-where-and-what
	    (let* (static defer-info username)
	      (cond (kom-deferred-printing
		     (setq static (cache-get-static-session-info session-no))
		     (if static
			 (setq username
			       (lyskom-combine-username
				(static-session-info->username static)
				(static-session-info->ident-user static)
				(static-session-info->hostname static)))
		       (setq defer-info
			     (lyskom-create-defer-info
			      'get-static-session-info
			      session-no
			      'lyskom-insert-deferred-session-info
			      (make-marker)
			      (length lyskom-defer-indicator)
			      "%#1s"))
		       (setq username defer-info)))
		    (t
		     (setq static
			   (blocking-do 'get-static-session-info session-no))
		     (setq username (lyskom-combine-username
				     (static-session-info->username static)
				     (static-session-info->ident-user static)
				     (static-session-info->hostname static)))))
	      (lyskom-format-insert
	       format-string-2
	       ""
	       username
	       (concat "(" (or (string-replace-match "\n" (dynamic-session-info->what-am-i-doing who-info) " " t t)
                               (dynamic-session-info->what-am-i-doing who-info))
		       ")"))))
	(if kom-show-since-and-when
	    (let ((active 
                   (if (session-flags->user_active_used 
                        (dynamic-session-info->flags who-info))
                       (if (< (dynamic-session-info->idle-time who-info) 60)
                           (lyskom-get-string 'active)
                         (lyskom-format-secs
                          (dynamic-session-info->idle-time who-info)))
                     (lyskom-get-string 'Unknown2))
                     )
		  defer-info
		  static
		  since)
	      (cond (kom-deferred-printing
		     (setq static (cache-get-static-session-info session-no))
		     (if static
			 (setq since
			       (upcase-initials
				(lyskom-format-time
                                 'timeformat-day-yyyy-mm-dd-hh-mm-ss
				 (static-session-info->connection-time static))))
		       (setq defer-info
			     (lyskom-create-defer-info
			      'get-static-session-info
			      session-no
			      'lyskom-insert-deferred-session-info-since
			      (make-marker)
			      (length lyskom-defer-indicator)
			      "%#1s"))
		       (setq since defer-info)))
		    (t
		     (setq static
			   (blocking-do 'get-static-session-info session-no))
		     (setq since (upcase-initials
				  (lyskom-format-time
                                 'timeformat-day-yyyy-mm-dd-hh-mm-ss
				   (static-session-info->connection-time static))))))
	      (lyskom-format-insert
	       format-string-3
	       ""
	       since
	       active)))
	
	(setq who-list (cdr who-list))))
    
    (lyskom-insert (concat (make-string (- (lyskom-window-width) 1) ?-)
			   "\n"))
    (lyskom-insert (lyskom-format
		    (cond ((and wants-invisibles (zerop idle-hide))
			   'total-users)
			  (wants-invisibles
			   'total-active-users)
			  ((zerop idle-hide)
			   'total-visible-users)
			  (t
			   'total-visible-active-users))
		    total-users
                    (lyskom-format-time
                     'timeformat-day-yyyy-mm-dd-hh-mm-ss)))))

(defun lyskom-who-is-on-check-membership-8 (who-info-list conf-stat)
  "Returns a list of those in WHO-INFO-LIST which is member in CONF-STAT."
  (let ((members (blocking-do 'get-members (conf-stat->conf-no conf-stat)
                              0 (conf-stat->no-of-members conf-stat)))
	(len (length who-info-list))
	(i 0)
	(res nil)
        (mship nil))
    (while (< i len)
      (setq mship (lyskom-member-list-find-member
                   (who-info->pers-no (aref who-info-list i))
                   members))
      (when (and mship
               (not (membership-type->passive 
                     (member->membership-type mship))))
	  (setq res (cons (aref who-info-list i) res)))
      (setq i (1+ i)))
    res))

(defun lyskom-who-is-on-check-membership-9 (who-info-list conf-stat)
  "Returns a list of those in WHO-INFO-LIST which is member in CONF-STAT."
  (let ((members (blocking-do 'get-members (conf-stat->conf-no conf-stat)
			      0 (conf-stat->no-of-members conf-stat)))
	(len (length who-info-list))
	(i 0)
	(res nil)
        (mship nil))
    (while (< i len)
      (setq mship (lyskom-member-list-find-member
                   (dynamic-session-info->person (aref who-info-list i))
                   members))
      (when (and mship
               (not (membership-type->passive 
                     (member->membership-type mship))))
	  (setq res (cons (aref who-info-list i) res)))
      (setq i (1+ i)))
    res))

(defun lyskom-who-is-present-check-membership-8 (who-info-list conf-stat)
  "Returns a list of those in WHO-INFO-LIST which is present in CONF-STAT."
  (let ((len (length who-info-list))
	(i 0)
	(res nil))
    (while (< i len)
      (if (eq (who-info->working-conf (aref who-info-list i))
	      (conf-stat->conf-no conf-stat))
	  (setq res (cons (aref who-info-list i) res)))
      (setq i (1+ i)))
    res))

(defun lyskom-who-is-present-check-membership-9 (who-info-list conf-stat)
  "Returns a list of those in WHO-INFO-LIST which is present in CONF-STAT."
  (let ((len (length who-info-list))
	(i 0)
	(res nil))
    (while (< i len)
      (if (eq (dynamic-session-info->working-conference (aref who-info-list i))
	      (conf-stat->conf-no conf-stat))
	  (setq res (cons (aref who-info-list i) res)))
      (setq i (1+ i)))
    res))

(defun lyskom-select-friends-from-who-list (who-list)
  "Returns a list of friends in WHO-LIST"
  (let ((result nil)
        (extended-friends (cons lyskom-pers-no kom-friends)))
    (while (not (null who-list))
      (when (memq (dynamic-session-info->person (car who-list))
		  extended-friends)
	(setq result (cons (car who-list) result)))
      (setq who-list (cdr who-list)))
    result))

(defun lyskom-insert-deferred-session-info (session-info defer-info)
  (if session-info
      (lyskom-replace-deferred defer-info
			       (lyskom-combine-username
				(static-session-info->username session-info)
				(static-session-info->ident-user session-info)
				(static-session-info->hostname session-info)))
    (lyskom-replace-deferred defer-info "")))

(defun lyskom-insert-deferred-session-info-since (session-info defer-info)
  (if session-info
      (lyskom-replace-deferred defer-info
			       (upcase-initials
				(lyskom-format-time
                                 'timeformat-day-yyyy-mm-dd-hh-mm-ss
				 (static-session-info->connection-time session-info))))
    (lyskom-replace-deferred defer-info "")))

;;; =====================================================================
;;;                 Lista klienter - List clients
;;; Author: David Kågedal
;;; Modified: Daivd Byers

		 
(def-kom-command kom-list-clients (prefix)
  "Display a list of all connected sessions, including current logon
and client software.

With a positive prefix argument ARG, list sessions who have been
active in the last ARG minutes. With a positive zero prefix argument
\(i.e. \"0\", not \"-0\"), list all visible sessions. With a negative
nonzero prefix argument ARG, list both visible and invisible sessions
who have been active in the last -ARG minutes. With a negative zero
prefix argument \(C-u -), list all sessions."
  (interactive "P")
  (let* ((want-invisible (or (eq '- prefix) (and (numberp prefix) (< prefix 0))))
         (who-info-list (blocking-do 'who-is-on-dynamic t want-invisible
				     (if (numberp prefix) (* (abs prefix) 60) nil)))
         (who-list (sort (listify-vector who-info-list)
                         (function
                          (lambda (who1 who2)
                            (< (dynamic-session-info->session who1)
                               (dynamic-session-info->session who2))))))
	 (total-users (length who-list))
	 (s-width (1+ (length (int-to-string
			       (dynamic-session-info->session
				(nth (1- total-users) who-list))))))
	 (format-string (lyskom-info-line-format-string
			 s-width "P" (if kom-deferred-printing "D" "s")))
         (collect (make-collector)))
    (lyskom-format-insert format-string
			  ""
			  (lyskom-get-string 'lyskom-name)
			  (lyskom-get-string 'lyskom-client))
    (lyskom-insert
     (concat (make-string (- (lyskom-window-width) 2) ?-) "\n"))

    (while who-list
      (let* ((who-info (car who-list))
	     (session-no (int-to-string (dynamic-session-info->session who-info)))
	     (my-session (if (= lyskom-session-no
				(dynamic-session-info->session who-info))
			     "*"
			   " "))
	     (client (if kom-deferred-printing
			 (lyskom-create-defer-info
			  'get-client-name
			  (dynamic-session-info->session who-info)
			  'lyskom-deferred-client-1
			  nil nil nil	; Filled in later
			  (list (dynamic-session-info->session who-info)
                                collect))
		       (blocking-do-multiple
			   ((name (get-client-name
				   (dynamic-session-info->session who-info)))
			    (version (get-client-version
				      (dynamic-session-info->session who-info))))
                         (lyskom-list-clients-collect name version collect)
			 (concat name " " version)))))
	(lyskom-format-insert
	 format-string
	 (concat session-no my-session)
	 (dynamic-session-info->person who-info)
	 client))
      (setq who-list (cdr who-list)))

    (lyskom-insert (concat (make-string (- (lyskom-window-width) 2) ?-)
			   "\n"))
    (lyskom-insert (lyskom-format (if want-invisible
				      (if (numberp prefix)
					  'total-active-users
					'total-users)
				    (if (and (numberp prefix) (/= prefix 0))
					'total-visible-active-users
				      'total-visible-users))
				  total-users
				  (lyskom-format-time
				   'timeformat-day-yyyy-mm-dd-hh-mm-ss)))
    (lyskom-format-insert "%#1D\n"
                          (lyskom-create-defer-info
                           'get-time nil
                           'lyskom-list-clients-statistics-1
                           nil nil nil collect))
))


(defun lyskom-list-clients-collect (client version collect)
  "Collect client statistics"
  (let* ((uversion
	  (cond ((null version) version)
		((null client) version)
		((string-match "(.*; CVS)" version)
		 (replace-in-string version "(.*; CVS)" "(from CVS)"))
		((string-match "(.*; Git.*)" version)
		 (replace-in-string version "(.*; Git.*)" "(from Git)"))
		((string-match "\\s-*(build [^)]*)" version)
		 (replace-in-string version "\\s-*(build [^)]*)" ""))
		(t version)))
	 (name (concat (or client "") (if uversion " " "") (or uversion "")))
	 (el (assoc name (collector->value collect))))
      (if el
	  (progn (aset (cdr el) 0 (1+ (aref (cdr el) 0)))
		 (unless (lyskom-string-member (or version "") (aref (cdr el) 1))
		   (aset (cdr el) 1 (cons version (aref (cdr el) 1)))))
	(set-collector->value collect (cons (cons name (vector 1 (list version)))
					    (collector->value collect))))))

(defun lyskom-list-clients-statistics-1 (time arg defer-info)
  (initiate-get-time 'deferred
                     'lyskom-list-clients-statistics-2 
                     defer-info))

(defun lyskom-list-clients-statistics-2 (time defer-info)
  (lyskom-replace-deferred 
   defer-info
   (concat "\n"
           (lyskom-get-string 'client-statistics)
           (mapconcat (lambda (el)
                        (lyskom-format 'client-statistics-line
                                       (if (equal (car el) " ")
                                           (lyskom-get-string 'Unknown)
                                         (car el))
                                       (aref (cdr el) 0)
				       (length (aref (cdr el) 1))))
                      (nreverse
                       (sort (collector->value (defer-info->data defer-info))
                             (lambda (a b)
                               (string-lessp (car a) (car b)))))
                      "\n")
           "\n")))
                                        


(defun lyskom-deferred-client-1 (name defer-info)
  (let* ((data (defer-info->data defer-info))
         (session (if (consp data) (elt data 0) data))
         (collector (if (consp data) (elt data 1) nil)))
    (initiate-get-client-version 'deferred
                                 'lyskom-deferred-client-2
                                 session
                                 defer-info
                                 name
                                 collector
                                 )))

(defun lyskom-deferred-client-2 (version defer-info name collect)
  (when collect (lyskom-list-clients-collect name version collect))
  (lyskom-replace-deferred defer-info (if (zerop (length name))
					  "-"
					(concat name " " version))))



(defun lyskom-info-line-format-string (prefixlen type1 type2)
  "Return a format string suitable for inserting who-info lines etc."
  (let* ((plen (or prefixlen 7))
         (width (- (lyskom-window-width) plen 2)))
    (concat "%" (int-to-string plen) "#1s"
	    "%=-"
	    (int-to-string (/ width 2))
	    "#2" type1
	    " %=-"
	    (int-to-string (+ (/ width 2) (% width 2)))
	    "#3" type2
	    "\n")))
    
(defun lyskom-window-width ()
  "Returns the width of the lyskom-window or the screen-width if not displayed."
  (let ((win (get-buffer-window (current-buffer))))
    (cond
     (win (window-width win))
     (t (lyskom-frame-width)))))


(defun lyskom-return-username (who-info)
  "Takes the username from the WHO-INFO and returns it on a better format."
  (let* ((username (cond ((lyskom-static-session-info-p who-info)
                          (static-session-info->username who-info))
                         ((lyskom-session-info-p who-info)
                          (session-info->username who-info))
                         (t (who-info->username who-info))))
	 (type (or 
		(string-match "\\([^%@.]+\\)%\\(.+\\)@\\([^%@.]+\\)" username)
		(string-match "\\([^%@.]+\\)@\\([^%@.]+\\)" username))))
    (if type
	(let ((name (substring username 0 (match-end 1)))
	      (sent (if (match-beginning 3)
			(substring username
				   (match-beginning 2)
				   (match-end 2))))
	      (gott (if (match-beginning 3)
			(substring username
				   (match-beginning 3)
				   (match-end 3))
		      (substring username
				 (match-beginning 2)
				 (match-end 2))))
	      (rest (substring username (match-end 0))))
	  (if (or (not sent)
		  (lyskom-string= (downcase sent) (downcase gott))
		  (lyskom-string= (downcase sent)
			   (downcase (concat gott rest))))
	      (concat name "@" gott rest)
	    (concat name "@" sent " (" gott rest ")")))
      username)))


(defun lyskom-combine-username (username identname hostname)
  "Return a description of from where a user is logged in."
  ;; Ignore ident info for now
  (setq hostname (downcase hostname))
  (if (string-match "\\(.*\\)%\\(.*\\)" username)
      (let ((user (substring username (match-beginning 1) (match-end 1)))
	    (uhost (downcase (substring username (match-beginning 2) (match-end 2)))))
	(if (lyskom-string= uhost hostname)
	    (concat user "@" hostname)
	  (concat username "@" hostname)))
    (concat username "@" hostname)))


;;; ================================================================
;;;            Status (för) Session - Status (for a) session
;;;
;;; Author: David Byers

(def-kom-command kom-status-session (&optional text-or-session-no)
  "Show status for all sessions a person has.

Lisp documentation:
Optional argument should be a list of sessions to get information
about or a single session number. When used interactively, the prefix
argument is used to find a text whose author's status is shown."
  (interactive (and current-prefix-arg ; only peek at textno:s when prefixed!
                    (list (lyskom-read-text-no-prefix-arg
                           'text-to-see-author-status-of))))
  (let ((sessions (or (when (and text-or-session-no (lyskom-called-interactively-p 'any))
			(lyskom-session-from-conf
			 (text-stat->author
			  (blocking-do 'get-text-stat text-or-session-no))))
		      (cond
		       ((listp text-or-session-no) text-or-session-no)
		       ((numberp text-or-session-no) (list text-or-session-no)))
                      (lyskom-read-session-no 'status-for-session)))
	who-info)
    (cond ((null sessions)
           (lyskom-insert-string 'no-such-session-r))
          ((and (numberp (car sessions))
                (<= (car sessions) 0))
	   (let ((pers-no (- (car sessions))))
	     (lyskom-format-insert
	      (lyskom-get-string 'person-not-logged-in-r)
	      pers-no
	      (lyskom-format-time 'date-and-time
				  (pers-stat->last-login
				   (blocking-do 'get-pers-stat pers-no))))))
          (t
           (if (lyskom-have-feature dynamic-session-info)
               (progn
                 (setq who-info (listify-vector
                                 (blocking-do 'who-is-on-dynamic t t 0)))
		 (let (need-separator)
		   (while sessions
		     (setq need-separator
			   (or (lyskom-status-session-9 (car sessions)
							who-info
							need-separator)
			       need-separator))
		     (setq sessions (cdr sessions)))))

             (setq who-info (listify-vector (blocking-do 'who-is-on)))
             (mapcar (function 
                      (lambda (x) (lyskom-status-session-8 x who-info)))
                     sessions))))))


(defun lyskom-status-session-8 (sid who-info-list)
  "Show session status for session SID. WHO-INFO is a list of
WHO-INFOS that are potential sessions."
  (while who-info-list
    (if (eq sid (who-info->connection (car who-info-list)))
	(let* ((info (car who-info-list))
	       (client (if kom-deferred-printing
			   (lyskom-create-defer-info
			    'get-client-name
			    (who-info->connection info)
			    'lyskom-deferred-client-1
			    nil nil nil
			    (who-info->connection info))
			 (blocking-do-multiple
			     ((name (get-client-name
				     (who-info->connection info)))
			      (version (get-client-version
					(who-info->connection info))))
			   (concat name " " version)))))
	  (lyskom-format-insert
	   (lyskom-get-string 'session-status)
	   (who-info->connection info)
	   (who-info->pers-no info)
	   (lyskom-return-username info)
	   (if (not (eq (who-info->working-conf info) 0))
	       (who-info->working-conf info)
	     (lyskom-get-string 'not-present-anywhere))
	   (let ((string (if (string-match "^\\(.*[^.]\\)\\.*$"
					   (who-info->doing-what info))
			     (match-string 1 (who-info->doing-what info))
			   (who-info->doing-what info))))
             (setq string (or (string-replace-match "\n" string " " t t) string))
	     (if (string= string "")
		 (lyskom-get-string 'unknown-doing-what)
	       string))
	   client
	   (if (not (eq (who-info->working-conf info) 0))
	       (lyskom-get-string 'doing-where-conn)
	     (lyskom-get-string 'doing-nowhere-conn)))))
    (setq who-info-list (cdr who-info-list))))


(defun lyskom-status-session-9 (sid who-info-list need-separator)
  "Show session status for session SID. WHO-INFO is a list of
WHO-INFOS that are potential sessions."
  (let ((static (blocking-do 'get-static-session-info sid))
	(found nil))
    (while who-info-list
      (if (eq sid (dynamic-session-info->session (car who-info-list)))
	  (let* ((info (car who-info-list))
		 (client (if kom-deferred-printing
			     (lyskom-create-defer-info
			      'get-client-name
			      (dynamic-session-info->session info)
			      'lyskom-deferred-client-1
			      nil nil nil
			      (dynamic-session-info->session info))
			   (blocking-do-multiple
			       ((name (get-client-name
				       (dynamic-session-info->session info)))
				(version
				 (get-client-version
				  (dynamic-session-info->session info))))
			     (concat name " " version)))))
	    (setq found t)
	    (if need-separator
		(lyskom-insert "---\n"))
	    (lyskom-format-insert
	     (lyskom-get-string 'session-status-9)
	     (dynamic-session-info->session info)
	     (dynamic-session-info->person info)
	     (lyskom-combine-username (static-session-info->username static)
				      (static-session-info->ident-user static)
				      (static-session-info->hostname static))
	     (if (not (eq (dynamic-session-info->working-conference info) 0))
		 (dynamic-session-info->working-conference info)
	       (lyskom-get-string 'not-present-anywhere))
	     (let ((string (if (string-match
				"^\\(.*[^.]\\)\\.*$"
				(dynamic-session-info->what-am-i-doing info))
			       (match-string
				1 (dynamic-session-info->what-am-i-doing info))
			     (dynamic-session-info->what-am-i-doing info))))
               (setq string (or (string-replace-match "\n" string " " t t ) string))
	       (if (string= string "")
		   (lyskom-get-string 'unknown-doing-what)
		 string))
	     client
	     (if (not (eq (dynamic-session-info->working-conference info) 0))
		 (lyskom-get-string 'doing-where-conn)
	       (lyskom-get-string 'doing-nowhere-conn))
	     (lyskom-format-time
              'timeformat-day-yyyy-mm-dd-hh-mm-ss
	      (static-session-info->connection-time static))
             (cond ((eq (/ (dynamic-session-info->idle-time info) 60) 0)
                    (lyskom-get-string 'session-is-active))
                   ((not (session-flags->user_active_used
                          (dynamic-session-info->flags info)))
                    "\n")
                   (t
                    (lyskom-format (lyskom-get-string 'session-status-inactive)
                                   (lyskom-format-secs
                                    (dynamic-session-info->idle-time info))))))
	    (if (session-flags->invisible (dynamic-session-info->flags info))
		(lyskom-insert (lyskom-get-string 'session-is-invisible)))))
      (setq who-info-list (cdr who-info-list)))
    found))


(defun lyskom-format-secs-aux (string num x1 x2 one many)
  (cond ((<= num 0) string)
        ((= num 1) (if (string= "" string)
                       (concat string (lyskom-get-string one))
                     (concat string (if (and (= x1 0)
                                             (= x2 0))
                                        (format " %s " 
                                                (lyskom-get-string 'and))
                                      ", ")
                             (lyskom-get-string one))))
        (t (if (string= "" string)
               (concat string (format "%d %s" num
                                      (lyskom-get-string many)))
             (concat string (if (and (= x1 0)
                                     (= x2 0))
                                (format " %s "
                                        (lyskom-get-string 'and))
                              ", ")
                     (format "%d %s"
                             num
                             (lyskom-get-string many)))))))

(defun lyskom-format-secs (time)
  "Format the number of seconds in TIME as a human-readable string."
  (let (;; (secs (% time 60))
        (mins (% (/ time 60) 60))
        (hrs  (% (/ time 3600) 24))
        (days (/ time 86400))
        (string ""))
    (setq string (lyskom-format-secs-aux string days hrs mins 'one-day 'days))
    (setq string (lyskom-format-secs-aux string hrs  mins 0 'one-hour 'hours))
    (setq string (lyskom-format-secs-aux string mins 0 0 'one-minute 'minutes))))
	

;;; ================================================================
;;;                     Hoppa -  Jump over comments

;;; Author: Linus Tolke Y

;; Hoppa |ver alla inl{gg som {r kommentarer till detta inl{gg (recursivt)


(defun kom-jump (&optional text-no)
  "Skips all comments to the selected text. Descends recursively in
the comment tree, marking all texts found as read. 

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'jump-from-text)))
  (cond ((and (null current-prefix-arg)
              (or (eq 'REVIEW-TREE (read-info->type (read-list->first lyskom-reading-list)))
                  (eq 'REVIEW-FAQ-TREE (read-info->type (read-list->first lyskom-reading-list)))))
         (lyskom-start-of-command 'kom-jump)
         (lyskom-format-insert 'jumping-from-text-review)
         (unwind-protect
             (progn 
               (set-read-list-del-first lyskom-reading-list)
               (set-read-list-del-first lyskom-to-do-list))
           (lyskom-end-of-command)))
        (text-no
         (lyskom-start-of-command 'kom-jump)
         (lyskom-format-insert 'jumping-from-text text-no)
         (initiate-get-text-stat 'main 'lyskom-jump text-no t)
         (lyskom-run 'main 'lyskom-end-of-command))
        (t (lyskom-start-of-command 'kom-jump)
           (lyskom-insert-string 'have-to-read)
           (lyskom-end-of-command))))


(defun lyskom-jump (text-stat mark-as-read &optional sync)
  "Jump past TEXT-STAT and all comments to it.
Remove TEXT-STAT from all internal tables in the client.
If MARK-AS-READ is non-nil, also mark TEXT-STAT and all comments (and
footnotes) to it as read in the server."
  (cond
   (text-stat				;+++ annan errorhantering.
    ;; Should check that we are a member of at least one of
    ;; the recipients, and stop otherwise.
    (if mark-as-read
	(lyskom-mark-as-read text-stat))
    (lyskom-is-read (text-stat->text-no text-stat))
    (lyskom-traverse 
	misc
	(text-stat->misc-info-list text-stat)
      (cond
       ((and (memq (misc-info->type misc) lyskom-comment-types-list)
	     (> (if (eq (misc-info->type misc)
			'COMM-IN)
		    (misc-info->comm-in misc)
		  (misc-info->footn-in misc))
		(text-stat->text-no text-stat)))
	(let ((comment (if (eq (misc-info->type misc)
			       'COMM-IN)
			   (misc-info->comm-in misc)
			 (misc-info->footn-in misc))))
	  (if sync
	      (lyskom-jump (blocking-do 'get-text-stat comment)
			   mark-as-read sync)
	    (initiate-get-text-stat 'main
				    'lyskom-jump
				    comment
				    mark-as-read)))))))))


;;; ================================================================
;;;                 Addera mottagare - Add recipient
;;;             Subtrahera mottagare - Subtract recipient

;;; Author: David Byers

(def-kom-command kom-add-recipient (text-no)
  "Add a recipient to a text.
If the recipient already exists, this will convert the current recipient
type to a regular copy recipient, if possible.

See `kom-confirm-add-recipients'.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-add-recipient)))
  (setq text-no (lyskom-add-sub-adjust-text-no text-no 'add-roots-q))
  (lyskom-add-helper text-no 
                     'who-to-add-q
                     'adding-name-as-recipient
                     'RECPT))

(def-kom-command kom-add-copy (text-no)
  "Add a carbon copy recipient to a text.
If the recipient already exists, this will convert the current recipient
type to a carbon copy recipient, if possible.

See `kom-confirm-add-recipients'.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-add-copy)))
  (setq text-no (lyskom-add-sub-adjust-text-no text-no 'add-roots-q))
  (lyskom-add-helper text-no 
                     'who-to-add-copy-q
                     'adding-name-as-copy
                     'CC-RECPT))

(def-kom-command kom-add-bcc (text-no)
  "Add a recipient to a text.
If the recipient already exists, this will convert the current recipient
type to a BCC recipient, if possible.

See `kom-confirm-add-recipients'.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-add-bcc)))
  (setq text-no (lyskom-add-sub-adjust-text-no text-no 'add-roots-q))
  (lyskom-add-helper text-no 
                     'who-to-add-bcc-q
                     'adding-name-as-copy
                     'BCC-RECPT))

(defun lyskom-add-sub-adjust-text-no (text-no prompt)
  "If TEXT-NO is a footnote or attachment, ask if user wants to operate on root instead."
  (let ((roots (lyskom-get-root-text-belongs-to (blocking-do 'get-text-stat text-no) t)))
    (cond ((null roots) (list text-no))
	  ((and (null (cdr roots))
		(eq text-no (car roots))) roots)
	  ((j-or-n-p (lyskom-format prompt text-no (car roots) (cdr roots))) roots)
	  (t (list text-no)))))

(defun lyskom-add-helper (text-nos who-prompt doing-prompt type)
  (let ((initial-dest nil))
    (lyskom-traverse text-no text-nos
      (let* ((text-stat (blocking-do 'get-text-stat text-no))
	     (attachments (lyskom-attachments-for-sub text-no))
	     (footnotes (lyskom-footnotes-for-sub text-no))
	     (move-footnotes nil)
	     (move-attachments nil)
	     (target (lyskom-read-conf-stat (lyskom-format who-prompt text-no) '(all) nil initial-dest t)))

	(when (and target text-no)
	  (setq initial-dest (conf-stat->name target))
	  (when (and (eq type 'RECPT)
		     kom-confirm-add-recipients
		     (not (lyskom-j-or-n-p
			   (lyskom-format 'really-add-as-recpt-q target))))
	    (setq type 'CC-RECPT doing-prompt 'adding-name-as-copy))

	  (setq move-footnotes (and footnotes (lyskom-j-or-n-p 'add-footnotes-too-q)))
	  (setq move-attachments (and attachments (lyskom-j-or-n-p 'add-attachments-too-q)))
	  (lyskom-traverse text (append (list (cons text-no text-stat))
					(and move-footnotes footnotes)
					(and move-attachments attachments))
	    (lyskom-format-insert doing-prompt target (car text))
	    (lyskom-move-recipient (car text) nil target type)))))))

(defun lyskom-footnotes-for-sub (text-no)
  "Return the list of footnotes to remove recipient from."
  (let ((result nil)
        (worklist (list text-no)))
    (while worklist
      (setq text-no (car worklist))
      (setq worklist (cdr worklist))
      (unless (assq text-no result)
        (let ((text-stat (blocking-do 'get-text-stat text-no)))
          (setq result (cons (cons text-no text-stat) result)
                worklist (nconc (mapcar 'misc-info->footn-in
                                        (lyskom-misc-infos-from-list
                                         'FOOTN-IN (text-stat->misc-info-list
                                                    text-stat)))
                                worklist)))))
    (cdr (nreverse result))))

(defun lyskom-attachments-for-sub (text-no)
  "Return the list of attachments to remove recipient from."
  (let ((result nil)
        (worklist (list text-no)))
    (while worklist
      (setq text-no (car worklist))
      (setq worklist (cdr worklist))
      (unless (assq text-no result)
        (let ((text-stat (blocking-do 'get-text-stat text-no)))
          (setq result (cons (cons text-no text-stat) result)
                worklist (nconc (lyskom-get-text-attachments text-stat)
                                worklist)))))
    (cdr (nreverse result))))



(def-kom-command kom-sub-recipient (text-no)
  "Remove a recipient from the selected text. Take care not to remove
the last recipient, since this will make the text unreadable.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-delete-recipient)))
  (let ((initial-source nil))
    (lyskom-traverse text-no (lyskom-add-sub-adjust-text-no text-no 'sub-roots-q)
      (let* ((text-stat (blocking-do 'get-text-stat text-no))
	     (attachments (lyskom-attachments-for-sub text-no))
	     (footnotes (lyskom-footnotes-for-sub text-no))
	     (move-footnotes nil)
	     (move-attachments nil))
	(if text-stat
	    (let ((recipients
		   (sort (lyskom-text-recipients text-stat t)
			 (lambda (a b)
			   (> (length (memq (cdr a) lyskom-recpt-types-list))
			      (length (memq (cdr b) lyskom-recpt-types-list)))))))
	      (if recipients
		  (let* ((source (lyskom-read-conf-stat
				  (lyskom-format 'who-to-sub-q text-no)
				  (list (cons 'restrict (mapcar 'car recipients)))
				  nil
				  initial-source
				  t)))
		    (when source
		      (setq initial-source (conf-stat->name source))
		      (setq move-footnotes (and footnotes (lyskom-j-or-n-p 'sub-footnotes-too-q)))
		      (setq move-attachments (and attachments (lyskom-j-or-n-p 'sub-attachments-too-q)))

		      (lyskom-traverse text (append (list (cons text-no text-stat))
						    (and move-footnotes footnotes)
						    (and move-attachments attachments))
			(lyskom-format-insert 'remove-name-as-recipient source (cdr text))
			(if (memq (conf-stat->conf-no source) (lyskom-text-recipients (cdr text)))
			    (lyskom-move-recipient (car text) source nil nil)
			  (lyskom-insert 'move-text-not-recipient)))))
		(lyskom-format-insert 'text-has-no-recipients-r text-no)))
	  (lyskom-format-insert 'no-such-text-no text-no))))))


(def-kom-command kom-move-text (text-no)
  "Move the selected text from one conference to another.
If you want to move an entire comment tree, use `kom-move-text-tree' 
instead.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg').

Lisp documentation:
CONTINUATION is the function that does the actual moving. It is called
with three arguments: source, target and text-stat, where source is the
recipient to remove and target the recipient to add to text-stat."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-move)))
  (let (initial-from initial-to)
    (lyskom-traverse text-no (lyskom-add-sub-adjust-text-no text-no 'move-roots-q)
      (let* ((text-stat (blocking-do 'get-text-stat text-no))
	     (attachments (lyskom-attachments-for-sub text-no))
	     (footnotes (lyskom-footnotes-for-sub text-no))
	     (move-footnotes nil)
	     (move-attachments nil))
	(if (null text-stat)
	    (lyskom-format-insert 'no-such-text-no text-no)
	  (let* ((recipients (sort (lyskom-text-recipients text-stat t)
				   (lambda (a b)
				     (> (length (memq (cdr a) lyskom-recpt-types-list))
					(length (memq (cdr b) lyskom-recpt-types-list)))))))
	    (if (null recipients)
		(lyskom-format-insert 'text-has-no-recipients-r text-no)
	      (let ((source (lyskom-read-conf-stat 
			     (lyskom-format 'who-to-move-from-q text-no)
			     (list (cons 'restrict (mapcar 'car recipients)))
			     nil 
			     (uconf-stat->name
			      (cond ((and (cdr recipients)
					  (assq initial-from recipients))
				     (blocking-do 'get-uconf-stat initial-from))
				    ((cdr recipients) nil)
				    (t (blocking-do 'get-uconf-stat (car (car recipients))))))
			     t))
		    (target (lyskom-read-conf-stat (lyskom-format 'who-to-move-to-q text-no)
						   '(all)
						   nil 
						   initial-to
						   t)))
		(when (and source target)
		  (setq initial-from (conf-stat->conf-no source)
			initial-to (conf-stat->name target))
		  (if (eq (conf-stat->conf-no source)
			  (conf-stat->conf-no target))
		      (lyskom-insert-before-prompt 'cant-move-from-to-same)
		    (setq move-footnotes (and footnotes (lyskom-j-or-n-p 'move-footnotes-too-q)))
		    (setq move-attachments (and attachments (lyskom-j-or-n-p 'move-attachments-too-q)))

		    (lyskom-traverse text (append (list (cons text-no text-stat))
						  (and move-footnotes footnotes)
						  (and move-attachments attachments))
		      (if (null (cdr text))
			  (lyskom-format-insert 'moving-cant-read (car text))
			(lyskom-format-insert 'moving-name source target (cdr text))
			(if (memq (conf-stat->conf-no source) (lyskom-text-recipients (cdr text)))
			    (lyskom-move-recipient (car text) source target 'RECPT)
			  (lyskom-insert 'move-text-not-recipient))
			))))))))))))


(def-kom-command kom-move-text-tree (text-no)
  "Move the selected text and all comments below it from one
conference to another. This command can also be used to remove or add
a recipient. Confirmation is required for each single move.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-tree-to-move)))
  (let ((root-text-stat (blocking-do 'get-text-stat text-no))
        (last-move-tree-add-type 'move-tree-rcpt)
        (last-move-leave-cc 'abc-no)
        (last-move-tree-action nil))
    (if root-text-stat

        ;; Set up default recipients for from and to.

        (let* ((recipients (sort (lyskom-text-recipients root-text-stat t)
                               (lambda (a b)
                                 (> (length (memq (cdr a) lyskom-recpt-types-list))
                                    (length (memq (cdr b) lyskom-recpt-types-list)))))))

          ;; Check that the text has recipients at all.

          (if (null recipients)
              (lyskom-format-insert 'text-has-no-recipients-r text-no)

            ;; Read the conference we are moving the tree away from.

            (let ((source (lyskom-read-conf-stat 
                           'who-to-move-from-q
                           (list (cons 'restrict (mapcar 'car recipients)))
                           t nil t))
                  (to-do (list (text-stat->text-no root-text-stat)))
                  (done nil))


              ;; Now loop over the tree

              (while to-do
                (let* ((text-to-move (car to-do))
                       (text-stat (blocking-do 'get-text-stat text-to-move))
                       (text-to-move-recipients
                        (lyskom-text-recipients text-stat)))
                  (setq to-do (cdr to-do))

                  ;; Check that this text is a candidate for moving
                  ;; If not, put it in done to fool the next control
                  ;; structure.
                  ;;
                  ;; A text is candidate for moving if one of its 
                  ;; recipients is the source, or there is no
                  ;; source (in which case we're just adding)

                  (unless text-stat
                    (setq done (cons text-to-move done)))

                  (unless (or (null source)
                              (memq (conf-stat->conf-no source)
                                    text-to-move-recipients))
                    (lyskom-format-insert 'moving-already-moved 
                                          text-to-move source)
                    (setq done (cons text-to-move done)))

                  ;; Use memoing to ensure that we don't loop.

                  (unless (memq text-to-move done)
                    (setq done (cons text-to-move done))

                    ;; Check that the text has the source as a
                    ;; recipient. If not, skip to the next in the
                    ;; tree (and don't traverse downwards).

                    ;; Show the text that we are planning on moving
                    ;; Truncate it. This may be a bad idea, but it
                    ;; kind of makes sense since we don't have the
                    ;; opportunity to scroll the buffer.

                    (let ((kom-truncate-show-lines 
                           (if (numberp kom-truncate-show-lines)
                               (min kom-truncate-show-lines 10)
                             10))
                          (kom-truncate-threshold 
                           (if (numberp kom-truncate-threshold)
                               (min kom-truncate-threshold 10)
                             10)))
                      (lyskom-view-text text-to-move nil nil nil
                                        nil nil nil t)
                      (setq lyskom-last-viewed (point-max)))


                    ;; Ask the user what to do. Options:
                    ;; source
                    ;; --------------------------------------
                    ;; null         add, quit, ign, jump
                    ;; non-null     sub, move, quit, ign, jump
                    ;; --------------------------------------

                    (let* ((completion-ignore-case t)
                           (action (lyskom-a-or-b-or-c-p
                                    (lyskom-get-string 'moving-tree-what-action-q)
                                    (if source 
                                        '(move-tree-move 
                                          move-tree-sub move-tree-ign
                                          move-tree-jump move-tree-quit)
                                      '(move-tree-add 
                                        move-tree-ign move-tree-jump
                                        move-tree-quit))
                                    (if source
                                        (if (memq last-move-tree-action '(move-tree-move move-tree-sub))
                                            last-move-tree-action
                                          'move-tree-move)
                                      'move-tree-add))))
                      (setq last-move-tree-action action)
                      (cond

                       ;; Add recipient.

                       ((eq action 'move-tree-add)
                        (condition-case nil
                            (let ((target
                                   (lyskom-read-conf-stat 'who-to-add-q
                                                          '(all)
                                                          nil nil t)))
                              (setq last-move-tree-add-type
                                    (lyskom-a-or-b-or-c-p 'move-tree-add-rcpt-type
                                                          '(move-tree-rcpt move-tree-cc move-tree-bcc)
                                                          last-move-tree-add-type))
                              (when target
                                (lyskom-format-insert (or (cdr (assq last-move-tree-add-type
                                                                     '((move-tree-rcpt . adding-name-as-recipient)
                                                                       (move-tree-cc . adding-name-as-copy)
                                                                       (move-tree-bcc . adding-name-as-bcc))))
                                                          'adding-name-as-recipient)
                                                      target (text-stat->text-no text-stat))
                                (lyskom-move-recipient text-to-move source target
                                                       (or (cdr (assq last-move-tree-add-type
                                                                      '((move-tree-rcpt . RECPT)
                                                                        (move-tree-cc . CC-RECPT)
                                                                        (move-tree-bcc . BCC-RECPT))))
                                                           'RECPT)))
                              (setq to-do (nconc (lyskom-text-comments text-stat) to-do))
                              )
                          (quit (setq to-do (cons text-to-move to-do))))
                        )

                       ;; Move the text.

                       ((or (eq action 'move-tree-move)
                            (eq action 'move-tree-sub))
                        (condition-case nil
                            (let ((target
                                   (when (eq action 'move-tree-move)
                                     (lyskom-read-conf-stat
                                      (if (> (length text-to-move-recipients) 1)
                                          'who-to-move-to-or-sub-q
                                        'who-to-move-to-q)
                                      '(all)
                                      (> (length text-to-move-recipients) 1)
                                      nil t))))
                              (if target
                                  (lyskom-format-insert 'moving-name source target text-stat)
                                (lyskom-format-insert 'remove-name-as-recipient
                                                      source
                                                      (text-stat->text-no text-stat)))
                              (lyskom-move-recipient text-to-move
                                                     source
                                                     target
                                                     'RECPT
                                                     (cond ((eq last-move-leave-cc 'abc-yes-all) t)
                                                           ((eq last-move-leave-cc 'abc-no-all) nil)
                                                           (t (setq last-move-leave-cc
                                                                    (lyskom-a-or-b-or-c-p 'move-tree-leave-cc-q
                                                                                          '(abc-yes abc-no abc-yes-all abc-no-all)
                                                                                          last-move-leave-cc))
                                                              (cdr (assq last-move-leave-cc '((abc-yes . t)
                                                                                              (abc-no . nil)
                                                                                              (abc-yes-all . t)
                                                                                              (abc-no-all . nil))))))
                                                     )
                              (setq to-do (nconc (lyskom-text-comments text-stat) to-do))
                              )
                          (quit (setq to-do (cons text-to-move to-do))))
                        )


                       ((eq action 'move-tree-ign)
                        (setq to-do (nconc (lyskom-text-comments text-stat) to-do))
                        )

                       ((eq action 'move-tree-quit)
                        (setq to-do nil))

                       ((eq action 'move-tree-jump))))))))))
      (lyskom-format-insert 'no-such-text-no text-no)
      )))



(defun lyskom-move-recipient (text-no source target type &optional leave-cc)
  "Remove TEXT-NO from SOURCE and add it to TARGET as TYPE.
This is the internal function for moving texts around. SOURCE or TARGET
may be nil. TYPE is ignored if TARGET is nil. If optional LEAVE-CC is 
non-nil, then leave the original conference as a CC recipient.

Calls lyskom-report-command-answer to report the result, to callers
must have printed something without a newline at the end of the buffer."
  (let ((text-stat (blocking-do 'get-text-stat text-no)))
    (if text-stat
        (let* ((was-read (lyskom-text-read-p text-stat))
               (add-result (if target
                               (blocking-do 'add-recipient
                                            text-no
                                            (conf-stat->conf-no target)
                                            type)
                             t))
               (add-errno lyskom-errno)
               (sub-result (if (and source add-result)
                               (if leave-cc
                                   (blocking-do 'add-recipient
                                                text-no
                                                (conf-stat->conf-no source)
                                                'CC-RECPT)
                                 (blocking-do 'sub-recipient
                                              text-no
                                              (conf-stat->conf-no source)) )
                             t))
               (sub-errno lyskom-errno))

          (when (null add-result)

            ;; Can't add to target. Explain why. We have not removed from the
            ;; source conference, so no need to add it back

            (cond ((eq add-errno 27) ; already-recipient
                   (lyskom-insert-string 'nope)
                   (lyskom-format-insert 'error-already-recipient text-stat target)
                   )
                  ((eq add-errno 33) ; recipient-limit
                   (lyskom-insert-string 'nope)
                   (lyskom-format-insert 'error-recipient-limit text-stat)
                   )
                  ((eq add-errno 12) ; permission-denied
                   (lyskom-insert-string 'nope)
                   (lyskom-format-insert 'error-permission-denied-add-recpt text-stat target)
                   )
                  ((eq add-errno 11) ; access-denied
                   (lyskom-insert-string 'nope)
                   (lyskom-format-insert 'error-access-denied-add-recpt text-stat target)
                   )
                  (t (lyskom-report-command-answer nil add-errno))) 
            )

          (when (null sub-result)
            ;; Can't sub from souce. Explain why.
            (when target
              (blocking-do 'sub-recipient text-no (conf-stat->conf-no source)))
            (cond ((eq sub-errno 30) ;not-recipient
                   (lyskom-insert-string 'nope)
                   (lyskom-format-insert 'error-not-recipient text-stat source)
                   )
                  ((eq sub-errno 12) ; permission-denied
                   (lyskom-insert-string 'nope)
                   (lyskom-format-insert 'error-permission-denied-sub-recpt
                                         text-stat
                                         source)
                   )
                  (t (lyskom-report-command-answer nil sub-errno)))
            )

          (when (and add-result sub-result)
            (lyskom-report-command-answer t))

          ;; If the text was read prior to the move, it is afterwards too.
          (cache-del-text-stat text-no)
          (when (and add-result target was-read)
            (lyskom-mark-as-read (blocking-do 'get-text-stat text-no)))


          (and add-result sub-result))
      (lyskom-format-insert 'no-such-text-no text-no)
      nil)))




;;; ================================================================
;;;                 Addera kommentar - Add comment
;;;             Subtrahera kommentar - Subtract comment

;;; Author: David Byers
;;; Heavily based on code by Lars Willf|r

(def-kom-command kom-add-comment (text-no-arg)
  "Add one text as a comment to another text.
This command is used to connect a text to another one after both have
been created. If you want to write a new comment, use `kom-write-comment'
instead.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-add-comment-to)))
  (lyskom-add-sub-comment text-no-arg
			  t))

(def-kom-command kom-sub-comment (text-no-arg)
  "Remove a comment from a text.
Be careful removing comments. If others have written comments to the
comment being removed, the context of those comments may become
hard to understand.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-delete-comment-from)))
  (if (lyskom-misc-infos-from-list 
       'COMM-IN (text-stat->misc-info-list
                 (blocking-do 'get-text-stat text-no-arg)))
      (lyskom-add-sub-comment text-no-arg nil)
    (lyskom-format-insert 'text-has-no-comments text-no-arg)))


(defun lyskom-add-sub-comment (text-no do-add)
  "Get the number of the text that is going to have a comment added to it or
subtracted from it
Arguments: TEXT-NO-ARG: an argument as it is gotten from (interactive P)
DO-ADD: NIL if a comment should be subtracted.
        Otherwise a comment is added"
  (if text-no
    (let* ((completions (unless do-add
                          (let ((text-stat (blocking-do 'get-text-stat text-no)))
                            (when text-stat
                              (mapcar 'misc-info->comm-in
                                      (lyskom-misc-infos-from-list 'COMM-IN (text-stat->misc-info-list text-stat)))))))
           (comment-text-no (lyskom-read-number
                             (lyskom-format (if do-add 'text-to-add-q 'text-to-remove-q) text-no)
                             (cond (do-add
                                    (if (eq text-no lyskom-current-text)
                                        nil
                                      lyskom-current-text))
                                   ((eq (length completions) 1)
                                    (car completions))
                                   ((memq lyskom-current-text completions)
                                    lyskom-current-text)
                                   (t nil))
                             nil
                             nil
                             completions
                             )))
      (lyskom-format-insert (if do-add 'add-comment-to 'sub-comment-to)
                            comment-text-no
                            text-no)
      (cache-del-text-stat text-no)
      (cache-del-text-stat comment-text-no)
      (lyskom-report-command-answer 
       (blocking-do (if do-add 'add-comment 'sub-comment)
                    comment-text-no
                    text-no)))
    (lyskom-format-insert (if do-add 
                              'confusion-what-to-add-comment-to
                            'confusion-what-to-sub-comment-from))))


;; ================================================================
;; Move comment
;;

(def-kom-command kom-move-comment (text-no)
  "Move a comment from one text to another.
This command is like first adding the comment to the new text,
then removing it from the old one, and then updating all the
recipients to match its new parent text."
  (interactive (list (lyskom-read-text-no-prefix-arg 'comment-to-move-q)))
  (let* ((text-stat (blocking-do 'get-text-stat text-no))
	 (comm-parents (lyskom-text-stat-commented-texts text-stat 'COMM-TO))
	 (footn-parents (lyskom-text-stat-commented-texts text-stat 'FOOTN-TO))
	 move-from move-to)
    (cond ((null text-stat) (lyskom-format-insert 'no-such-text-no text-no))
	  ((and (null comm-parents)
		(null footn-parents)) 
	   (lyskom-format-insert 'text-is-not-a-comment text-no))
	  ((null comm-parents) (lyskom-format-insert 'cant-move-footnotes))
	  (t (setq move-from (lyskom-completing-read-text-no 
			      (lyskom-format 'comment-move-from-q text-no)
			      (car comm-parents)
			      (text-stat->text-no text-stat)
			      'COMM-TO))
	     (setq move-to (lyskom-read-number 
			    (lyskom-format 'comment-move-to-q text-no)))
	     (cond ((null (blocking-do 'get-text-stat move-to))
		    (lyskom-format-insert 'no-such-text move-to))
		   ((null (blocking-do 'get-text-stat move-from))
		    (lyskom-format-insert 'no-such-text move-from))
		   (t (lyskom-move-comment text-no move-from move-to)))))))


(defun lyskom-compute-text-recipients (text-no &optional move-from move-to)
  "Compute the default recipients for TEXT-NO.
If optional MOVE-FROM and MOVE-TO are non-nil, pretend text-no is no
longer a comment to move-from, but a comment to move-to."
  (let* ((text-stat (blocking-do 'get-text-stat text-no))
	 (text-rcpts (lyskom-text-recipients text-stat t))
	 (comments (lyskom-text-comments text-stat))
	 (comment-rcpts (apply 'nconc
			       (mapcar (lambda (c)
					 (lyskom-text-recipients (blocking-do 'get-text-stat c) t))
				       comments)))
	 (parents (delq nil (delq move-from (cons move-to (lyskom-text-stat-commented-texts text-stat)))))
	 (parent-rcpts (apply 'nconc
			       (mapcar (lambda (p)
					 (lyskom-text-recipients (blocking-do 'get-text-stat p) t))
				       parents)))
	 (proposed-rcpts nil))

    ;; Parent recipients live on in this text

    (lyskom-traverse rcpt parent-rcpts
      (when (or (eq (cdr rcpt) 'RECPT)
		(eq (cdr rcpt) 'BCC-RECPT))
	(setq proposed-rcpts (cons rcpt proposed-rcpts))))

    ;; Child recipients live on as CC-recipients, to preserve the link

    (lyskom-traverse rcpt comment-rcpts
      (unless (assq (car rcpt) parent-rcpts)
	(setq proposed-rcpts (cons (cons (car rcpt) 'CC-RECPT) proposed-rcpts))))

    ;; Check our own membership

    (unless
	(lyskom-traverse rcpt proposed-rcpts
	  (when (lyskom-get-membership (car rcpt))
	    (lyskom-traverse-break t)))
      (setq proposed-rcpts (cons (cons lyskom-pers-no 'RECPT) proposed-rcpts)))

    ;; Check authors of commented texts

    (setq proposed-rcpts (nconc (lyskom-filter-list 
				 (lambda (x) (unless (assq x proposed-rcpts)
							    (cons x 'RECPT)))
				 (lyskom-compute-recipients-commented-authors 
				  parents
				  (mapcar 'car proposed-rcpts)))
				proposed-rcpts))
    proposed-rcpts))


(defun lyskom-recipient-type-> (t1 t2)
  "Return non-nil if recipient type T1 is stronger than T2.
RECPT > CC-RECPT > BCC-RECPT > nil"
  (> (cdr (assq t1 '((RECPT . 4) (CC-RECPT . 3) (BCC-RECPT . 2) (nil . 1))))
     (cdr (assq t2 '((RECPT . 4) (CC-RECPT . 3) (BCC-RECPT . 2) (nil . 1))))))

(defun lyskom-recipient-type-string (rcpt-type)
  "Return a string representing recipient type T"
  (lyskom-get-string
   (or (cdr (assq rcpt-type '((RECPT . recpt-text)
			      (CC-RECPT . cc-recpt-text)
			      (BCC-RECPT . bcc-recpt-text))))
       'unknown-recpt-text)))

(defun lyskom-recipient-sender (text-stat rcpt)
  "Return who sent TEXT-STAT to recipient number RCPT."
  (lyskom-traverse misc (text-stat->misc-info-list text-stat)
    (when (and (memq (misc-info->type misc) lyskom-recpt-types-list)
	       (eq rcpt (misc-info->recipient-no misc)))
      (lyskom-traverse-break
       (or (misc-info->sender misc)
	   (text-stat->author text-stat))))))

(defun lyskom-move-comment (text-no from-text to-text)
  "Move comment TEXT-NO from FROM-TEXT to TO-TEXT
If FROM-TEXT is nil, then just add TEXT-NO as comment to TO-TEXT.
If TO-TEXT is nil, then just remove TEXT-NO as comment to FROM-TEXT."

  (let* ((text-stat (blocking-do 'get-text-stat text-no))
	 (org-rcpts (lyskom-text-recipients text-stat t))
	 (org-computed-rcpts (lyskom-compute-text-recipients text-no))
	 (new-computed-rcpts (lyskom-compute-text-recipients text-no from-text to-text))
	 wanted-rcpts ab ca cb add-rcpts del-rcpts change-rcpts)


    (when (and from-text to-text)
      (lyskom-format-insert 'moving-comment text-no from-text to-text))



    ;; AB = Actual recipient before move
    ;; CB = Computed recipient before move
    ;; CA = Computed recipient after move
    
    ;; User accepted old default (AB = CB)			-> CA (16 cases)
    ;; 
    ;; User upgraded old default (AB > CB)
    ;; - New default is higher than upgrade (CA > AB)		-> CA (4 cases)
    ;; - New default is lower than old default (CA < CB)	-> CA (4 cases)
    ;; - Otherwise						-> AB (16 cases)
    ;; 
    ;; User downgraded old default (AB < CB)
    ;; - New default is lower than user selected (CA < AB)	-> CA (4 cases)
    ;; - New default is higher than old default (CA > CB)	-> CA (4 cases)
    ;; - Otherwise						-> AB (16 cases)

    ;; Traverse the original actual recipients and compare
    ;; to the new ones.

    (lyskom-traverse ab org-rcpts
      (setq ca (assq (car ab) new-computed-rcpts)
	    cb (assq (car ab) org-computed-rcpts))
      (cond ((eq (cdr ab) (cdr ca))
	     (push (cons (car ab) (cdr ca)) wanted-rcpts))
	    ((lyskom-recipient-type-> (cdr ab) (cdr cb))
	     (cond ((lyskom-recipient-type-> (cdr ca) (cdr ab))
		    (push (cons (car ab) (cdr ca)) wanted-rcpts))
		   ((lyskom-recipient-type-> (cdr cb) (cdr ca))
		    (push (cons (car ab) (cdr ca)) wanted-rcpts))
		   (t (push ab wanted-rcpts))))
	    (t (cond ((lyskom-recipient-type-> (cdr ab) (cdr ca))
		      (push (cons (car ab) (cdr ca)) wanted-rcpts))
		     ((lyskom-recipient-type-> (cdr ca) (cdr cb))
		      (push (cons (car ab) (cdr ca)) wanted-rcpts))
		     (t (push ab wanted-rcpts)))))
      (setq org-computed-rcpts (delq cb org-computed-rcpts)
	    new-computed-rcpts (delq ca new-computed-rcpts)))

    ;; Traverse any old default recipients that didn't
    ;; exist as original recipients (i.e. AB < CB). Same
    ;; code as in the third cond branch above.
      
    ;; User downgraded old default (AB < CB)
    ;; - New default is lower than user selected (CA < AB)	-> CA (can't happen)
    ;; - New default is higher than old default (CA > CB)	-> CA (can happen)
    ;; - Otherwise						-> AB 
      
    (setq ab nil)
    (lyskom-traverse cb org-computed-rcpts
      (setq ca (assq (car cb) new-computed-rcpts))
      (cond ((lyskom-recipient-type-> (cdr ca) (cdr cb))
	     (push (cons (car cb) (cdr ca)) wanted-rcpts))
	    (t))
      (setq new-computed-rcpts (delq ca new-computed-rcpts)))

    ;; Any new default recipients that didn't exist
    ;; as old defaults or old recipients get added
    ;; to the list of wanted recipients. This is part
    ;; of the case AB = CB.

    (setq wanted-rcpts (nconc new-computed-rcpts
			      (nreverse wanted-rcpts)))

    ;; Done. Classify recipients in add, del or change.

    ;; Special case: single recipient before and after -- move

    (let ((a (lyskom-filter-list (lambda (x) (eq (cdr x) 'RECPT))
				 (lyskom-text-recipients text-stat t)))
	  (b (lyskom-filter-list (lambda (x) (eq (cdr x) 'RECPT))
				 wanted-rcpts)))
      (when (and (= 1 (length a))
		 (= 1 (length b))
		 (null (cdr (assq (car (car a)) wanted-rcpts)))  
		 (not (eq (car (car a)) (car (car b))))
		 (j-or-n-p (lyskom-format 'move-conf-as-rcpt-q
					  text-no
					  (car (car a))
					  (car (car b)))))
	(push (car a) del-rcpts)
	(push (car b) add-rcpts)
	(setq wanted-rcpts (delq (assq (car (car b)) wanted-rcpts) wanted-rcpts))
	(setq wanted-rcpts (delq (assq (car (car a)) wanted-rcpts) wanted-rcpts))))


    ;; General case

    (lyskom-traverse rcpt wanted-rcpts
      (cond ((null (cdr rcpt))
	     (when (and (or (null (lyskom-recipient-sender text-stat (car rcpt)))
			    (lyskom-is-supervisor (lyskom-recipient-sender text-stat (car rcpt))
						  lyskom-pers-no))
			(j-or-n-p (lyskom-format 'del-conf-as-rcpt-q 
						 (car rcpt)
						 (lyskom-recipient-type-string 'RECPT) 
						 text-no)))
	       (push rcpt del-rcpts)))
	    ((member rcpt org-rcpts))
	    ((assq (car rcpt) org-rcpts)
	     (when (and (or (null (lyskom-recipient-sender text-stat (car rcpt)))
			    (lyskom-is-supervisor (lyskom-recipient-sender text-stat (car rcpt))
						  lyskom-pers-no))
			(j-or-n-p 
			 (lyskom-format 'change-conf-as-rcpt-q 
					(car rcpt)
					(lyskom-recipient-type-string (cdr (assq (car rcpt) org-rcpts)))
					(lyskom-recipient-type-string (cdr rcpt)) 
					text-no)))
	       (push rcpt change-rcpts)))
	    ((j-or-n-p (lyskom-format 'add-conf-as-rcpt-q
				      (car rcpt)
				      (lyskom-recipient-type-string (cdr rcpt)) 
				      text-no))
	     (push rcpt add-rcpts))))
			
    ;; Add the comment

    (when to-text
      (cache-del-text-stat to-text)
      (lyskom-format-insert 'add-comment-to text-no to-text)
      (lyskom-report-command-answer (blocking-do 'add-comment
						 text-no
						 to-text)))

    ;; Finally execute it all

    (when from-text
      (cache-del-text-stat from-text)
      (lyskom-format-insert 'sub-comment-to text-no from-text)
      (lyskom-report-command-answer (blocking-do 'sub-comment
						 text-no
						 from-text)))

    (cache-del-text-stat text-no)

    (lyskom-traverse rcpt (append add-rcpts change-rcpts)
      (lyskom-format-insert (cond ((eq (cdr rcpt) 'RECPT) 'adding-name-as-recipient)
				  ((eq (cdr rcpt) 'CC-RECPT) 'adding-name-as-copy)
				  ((eq (cdr rcpt) 'BCC-RECPT) 'adding-name-as-bcc))
			    (car rcpt) text-no)
      (lyskom-report-command-answer (blocking-do 'add-recipient text-no (car rcpt) (cdr rcpt))))

    (lyskom-traverse rcpt del-rcpts
      (lyskom-format-insert 'remove-name-as-recipient (car rcpt) text-no)
      (lyskom-report-command-answer (blocking-do 'sub-recipient text-no (car rcpt))))

    ))



(def-kom-command kom-add-footnote (text-no-arg)
  "Add a text as a footnote to another text. This command is used to
add a text as a footnote to another text after both have been created.
If you want to write a new footnote, use `kom-write-footnote' instead.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-add-footnote-to)))
  (lyskom-add-sub-footnote text-no-arg
			  t))

(def-kom-command kom-sub-footnote (text-no-arg)
  "Remove a footnote from a text.

Be careful removing comments. If others have written comments to the
comment being removed, the context of those comments may become
hard to understand.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-delete-footnote-from)))
  (if (lyskom-misc-infos-from-list 
       'FOOTN-IN (text-stat->misc-info-list
                 (blocking-do 'get-text-stat text-no-arg)))
      (lyskom-add-sub-footnote text-no-arg nil)
    (lyskom-format-insert 'text-has-no-footnotes text-no-arg)))


(defun lyskom-add-sub-footnote (text-no do-add)
  "Get the number of the text that is going to have a footnote added to it or
subtracted from it
Arguments: TEXT-NO-ARG: an argument as it is gotten from (interactive P)
DO-ADD: NIL if a footnote should be subtracted.
        Otherwise a footnote is added"
  (if text-no
      (let* ((completions (unless do-add
                          (let ((text-stat (blocking-do 'get-text-stat text-no)))
                            (when text-stat
                              (mapcar 'misc-info->footn-in
                                      (lyskom-misc-infos-from-list 'FOOTN-IN (text-stat->misc-info-list text-stat)))))))
             (footnote-text-no  (lyskom-read-number
                                 (lyskom-format
                                  (if do-add 'text-to-add-footn-q 'text-to-remove-footn-q)
                                  text-no)
                                 (cond (do-add
                                        (if (eq text-no lyskom-current-text)
                                            nil
                                          lyskom-current-text))
                                       ((eq (length completions) 1)
                                        (car completions))
                                       ((memq lyskom-current-text completions)
                                        lyskom-current-text)
                                       (t nil))
                                 nil
                                 nil
                                 completions
                                 )))
        (lyskom-format-insert (if do-add 'add-footnote-to 'sub-footnote-to)
                              footnote-text-no
                              text-no)
        (cache-del-text-stat text-no)
        (cache-del-text-stat footnote-text-no)
        (lyskom-report-command-answer 
         (blocking-do (if do-add 'add-footnote 'sub-footnote)
                      footnote-text-no
                      text-no)))
    (lyskom-insert (if do-add 
                       'confusion-what-to-add-footnote-to
                     'confusion-what-to-sub-footnote-from))))

;;; ================================================================
;;;                 Addera referens - Add cross reference

;;; Author: Joel Rosdahl

(def-kom-command kom-add-cross-reference (text-no-arg)
  "Add a cross reference to a text.
The reference can be to another text, a conference or a person.

This command accepts text number prefix arguments (see
`lyskom-read-text-no-prefix-arg')."
  (interactive "P")
  (lyskom-add-cross-reference text-no-arg
                              (lyskom-get-string
                               'text-to-add-cross-reference-to)))

(defun lyskom-add-cross-reference (text-no-arg prompt)
  "Get the number of the text that is going to have a cross reference
added to it, ask the user about cross reference type and value, and
add the cross reference.

Arguments:
  TEXT-NO-ARG: An argument as gotten from (interactive \"P\").
  PROMPT: A string that is used when prompting for a text number."
  (let ((text-no (let ((current-prefix-arg text-no-arg))
                   (lyskom-read-text-no-prefix-arg prompt)))
        (aux-item (lyskom-read-cross-reference-and-get-aux-item)))
    (when (and text-no aux-item)
      (cache-del-text-stat text-no)
      (lyskom-insert 'adding-cross-reference)
      (lyskom-report-command-answer
       (blocking-do 'modify-text-info
                    text-no
                    nil
                    (list aux-item))))))

(defun lyskom-read-cross-reference-and-get-aux-item ()
  "Query user about cross reference type and value, and return the
corresponding aux-item."
  (let* ((type (lyskom-a-or-b-or-c-p 'xref-type
                                     '(abc-conference
                                       abc-person
                                       abc-text)
                                     nil))
         (obj nil)
         (char nil))
    (cond
     ((eq type 'abc-text)
      (let ((prompt 'which-text-to-xref))
        (while (null obj)
          (setq obj (text-stat->text-no
                     (blocking-do 'get-text-stat
                                  (lyskom-read-number prompt))))
          (setq prompt 'which-text-to-xref-err )))
      (setq char "T"))
     ((eq type 'abc-conference)
      (while (null obj)
        (setq obj (lyskom-read-conf-no 'which-conf-to-xref '(conf) nil nil t)))
      (setq char "C"))
     ((eq type 'abc-person)
      (while (null obj)
        (setq obj (lyskom-read-conf-no 'which-pers-to-xref '(pers) nil nil t)))
      (setq char "P")))

    (when obj
      (lyskom-create-aux-item 0 3 0 0
                              (lyskom-create-aux-item-flags
                               nil nil nil nil nil nil nil nil)
                              0
                              (format "%s%d" char obj)))))


;;; ================================================================
;;; Local Variables: 
;;; eval: (put 'lyskom-traverse 'lisp-indent-hook 2)
;;; end: 
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: commands2.el
;;;;
;;;; This file contains the code for some high level commands.
;;;;


(eval-when-compile
  (require 'lyskom-command "command"))


;;; ================================================================
;;;              Lista medlemsskap - List membership

(def-kom-command kom-membership ()
  "Alias for `kom-prioritize'."
  (interactive)
  (lyskom-prioritize))


;;; ================================================================
;;;          Status (f|r) M|te - Status for a conference

;;; Author: ceder (with some help by Linus)
;;; much enhanced by Inge Wallin (lyskom-status-conf-2 and beyond)


(defun lyskom-conf-type-marker (conf-stat)
  "Return a pretty string describing the type of CONF-STAT."
  (let* ((type (conf-stat->conf-type conf-stat))
	 (box (conf-type->letterbox type))
	 (ori (conf-type->original type))
	 (pro (conf-type->rd_prot type))
	 (sec (conf-type->secret type)))
    (cond
     ((or box ori pro sec)
      (concat
       " ("
       (if box (lyskom-get-string 'Mailbox) "")
       (if (and box (or sec ori pro)) ", " "")
       (if sec (lyskom-get-string
		'Protected) "")
       (if (and sec (or ori pro)) ", " "")
       (if ori (lyskom-get-string
		'no-comments) "")
       (if (and ori pro) ", " "")
       (if pro (lyskom-get-string 'closed) "")
       ")"))
     (t ""))))

(def-kom-command kom-status-conf (&optional conf-no)
  "Print information about a conference.
The information listed may be taken from the client's cache and
therefore slightly out of date.

See `kom-extended-status-information'.

Lisp documentation:
If argument CONF-NO is existing and non-nil then this conference is used. 
otherwise: the conference is read with lyskom-completing-read."
  (interactive)
  (let ((conf-no
         (or conf-no
             (lyskom-read-conf-no 'conf-for-status '(all) nil nil t)))
        (kom-print-seconds-in-time-strings nil)
        (kom-extended-status-information (lyskom-extended-status-override 'conf)))
    (cache-del-conf-stat conf-no)
    (cache-del-uconf-stat conf-no)
    (blocking-do-multiple ((conf-stat (get-conf-stat conf-no))
                           (uconf-stat (get-uconf-stat conf-no)))
      (if (null conf-stat)
          (lyskom-insert-string 'no-such-conf)
        
	(lyskom-format-insert 'status-record
			      conf-stat
			      (lyskom-conf-type-marker conf-stat))
        (let ((creator (conf-stat->creator conf-stat)))
          (lyskom-format-insert 'created-by
                                creator
                                creator
                                (if (and 
                                     (lyskom-conf-stat-p creator)
                                     (> (lyskom-string-width (conf-stat->name creator))
                                        (- (lyskom-window-width) 46)))
                                    "\n"
                                  "")))
        (lyskom-format-insert 'created-at
                              (lyskom-format-time
                               'date-and-time
                               (conf-stat->creation-time conf-stat)))
        (lyskom-format-insert 'members
                              (conf-stat->no-of-members conf-stat))
        (lyskom-format-insert 'conf-allows-secret-members
                              (lyskom-get-string 
                               (if (conf-type->forbid-secret 
                                    (uconf-stat->conf-type uconf-stat))
                                   'secret-members-not-permitted
                                 'secret-members-permitted)))
        (lyskom-format-insert 'conf-allows-anon-texts
                              (lyskom-get-string 
                               (if (conf-type->anarchy
                                    (uconf-stat->conf-type uconf-stat))
                                   'anon-texts-permitted
                                 'anon-texts-not-permitted))) 
        (lyskom-format-insert 'garb-nice
                              (conf-stat->garb-nice conf-stat))
        (lyskom-format-insert 'keep-commented
                              (conf-stat->keep-commented conf-stat))
        (lyskom-format-insert 'lowest-local-no
                              (conf-stat->first-local-no conf-stat))
        (lyskom-format-insert 'highest-local-no
                              (1- (+ (conf-stat->no-of-texts conf-stat)
                                     (conf-stat->first-local-no conf-stat))))
        (lyskom-format-insert 'last-text-time
                              (lyskom-format-time
                               'date-and-time
                               (conf-stat->last-written conf-stat)))
        (lyskom-format-insert 'no-of-motd
                              (conf-stat->msg-of-day conf-stat))
        (let ((superconf (conf-stat->super-conf conf-stat)))
          (lyskom-format-insert 'superconf-is-no-name
                                superconf
                                superconf
                                (if (and 
                                     (lyskom-conf-stat-p superconf)
                                     (> (lyskom-string-width (conf-stat->name superconf))
                                        (- (lyskom-window-width) 46)))
                                    "\n"
                                  "")))
        (let ((permitted-submitters (conf-stat->permitted-submitters conf-stat)))
          (lyskom-format-insert 'permitted-submitters-no-name
                                permitted-submitters
                                (if (zerop permitted-submitters)
                                    (lyskom-get-string 'Everybody)
                                  permitted-submitters)
                                ""))
        (let ((supervisor (conf-stat->supervisor conf-stat)))
          (lyskom-format-insert 'supervisor-is-no-name
                                supervisor
                                supervisor
                                ""))
        (lyskom-format-insert 'presentation-no
                              (conf-stat->presentation conf-stat))

        (if (zerop (conf-stat->msg-of-day conf-stat))
            nil
          (lyskom-format-insert 'conf-has-motd conf-stat)
          (lyskom-view-text (conf-stat->msg-of-day conf-stat)))

        ;; Show aux items

        (lyskom-traverse-aux item
            (conf-stat->aux-items conf-stat)
          (if (lyskom-aux-item-definition-field item 'status-print)
              (lyskom-aux-item-call item 'status-print item conf-stat)
            (lyskom-format-insert 'status-aux-item
                                  (format "%d/%d" 
                                          (aux-item->aux-no item)
                                          (aux-item->tag item))
                                  (aux-item->creator item)
                                  (lyskom-aux-item-terminating-button item conf-stat))
            ))

        (let ((mship (lyskom-try-get-membership (conf-stat->conf-no conf-stat) t)))
          (when mship
            (lyskom-format-insert 'conf-mship-priority
                                  (membership->priority mship)
                                  (lyskom-return-membership-type
                                   (membership->type mship)))))

        ;; Show all members of CONF-STAT if the user so wishes."
        (lyskom-scroll)
        (if (lyskom-j-or-n-p
             (lyskom-get-string 'show-members-list-also-q))
            (let ((member-list (blocking-do 'get-members
                                            (conf-stat->conf-no conf-stat)
                                            0 lyskom-max-int)))

              (if (null member-list)
                  (lyskom-format-insert 'conf-has-no-members conf-stat)
                (lyskom-format-insert 'conf-has-these-members conf-stat)
                (if (lyskom-j-or-n-p (lyskom-get-string 'show-membership-info-q))
                    (progn
                      (lyskom-insert-string 'member-list-header)
                      (lyskom-traverse 
                       member (member-list->members member-list)
                       (let ((membership (blocking-do
                                          'query-read-texts 
                                          (member->pers-no member)
                                          (conf-stat->conf-no conf-stat)
                                          t
                                          0)))
                         ;; Print a row describing the membership of MEMBER
                         ;; (described by MEMBERSHIP) in CONF-STAT.
                         (if (or (null membership))
                             (lyskom-insert-string 'secret-membership)
                           (lyskom-format-insert 
                            "%#1@%-17#2s"
                            (if (membership-type->passive
                                 (member->membership-type member))
                                `(face ,kom-dim-face)
                              nil)
                            (lyskom-format-time
                             'date-and-time
                             (membership->last-time-read membership)))

                           (let ((unread (- (+ (conf-stat->first-local-no
                                                conf-stat)
                                               (conf-stat->no-of-texts conf-stat))
                                            (membership->last-text-read membership)
                                            (length (membership->read-texts
                                                     membership))
                                            1)))
                             (lyskom-format-insert 'conf-membership-line
                                                   (if (zerop unread)
                                                       "           "
                                                     (format "%9d  " unread))
                                                   (member->pers-no member)
                                                   (lyskom-return-membership-type (member->membership-type member))
                                                   (if (membership-type->passive
                                                        (member->membership-type member))
                                                       `(face ,kom-dim-face)
                                                     nil)
                                                   )
                             (when (and (member->created-by member)
                                        (not (zerop (member->created-by member)))
                                        (not (eq (member->pers-no member)
                                                 (member->created-by member))))
                               (lyskom-format-insert 'conf-membership-line-2
                                                     (lyskom-format-time
                                                      'date-and-time
                                                      (member->created-at member))
                                                     (member->created-by member)))
                             )))))

                  ;; Don't show membership info
                  (lyskom-insert "\n")
                  (lyskom-traverse
                   member (member-list->members member-list)
                   (lyskom-format-insert "  %#1P\n" 
                                         (member->pers-no member)))))))))))


;;; ================================================================
;;;            Status (f|r) Person - status for a person

;;; Author: ceder
;;; Heavily enhanced: Inge Wallin (lyskom-status-pers-3 and beyond)


(def-kom-command kom-status-person (&optional text-or-pers-no)
  "Show information about a person. 
If a prefix argument is given, the status of the author of that text
will be shown.

See `kom-extended-status-information'.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (and current-prefix-arg ; only peek at textno:s when prefixed!
		    (list (lyskom-read-text-no-prefix-arg
			   'text-to-see-author-status-of))))
  (let ((pers-no
         (or (when (lyskom-called-interactively-p 'any)
	       (text-stat->author (blocking-do 'get-text-stat text-or-pers-no)))
	     text-or-pers-no
             (lyskom-read-conf-no 'pers-for-status '(pers) nil nil t)))
        (kom-print-seconds-in-time-strings nil)
        (kom-extended-status-information (lyskom-extended-status-override 'pers))
        conf-stat
        pers-stat)
    (cache-del-conf-stat pers-no)
    (cache-del-pers-stat pers-no)
    (setq pers-stat (blocking-do 'get-pers-stat pers-no))
    (setq conf-stat (blocking-do 'get-conf-stat pers-no))

    ;; "Print status about PERS-STAT. The name is in CONF-STAT"
    (if (or (null pers-stat)
            (null conf-stat))
        (lyskom-insert-string 'no-such-pers)
      (lyskom-format-insert 'pers-status-record
                            conf-stat)
      (lyskom-format-insert 'created-time
                            (lyskom-format-time
                             'date-and-time
                             (conf-stat->creation-time conf-stat)))

      (lyskom-format-insert 'created-confs
                            (pers-stat->created-confs pers-stat))
      (lyskom-format-insert 'created-persons
                            (pers-stat->created-persons pers-stat))
      (lyskom-format-insert 'created-texts
                            (1- (+ (pers-stat->no-of-created-texts pers-stat)
                                   (pers-stat->first-created-text pers-stat))))
      (lyskom-format-insert 'created-lines
                            (pers-stat->created-lines pers-stat))
      (lyskom-format-insert 'created-chars
                            (pers-stat->created-bytes pers-stat))

      (lyskom-format-insert 'no-of-sessions
                            (pers-stat->sessions pers-stat))
      (let ((time (pers-stat->total-time-present pers-stat)))
        (unless (zerop time) ;; Why not let it print "0 d 00:00:00"?
          (lyskom-format-insert 'present-time-d-h-m-s
                                (floor time (* 24 3600))
                                (mod (floor time 3600) 24)
                                (mod (floor time 60) 60)
                                (round (mod time 60)))))
      (lyskom-format-insert 'last-log-in
                            (lyskom-format-time
                             'date-and-time
                             (pers-stat->last-login pers-stat)))
      (lyskom-format-insert 'user-name
                            (pers-stat->username pers-stat))
      (lyskom-format-insert 'read-texts
                            (pers-stat->read-texts pers-stat))
      (if (= (pers-stat->pers-no pers-stat) lyskom-pers-no)
          (lyskom-format-insert 'marked-texts
                                (pers-stat->no-of-marks pers-stat)))
      (lyskom-format-insert 'time-for-last-letter
                            (lyskom-format-time
                             'date-and-time
                             (conf-stat->last-written conf-stat)))

      (let* ((a (lyskom-format 'pers-has-privileges ""))
             (b (concat "\n" (make-string (lyskom-string-width a) ?\ ))))
        (lyskom-format-insert 'pers-has-privileges
                              (lyskom-privilege-string (pers-stat->privileges pers-stat)
                                                       'pers-has-privileges-2
                                                       b)))
      (let ((superconf (conf-stat->super-conf conf-stat)))
        (lyskom-format-insert 'superconf
                              superconf
                              superconf
                              ""))
      (if (not (zerop (conf-stat->supervisor conf-stat)))
          (let ((supervisor (conf-stat->supervisor conf-stat)))
            (lyskom-format-insert 'supervisor
                                  supervisor
                                  supervisor
                                  "")))
      (lyskom-format-insert 'member-of-confs
                            (pers-stat->no-of-confs pers-stat))
      (lyskom-format-insert 'presentation
                            (conf-stat->presentation conf-stat))

      ;; Show aux items

      (lyskom-traverse-aux item
          (conf-stat->aux-items conf-stat)
        (lyskom-aux-item-call item 'status-print item conf-stat))

      ;; Show motd

      (if (not (zerop (conf-stat->msg-of-day conf-stat)))
          (progn
            (lyskom-format-insert 'has-motd conf-stat)
            (lyskom-view-text (conf-stat->msg-of-day conf-stat))))


      ;; "Show all conferences CONF-STAT is a member of if the user so wishes."
      (lyskom-scroll)
      (if (null (lyskom-j-or-n-p (lyskom-get-string
                                  'show-membership-list-also-q))) 
          nil
        (let ((membership-list 
               (blocking-do 'get-membership
                            (conf-stat->conf-no conf-stat)))
              (deferred-mships nil)
              (lyskom-count-var 0)
              (lyskom-passive-count-var 0))

          (if (null membership-list)
              (lyskom-format-insert 'not-allowed-see-confs conf-stat)
            (lyskom-format-insert 'is-member-of conf-stat)
            (lyskom-insert-string 'membership-list-header)
            (setq lyskom-count-var 0)
            (setq lyskom-passive-count-var 0)
            (lyskom-traverse
             membership membership-list
             (let ((cs (cache-get-conf-stat
                        (membership->conf-no membership))))
               (and cs
                    (lyskom-time-greater
                     (membership->last-time-read membership)
                     (conf-stat->last-written conf-stat))
                    (cache-del-conf-stat (membership->conf-no membership))))

             ;; "Print a row describing the membership of
             ;; MEMBER-CONF-STAT
             (if (membership-type->passive 
                  (membership->type membership))
                 (setq deferred-mships 
                       (cons membership deferred-mships))
               (setq lyskom-count-var
                     (+ lyskom-count-var
                        (lyskom-status-pers-list-one-membership conf-stat membership)))))
            (lyskom-traverse membership (nreverse deferred-mships)
              (setq lyskom-passive-count-var
                     (+ lyskom-passive-count-var
                        (lyskom-status-pers-list-one-membership conf-stat membership)))))

          ;; "Print the total number of unread texts for the person CONF-STAT."
          (lyskom-format-insert 'his-total-unread 
                                conf-stat
                                lyskom-count-var
                                lyskom-passive-count-var))))))

(defun lyskom-status-pers-list-one-membership (conf-stat membership)
  (let ((member-conf-stat
         (blocking-do 'get-conf-stat 
                      (membership->conf-no membership))))
    (if (or (null member-conf-stat)
            (null membership))
        (lyskom-insert-string 'secret-membership)
      (lyskom-format-insert
       "%#1@%-17#2s"
       (if (membership-type->passive
            (membership->type membership))
           `(face ,kom-dim-face)
         nil)
       (lyskom-format-time
        'date-and-time
        (membership->last-time-read membership)))
      (let ((unread (- (+ (conf-stat->first-local-no
                           member-conf-stat)
                          (conf-stat->no-of-texts
                           member-conf-stat))

                       (membership->last-text-read membership)
                       (length (membership->read-texts
                                membership))
                       1)))
        (lyskom-format-insert
         'pers-membership-line
         (if (zerop unread) "          " (format "%9d " unread))
         (if (= (conf-stat->conf-no conf-stat)
                (conf-stat->supervisor member-conf-stat))
             (lyskom-get-string 'is-supervisor-mark)
           "  ")
         member-conf-stat
         (lyskom-return-membership-type
          (membership->type membership))
         (if (membership-type->passive
              (membership->type membership))
             `(face ,kom-dim-face)
           nil)
         )
        (when (and (membership->created-by membership)
                   (not (zerop (membership->created-by membership)))
                   (not (eq (conf-stat->conf-no conf-stat)
                            (membership->created-by membership))))
          (lyskom-format-insert 'pers-membership-line-2
                                (lyskom-format-time
                                 'date-and-time
                                 (membership->created-at membership))
                                (membership->created-by membership)))
        unread))))



;;; ================================================================
;;;              Skicka meddelande - Send message

;;; Author: Inge Wallin
;;; Rewritten to use lyskom-read-conf-no by Linus Tolke
;;; Modified to use default recipient by David Byers


(defun lyskom-default-conference-for-send-message (&rest args)
  (let ((tmp (cond
              ((eq kom-default-message-recipient 'everybody) nil)
              ((and (eq kom-default-message-recipient 'group)
                    lyskom-last-group-message-recipient)
               lyskom-last-group-message-recipient)
              ((or (and (eq kom-default-message-recipient 'group)
                        (null lyskom-last-group-message-recipient))
                   (and (eq kom-default-message-recipient 'sender)
                        lyskom-last-personal-message-sender))
               lyskom-last-personal-message-sender)
              ((and (eq kom-default-message-recipient 'last-recipient)
                    lyskom-last-message-recipient
                    (not (eq 0 lyskom-last-message-recipient))
                    lyskom-last-message-recipient))
              (t
               (if lyskom-last-personal-message-sender
                   lyskom-last-personal-message-sender
                 nil)))))
    (and tmp (list tmp))))

(def-kom-command kom-moronify (&optional whom)
  "Add person to `kom-morons' list."
  (interactive)
  (let ((moron (or whom
		   (lyskom-read-conf-no 'moronify-whom '(pers) nil nil t))))
    (when (not (memq moron kom-morons))
      (setq kom-morons (cons moron kom-morons))
      (lyskom-save-options (current-buffer)
			   (lyskom-get-string 'moronify-saving)
			   (lyskom-get-string 'moronify-saving-done)
			   (lyskom-get-string 'moronify-saving-error)))))

(def-kom-command kom-befriend (&optional whom)
  "Add person to `kom-friends' list."
  (interactive)
  (let ((friend (or whom
		    (lyskom-read-conf-no 'befriend-whom '(pers) nil nil t))))
    (when (not (memq friend kom-friends))
      (setq kom-friends (cons friend kom-friends))
      (lyskom-save-options (current-buffer)
			   (lyskom-get-string 'befriend-saving)
			   (lyskom-get-string 'befriend-saving-done)
			   (lyskom-get-string 'befriend-saving-error)))))

(def-kom-command kom-send-message (&optional who message)
  "Send a message to another user or all members of a conference.
Messages sent with this command are not texts and are not stored
in the database. If you don't know if you should use this or write
a text, write a text \(see `kom-write-text' instead). Remember that
messages of this type are intrusive, yet may not be read by all
users.

Runs `kom-send-message-setup-hook' when entering the minibuffer.

See `kom-default-message-recipient'."
  (interactive)
  (lyskom-interactive-send-message who message nil))


(def-kom-command kom-send-alarm (&optional message) 
  "Send a message to all of the users in LysKOM.
Don't use this command unless what you have to say is really important to
everyone who is logged on.

Runs `kom-send-message-setup-hook' when entering the minibuffer."
  (interactive)
  (lyskom-interactive-send-message nil message t))


(defun lyskom-interactive-send-message (who message alarm-ok)
  "Implementation of kom-send-message and kom-send-alarm."
  (let* ((target (or who
                     (lyskom-read-conf-no
                      (list 'who-to-send-message-to
                            (lyskom-get-string (if alarm-ok 'everybody 'nobody)))
                      (if kom-permissive-completion '(all) '(login conf))
                      t nil t))))
    (cond ((and (zerop target)
                (not alarm-ok))
           (lyskom-format-insert 'message-use-alarm-instead
                                 (lyskom-command-name 'kom-send-alarm)))
          ((not (zerop target))
           (setq lyskom-last-message-recipient target)
           (lyskom-format-insert 'message-recipient-info target)
           (lyskom-send-message target message))

          (t (lyskom-format-insert 'message-all-info 
                                   `(face ,kom-warning-face)
                                   'kom-send-message
                                   (max 20 (- (window-width) 8)))
             (lyskom-beep t)
             (lyskom-send-message target message)))))


(defvar lyskom-message-recipient)
(defvar lyskom-message-string)


(defun lyskom-send-message-minibuffer-setup-hook ()
  (unwind-protect
      (progn
        (run-hooks 'kom-send-message-setup-hook))
    (remove-hook 'minibuffer-setup-hook 
                 'lyskom-send-message-minibuffer-setup-hook)))

(defun lyskom-send-message-minibuffer-exit-hook ()
  (unwind-protect
      (progn
        (run-hooks 'kom-send-message-exit-hook))
    (remove-hook 'minibuffer-exit-hook
                 'lyskom-send-message-minibuffer-exit-hook)))

(defun lyskom-send-message (pers-no
                            message &optional dontshow)
  "Send a message to the person with the number PERS-NO.  PERS-NO == 0
means send the message to everybody. MESSAGE is the message to
send. If DONTSHOW is non-nil, don't display the sent message."
  (let* ((lyskom-message-string nil)
         (reply nil)
         (lyskom-message-recipient nil)
         (lyskom-last-text-format-flags nil))

    (add-hook 'minibuffer-setup-hook
              'lyskom-send-message-minibuffer-setup-hook)
    (add-hook 'minibuffer-exit-hook
              'lyskom-send-message-minibuffer-exit-hook)
    (setq lyskom-message-string 
          (or message
              (lyskom-read-string (lyskom-format 'message-prompt pers-no)
                                  nil
                                  'lyskom-message-history)))
    (setq lyskom-message-recipient (if (zerop pers-no)
                                       nil
                                     (blocking-do 'get-conf-stat 
                                                  pers-no)))

    (run-hooks 'kom-send-message-hook)
    (if lyskom-message-string
        (progn
          (setq reply (blocking-do 'send-message pers-no
                                   lyskom-message-string))

          (if reply
              (if (not dontshow)
                  (lyskom-handle-as-personal-message
                   (lyskom-format
                    (if lyskom-message-recipient
                        (lyskom-get-string-sol 'message-sent-to-user)
                      (lyskom-get-string-sol 'message-sent-to-all))
                    lyskom-message-string 
                    lyskom-message-recipient
                    (when kom-async-highlight-dashed-lines
                      `(face ,(or kom-async-dashed-lines-face
                                  lyskom-default-async-dashed-lines-face)))
                    (when kom-async-highlight-text-body
                      `(face ,(or kom-async-text-body-face
                                  lyskom-default-async-text-body-face)))
		    (let ((kom-print-relative-dates nil))
		      (lyskom-format-time 'date-and-time)))
                   lyskom-pers-no
                   kom-filter-outgoing-messages))
            (lyskom-format-insert-before-prompt
             'message-nope 
             (or lyskom-message-recipient
                 (lyskom-get-string 'everybody))
             lyskom-message-string
             (lyskom-format 'error-code
                            (lyskom-get-error-text lyskom-errno)
                            lyskom-errno
                            lyskom-err-stat))))
      (lyskom-insert-string 'interrupted))                   ;+++ lyskom-errno
    ))

(defun lyskom-send-message-trim-newlines ()
  (when (stringp lyskom-message-string)
    (let ((size (length lyskom-message-string)))
      (while (and (> size 0)
                  (eq ?\n (aref lyskom-message-string (1- size))))
        (setq size (1- size)))
      (cond ((and (eq size 0)
                  (not (lyskom-j-or-n-p (lyskom-get-string 
                                         'send-empty-message-p))))
             (setq lyskom-message-string nil))
            ((eq size 0)
             (setq lyskom-message-string ""))
            (t (setq lyskom-message-string (substring lyskom-message-string 
                                                      0 size)))))))

(lyskom-with-external-functions (resize-minibuffer-setup 
				 resize-minibuffer-mode)

  (defun lyskom-send-message-turn-off-resize-on-exit ()
    (resize-minibuffer-mode -1)
    (remove-hook 'kom-send-message-exit-hook
                 'lyskom-send-message-turn-off-resize-on-exit))

  ;; USER-HOOK: lyskom-send-message-resize-minibuffer
  (defvar resize-minibuffer-mode)

  (defun lyskom-send-message-resize-minibuffer ()
    "Temporarily turn on resizing of minibuffer"
    (unless resize-minibuffer-mode
      (resize-minibuffer-mode 1)
      (resize-minibuffer-setup)
      (add-hook 'kom-send-message-exit-hook
                'kom-send-message-turn-off-resize-on-exit)))

  )


;; USER-HOOK: lyskom-send-message-auto-fill
(defun lyskom-send-message-auto-fill ()
  "Temporarily turn on auto fill in minibuffer"
  (setq fill-column 78)                 ;+++ Ta bort?
  (auto-fill-mode 1))



;;; ================================================================
;;;       Endast l{sa senaste - Set unread articles in a conf. 
;;;            (Skip or re-read articles).

;;; Author: Linus Tolke
;;; Rehacked: David K}gedal


(def-kom-command kom-set-unread (&optional arg conf-no)
  "Set number of unread articles in current conference."
  (interactive "P")
  (setq conf-no (or conf-no lyskom-current-conf))
  (when conf-no (cache-del-conf-stat conf-no))
  (if (or (null conf-no) (zerop conf-no))
      (progn
        (lyskom-insert-string 'not-present-anywhere)
        (lyskom-insert-string "\n"))
    (let ((conf-stat (blocking-do 'get-conf-stat conf-no)))
      (if (null conf-stat)
          (lyskom-insert 'somebody-deleted-that-conf)
        (let* ((narg (prefix-numeric-value arg))
               (n (if (and arg
                           (<= 0 narg)
                           (<= narg (conf-stat->no-of-texts conf-stat)))
                      narg
                    (lyskom-read-num-range-or-date 0 (conf-stat->no-of-texts conf-stat)
                                                   (lyskom-format 'only-last
                                                                  (conf-stat->name conf-stat)))))
               (membership nil))
          (cond ((listp n)
                 (lyskom-format-insert 'set-unread-date 
                                       (elt n 0)
                                       (car (rassq (elt n 1) lyskom-month-names))
                                       (elt n 2))
                 (let* ((target-date (lyskom-create-time 0 0 0 (elt n 2) (elt n 1) (elt n 0) 0 0 nil))
                        (text (lyskom-find-text-by-date conf-stat target-date)))
                   (when text
                     (blocking-do 'set-last-read 
                                  (conf-stat->conf-no conf-stat)
                                  (car text)))))
                ((numberp n) 
                 (lyskom-format-insert 'set-unread-n n)
                 (blocking-do 'set-unread conf-no n)))
          (setq membership (blocking-do 'query-read-texts lyskom-pers-no conf-no t 0))
          (lyskom-replace-membership membership)
          (if (= conf-no lyskom-current-conf)
              (set-read-list-empty lyskom-reading-list))
          (read-list-delete-read-info conf-no lyskom-to-do-list)
          (if (= conf-no lyskom-current-conf)
              (progn (lyskom-fetch-start-of-map conf-stat membership)
                     (lyskom-go-to-conf lyskom-current-conf t))
            (lyskom-prefetch-map conf-no membership))
          )))))



(def-kom-command kom-list-sessions ()
  "List current LysKOM sessions and unread messages for each session.

See `kom-session-nickname' for a setting that affects display."
  (interactive)
  (let ((total-letters 0)
	(total-texts 0)
	(total-confs 0)
	(buflist (buffer-list))
	(session-list nil))
    (lyskom-save-excursion
     (lyskom-traverse buf buflist
       (when (lyskom-buffer-p buf)
	 (set-buffer buf)
	 (let ((letters 0)
	       (texts 0)
	       (confs 0))
	   (lyskom-traverse entry (lyskom-list-news)
	     (unless (or (not entry) ; Happens sometimes
			 (zerop (car entry))) ; Ignore confs with 0 unread
	       (setq texts (+ (car entry)
			      texts))
	       (setq confs (1+ confs))
	       (when (= (conf-stat->conf-no (cdr entry))
			lyskom-pers-no)
		 (setq letters (+ (car entry)
				  letters)))))

	   (setq total-texts (+ texts 
				total-texts))
	   (setq total-letters (+ letters
				total-letters))
	   (setq total-confs (+ confs
				total-confs))

	   (setq session-list (append (list (list (lyskom-session-nickname) 
						  (lyskom-format "%#1P" lyskom-pers-no)
						  texts 
						  letters 
						  confs
						  kom-server-priority))
				      session-list))))))

    (setq session-list
	  (sort session-list (lambda (s1 s2)
			       (if (= (nth 5 s1) ; Same priority?
				      (nth 5 s2))
				   (string< (nth 0 s1) 
					    (nth 0 s2)) ; Sort on name
				 (< (nth 5 s1) ; Different priority - sort on that
				    (nth 5 s2))))))

    (lyskom-traverse session session-list
      (if (zerop (nth 2 session))
	(lyskom-format-insert 'session-list-no-unread-in 
			      (nth 0 session))
	(lyskom-format-insert 'session-list-unreads-in-confs
			      (nth 0 session)
			      (nth 3 session)
			      (nth 2 session)
			      (nth 4 session))))))


;;; ================================================================
;;;                 Lista Nyheter - List News

;;; Author:   Linus Tolke
;;; Rehacked: Inge Wallin, Johan Sundström


(defvar lyskom-special-conf-name "\\`\\(Inl.gg .t mig\\|NL:\\)\\'"
  "Regexp to match conf names that are special.")


(defvar lyskom-iter-list-news-total-confs)
(defvar lyskom-iter-list-news-mship-confs)
(defvar lyskom-iter-list-news-shown-unreads)
(defvar lyskom-iter-list-news-shown-confs)
(defvar lyskom-iter-list-news-total-unreads)
(defvar lyskom-iter-list-news-total-confs)

(defun lyskom-iter-list-news (unreads conf-stat at-least at-most)
  "Callback function used to show the number of unread messages of a
conference. It heavily relies on (and destructively modifies) its
environment."
  (when (and (or (not at-least)
                 (>= unreads at-least))	; unreads within lower bound
             (or (not at-most)
                 (<= unreads at-most)))	; unreads within upper bound
    (when lyskom-iter-list-news-mship-confs			; remember all read conferences?
      (setq lyskom-iter-list-news-mship-confs
            (delq (conf-stat->conf-no conf-stat) 
                  lyskom-iter-list-news-mship-confs)))
    (cond
     ((and (boundp 'lyskom-special-conf-name)
           (stringp lyskom-special-conf-name)
           (string-match lyskom-special-conf-name
                         (conf-stat->name conf-stat)))
      (lyskom-format-insert 'you-have-unreads-special unreads conf-stat))
     (t (lyskom-format-insert 'you-have-unreads unreads conf-stat)))
    (setq lyskom-iter-list-news-shown-unreads 
          (+ lyskom-iter-list-news-shown-unreads unreads)
          lyskom-iter-list-news-shown-confs
          (1+ lyskom-iter-list-news-shown-confs)))
  (setq lyskom-iter-list-news-total-unreads 
        (+ lyskom-iter-list-news-total-unreads unreads)
        lyskom-iter-list-news-total-confs
        (1+ lyskom-iter-list-news-total-confs)))


(def-kom-command kom-list-news (&optional num)
  "Print the number of unread texts in each conference.

If the prefix argument is zero, show all conferences, including those
with no unread textts. With a positiv prefix argument, only show
conferences with at least that many unread texts. With a negative
prefix argument, only show conferences with no more than that many
unread texts.

See `kom-allow-incompleteness'."
  (interactive "P")
  (let ((num-arg (cond
                  ((numberp num) num)
                  ((and (listp num)
                        (numberp (car num))) (car num))
                  (t nil)))
        (lyskom-iter-list-news-mship-confs nil)
        (at-least 1)
        (at-most nil)
        (lyskom-iter-list-news-shown-unreads 0)
        (lyskom-iter-list-news-total-unreads 0)
        (lyskom-iter-list-news-shown-confs 0)
        (lyskom-iter-list-news-total-confs 0))
    (when num-arg
      (cond
       ((= num-arg 0)
        (lyskom-traverse-membership el
          (when (not (membership-type->passive (membership->type el)))
            (setq lyskom-iter-list-news-mship-confs
                  (cons (membership->conf-no el)
                        lyskom-iter-list-news-mship-confs))))
        (setq at-least nil
              lyskom-iter-list-news-mship-confs (nreverse lyskom-iter-list-news-mship-confs)))

       ((> num-arg 0)
        (lyskom-format-insert 'list-unread-with-n-unread
                              (setq at-least num-arg)))
       ((< num-arg 0)
        (lyskom-format-insert 'list-unread-with-at-most-n-unread
                              (setq at-most (- num-arg))))))

    ;; The following variables are bound in the callback
    ;; lyskom-iter-list-news-total-unreads
    ;; lyskom-iter-list-news-shown-unreads
    ;; lyskom-iter-list-news-total-confs
    ;; lyskom-iter-list-news-shown-confs
    ;; lyskom-iter-list-news-mship-confs

    (lyskom-list-news 'lyskom-iter-list-news (list at-least at-most))
    (when lyskom-iter-list-news-mship-confs ; then list all read conferences too
      (lyskom-traverse conf-no lyskom-iter-list-news-mship-confs
        (lyskom-format-insert 'you-have-no-unreads conf-no)))

    (if (= 0 lyskom-iter-list-news-total-unreads)
        (lyskom-insert-string 'you-have-read-everything)
      (if (= 0 lyskom-iter-list-news-shown-unreads)
          (lyskom-insert-string 'no-unreads-shown)
        (lyskom-insert "\n")) ; separate the shown list from the summary message
      (when (and
             (> lyskom-iter-list-news-shown-unreads 0)
             (< lyskom-iter-list-news-shown-unreads
                lyskom-iter-list-news-total-unreads))
        (lyskom-format-insert 'shown-unreads
                              lyskom-iter-list-news-shown-unreads
                              lyskom-iter-list-news-shown-confs))
      (lyskom-format-insert 'total-unreads
                            lyskom-iter-list-news-total-unreads 
                            lyskom-iter-list-news-total-confs))))


(defun lyskom-list-news (&optional callback callback-args)
  "With no arguments, returns a list of tuples (unread . conf-stat).
When called with a CALLBACK function, this function is called
iteratively as the list is built up. This function should take the two
arguments `number-of-unread-messages-in-conference' and `conf-stat'
and optionally any other arguments sent in the list CALLBACK-ARGS, and
its return value will form the elements of the list returned from
lyskom-list-news. The callback will only be fed conferences with at
least one unread message in them."

  (unless kom-allow-incompleteness
    (sit-for 0)
    (lyskom-prefetch-all-confs))

  (mapcar
   (function
    (lambda (info)
      (let ((unreads (length (text-list->texts (read-info->text-list info))))
            (conf-stat (read-info->conf-stat info)))
        (when (eq (read-info->type info) 'CONF)
          (if callback
              (apply callback unreads conf-stat callback-args)
            (cons unreads conf-stat))))))
   (read-list->all-entries lyskom-to-do-list)))



;;; ================================================================
;;; 			V{nta - Idle wait


(defun kom-busy-wait (arg)
  "Wait for new texts.
Waiting is interrupted when a text in a conference with higher priority
than that of the next text to be read. If you want another priority to
break that the ones higher that the next text to be read, give the 
priority as a prefix argument. When a text is received the new text
is displayed.

This command is semi-obsolete and may be removed in a future version
of the client.

See `kom-ding-on-wait-done'."
  (interactive "P")
  (lyskom-start-of-command 'kom-busy-wait)
  (unwind-protect
      (let ((waitfor (or (cond
                          ((integerp arg) arg)
                          ((listp arg) (car arg)))
                         (read-info->priority
                          (read-list->first lyskom-to-do-list))
                         -2)))
        (lyskom-tell-server kom-mercial)
        (if (not (read-list-isempty lyskom-reading-list))
            (set-read-list-empty lyskom-reading-list))
        (if (= waitfor -2)
            (lyskom-insert-string 'waiting-for-anything)
          (lyskom-format-insert 'waiting-higher-than waitfor))
        (lyskom-scroll)
        (setq lyskom-is-waiting
              (list '>
                    '(or (read-info->priority
                          (read-list->first lyskom-reading-list))
                         (read-info->priority
                          (read-list->first lyskom-to-do-list))
                         257)
                    waitfor))
        (while lyskom-is-waiting
          ;; This is a bit trial-and-error stuff at the momemt.
          ;; o How to make personal messages appear *fast*
          ;; o How to enable C-g with a quick response
          (sit-for 0)
          (accept-process-output nil 1)
          (sit-for 0)
          (if lyskom-quit-flag
              (signal 'quit nil))))
    (lyskom-end-of-command))
  ;; We are done waiting
  (lyskom-beep kom-ding-on-wait-done)
  (if (read-list-isempty lyskom-reading-list)
      (kom-go-to-next-conf))
  (kom-next-command))



(defun lyskom-time-greater (time1 time2)
  "Returns t if TIME2 is before TIME1 chronologically."
  (cond
   ((< (time->year time2) (time->year time1)))
   ((> (time->year time2) (time->year time1)) nil)
   ((< (time->mon time2) (time->mon time1)))
   ((> (time->mon time2) (time->mon time1)) nil)
   ((< (time->mday time2) (time->mday time1)))
   ((> (time->mday time2) (time->mday time1)) nil)
   ((< (time->hour time2) (time->hour time1)))
   ((> (time->hour time2) (time->hour time1)) nil)
   ((< (time->min time2) (time->min time1)))
   ((> (time->min time2) (time->min time1)) nil)
   ((< (time->sec time2) (time->sec time1)))
   ((> (time->sec time2) (time->sec time1)) nil)
   (t nil)))


;;; ================================================================
;;;               Lista {rende - list summary

;;; Author: Linus Tolke


(def-kom-command kom-list-summary (prefix)
  "List a summary of the unread texts in the current conference.
The summary contains the date, number of lines, author and subject
of the text on one line.

with a prefix argument, list each unique subject only once."
  (interactive "P")
  (if (read-list-isempty lyskom-reading-list)
      (lyskom-insert-string 'have-to-be-in-conf-with-unread)
    (lyskom-list-summary nil prefix)))

(defun lyskom-list-summary (conf-no &optional unique)
  "List a summary of unread texts in conference CONF-NO.
If CONF-NO is nil, list the first text-list element in lyskom-reading-list.
If UNIQUE is non-nil, list only the first text with a particular subject.

The summary contains the date, number of lines, author and subject of 
the text on one line."
  (let* ((read-info nil)
         (read-list (if conf-no lyskom-to-do-list lyskom-reading-list))
         (len (read-list-length read-list))
         (r 0))
    (while (< r len)
      (if (or (and conf-no 
                   (eq (read-info->type (read-list->nth read-list r)) 'CONF)
                   (eq conf-no (conf-stat->conf-no 
                                (read-info->conf-stat
                                 (read-list->nth read-list r)))))
              (and (null conf-no)
                   (memq (read-info->type (read-list->nth read-list r))
                         '(CONF REVIEW-MARK REVIEW REVIEW-TREE REVIEW-FAQ REVIEW-FAQ-TREE))))
          (setq len 0)
        (setq r (1+ r))))
    (setq read-info (read-list->nth read-list r))
    (when read-info
      (lyskom-list-text-summary 
        (copy-sequence (text-list->texts (read-info->text-list read-info)))
        '(text-no " " written " " lines " " comments " " author " " subject)
        (if unique :unique :comment-order)))))


;;; ============================================================
;;; kom-list-marks                      Lista markeringar
;;; Author: David Byers
;;; Modified by: Joel Rosdahl

(def-kom-command kom-list-marks (&optional which-mark)
  "List texts marked with a particular mark. Use `kom-mark-text' to
mark texts and `kom-unmark-text' to unmark them. A numeric prefix
argument indicated the mark to list. With no prefix argument, you
will be prompted for the mark."
  (interactive "P")
  (when (not (numberp which-mark))
    (setq which-mark (lyskom-read-mark-type
                      (lyskom-get-string 'list-which-mark)
                      t)))

  ;; Start fetching all text-stats and text to list them.

  (lyskom-list-text-summary
    (sort (listify-vector (blocking-do 'get-marks))
          (lambda (a b) (< (mark->mark-type a)
                           (mark->mark-type b))))
    '(mark-type " " text-no " " written " " lines " " mark-count " " author " " subject)
    :filter (lambda (mark which-mark)
              (or (null which-mark)
                  (eq (mark->mark-type mark) which-mark)))
    :filter-args (list which-mark)))


;;; ============================================================
;;;     kom-who-am-i - Vem är jag
;;;
;;; Author: David Byers

(def-kom-command kom-who-am-i ()
  "Show your name and information about your sessions."
  (interactive)
  (if (and lyskom-current-conf
           (not (zerop lyskom-current-conf)))
      (lyskom-format-insert 'who-i-am-present 
                            lyskom-pers-no
                            lyskom-current-conf)
    (lyskom-format-insert 'who-i-am-not-present lyskom-pers-no))

  (lyskom-format-insert 
   'who-i-am-server
   lyskom-server-name
   (version-info->software-version lyskom-server-version-info))

  (lyskom-format-insert 'who-i-am-client
                        lyskom-clientversion
                        lyskom-mule-compiled)

  (lyskom-format-insert 'who-i-am-emacs
                        (emacs-version)
                        enable-multibyte-characters))




;;; ================================================================
;;;          Hj{lp vid del av kommando - Help function

;;; Author: Linus Tolke


(defun lyskom-help (&optional only-kom)
  "Prints a short list of alternatives when you don't know what you can do."
  (interactive)
  (let* ((tohere (cond
                  ((stringp (this-command-keys))
                   (substring (this-command-keys) 0 -1))
                  (t                    ;This is the case in the lucid-emacs.
                   (let* ((tck (this-command-keys))
                          (newvec (make-vector (1- (length tck)) nil))
                          (r 0))
                     (while (< r (length newvec))
                       (aset newvec r (aref tck r))
                       (++ r))
                     newvec))))
         (binding (key-binding tohere))
         (keymap (cond
                  ((and (symbolp binding)
                        (fboundp binding))
                   (symbol-function binding))
                  (t (if (eq binding lyskom-mode-map)
                         (keymap-parent binding)
                       binding))))
         (keylis (lyskom-help-get-keylist keymap))
         (keydes (mapconcat 'single-key-description tohere " "))
         (text (lyskom-format
                (if (string= keydes "")
                    "\n%#2s\n"
                  "\n%#1s:\n%#2s\n")
                keydes
                (mapconcat
                 'identity
                 (delq nil (mapcar
                            (lambda (arg)
                              (if (or (null (cdr arg)) 
                                      (eq (cdr arg) 'undefined)
                                      (and only-kom
                                           (not (or (lyskom-command-name (cdr arg))
                                                    (keymapp (cdr arg))))))
                                  nil
                                (format "%s - %s" 
                                        (if (fboundp 'key-description)
                                            (if (not (vectorp (car arg)))
                                                (key-description (vector (car arg)))
                                              (key-description (car arg)))
                                          (cond ((symbolp (car arg))
                                                 (format "%s" (car arg)))
                                                ((lyskom-characterp (car arg))
                                                 (format "%c" (car arg)))
                                                (t (format "%S" (car arg)))))
                                        (or (lyskom-command-name (cdr arg))
                                            (and (keymapp (cdr arg))
                                                 (lyskom-get-string
                                                  'multiple-choice))
                                            (cdr arg)))))
                            keylis))
                 "\n")))
         ;; next-char
         )
    (when (lyskom-called-interactively-p 'any)
      (if (eq major-mode 'lyskom-mode)
          (progn
            (lyskom-insert text)
            (lyskom-end-of-command))
        (with-output-to-temp-buffer "*Help*"
          (princ text))))
    text))


(defun lyskom-help-get-keylist (keymap)
  (and keymap
       (let (keylist)
         (lyskom-map-keymap
          (lambda (event function)
            (setq keylist (cons (cons event function) keylist)))
          keymap t)
         (nreverse keylist))))


;;; ================================================================
;;;      [ndra livsl{ngd - Set lifespan of texts in a conference

;;; Author: Inge Wallin


(def-kom-command kom-set-garb-nice ()
  "Set the garbage collection time for a conference. Texts in a
conference will eventually be deleted automatically \(this process is
called garbage collection). This can only happen when a text is older
than the garbage collection time of all its recipients."
  (interactive)
  (let ((conf-stat (lyskom-read-conf-stat 'conf-to-set-garb-nice-q
                                          '(all) nil nil t)))
    (if (not conf-stat)
        (lyskom-insert-string 'somebody-deleted-that-conf)
      (let ((garb-nice (lyskom-read-number 'new-garb-nice-q
                                           (conf-stat->garb-nice conf-stat)))
            (keep-commented
             (lyskom-read-number 'new-keep-commented-q
                                 (conf-stat->keep-commented conf-stat))))
        (lyskom-format-insert 'garb-nice-for-is conf-stat garb-nice)
        (if (not (blocking-do 'set-garb-nice
                              (conf-stat->conf-no conf-stat) 
                              garb-nice))
            (lyskom-insert-string 'nope) ;+++lyskom-errno
          (lyskom-insert-string 'done)
          (cache-del-conf-stat (conf-stat->conf-no conf-stat))

          (sit-for 0)
          (lyskom-format-insert 'keep-commented-for-is
                                conf-stat
                                keep-commented)
          (if (not (blocking-do 'set-keep-commented
                                (conf-stat->conf-no conf-stat) 
                                keep-commented))
              (lyskom-insert-string 'nope) ;+++lyskom-errno
            (lyskom-insert-string 'done)
            (cache-del-conf-stat (conf-stat->conf-no conf-stat))))))))


;;; ================================================================
;;;       S{tt till}tna f|rfattare - set-permitted-submitters

;;; Author: Linus Tolke

(def-kom-command kom-set-permitted-submitters ()
  "Set the permitted submitters of a conference. 
The permitted submitters of a conference is another conference. Only
members of the permitted submitters may submit texts to the conference."
  (interactive)
  (let ((conf-stat (lyskom-read-conf-stat 'conf-to-set-permitted-submitters-q
                                          '(all) nil nil t)))

    (if (not conf-stat)
        (lyskom-insert-string 'somebody-deleted-that-conf)
      (let ((new-conf (lyskom-read-conf-stat
                       `(new-permitted-submitters-q 
                         ,(conf-stat->name conf-stat))
                       '(all) t nil t)))
        (if (eq new-conf nil)
            (lyskom-format-insert 'permitted-submitters-removed-for-conf 
                                  conf-stat)
          (lyskom-format-insert 'submitters-conf-for-is
                                conf-stat
                                new-conf))
        (if (not (blocking-do 'set-permitted-submitters
                              (conf-stat->conf-no conf-stat) 
                              (if (eq new-conf nil) ;Allowing all to write there
                                  0
                                (conf-stat->conf-no new-conf))))
            (lyskom-insert-string 'nope) ;+++ lyskom-errno
          (lyskom-insert-string 'done)
          (cache-del-conf-stat (conf-stat->conf-no conf-stat)))))))


;;; ================================================================
;;;             [ndra superm|te - Set super conference 

;;; Author: Inge Wallin


(def-kom-command kom-set-super-conf ()
  "Set the super conference for a conference.
If a conference is set to only accept new texts, and not comments, any
comments submitted to the conference will be sent to the super
conference instead."
  (interactive)
  (let ((conf-stat (lyskom-read-conf-stat 'conf-to-set-super-conf-q
                                          '(all) nil nil t)))
    (if (not conf-stat)
        (lyskom-insert-string 'somebody-deleted-that-conf)
      (let ((new-conf (lyskom-read-conf-stat
                       `(new-super-conf-q ,(conf-stat->name conf-stat))
                       '(all) nil nil t)))

        ;; Set the super conference for conf-stat to new-conf.
        (lyskom-format-insert 'super-conf-for-is
                              conf-stat
                              new-conf)
        (if (not (blocking-do 'set-super-conf 
                              (conf-stat->conf-no conf-stat) 
                              (conf-stat->conf-no new-conf)))
            (lyskom-insert-string 'nope) ;+++ lyskom-errno
          (lyskom-insert-string 'done)
          (cache-del-conf-stat (conf-stat->conf-no conf-stat)))))))



;;; ================================================================
;;;                  Spara databasen - Save database
;;;

(def-kom-command kom-sync-database ()
  "Save the LysKOM database.
You can only run this command if you have administrative rights.

See `kom-enable-adm-caps'."
  (interactive)
  (if (and (>= (version-info->protocol-version lyskom-server-version-info) 8)
           (lyskom-ja-or-nej-p (lyskom-get-string 'really-sync)))
      (progn (lyskom-insert-string 'syncing-server)
             (lyskom-report-command-answer (blocking-do 'sync)))
    (setq lyskom-errno 12)
    (lyskom-report-command-answer nil)))


;;; ================================================================
;;;                  St{ng av servern - Shutdown

;;; Author: Inge Wallin

(def-kom-command kom-shutdown-server ()
  "Shutdown the LysKOM server.
You can only run this command if you have administrative rights.

See `kom-enable-adm-caps'."
  (interactive)
  (if (lyskom-ja-or-nej-p (lyskom-get-string 'really-shutdown))
      (progn
        (lyskom-insert-string 'closing-server)
        (lyskom-report-command-answer (blocking-do 'shutdown 0)))))


;;; ================================================================
;;;       \verg} till adm.mod - Enable administrator capabilities
;;;     \verg} till normalmod - Disable administrator capabilities

;;; Author: Inge Wallin


(def-kom-command kom-enable-adm-caps ()
  "Enable the adminstrator commands for the current user.
You can use this command even if you don't have administrative rights
on the system, but you still won't be able to use privileged commands.

Use `kom-disable-adm-caps' to return to normal mode."
  (interactive)
  (lyskom-enable-adm-caps (blocking-do 'enable 255)
                          (lyskom-get-string 'administrator)
                          t))


(def-kom-command kom-disable-adm-caps ()
  "Disable the LysKOM adminstrator commands for the current user."
  (interactive)
  (lyskom-enable-adm-caps (blocking-do 'enable 0)
                          (lyskom-get-string 'no-longer-administrator)
                          nil))

(defun lyskom-enable-adm-caps (answer string is-administrator)
  "Tell the user if the call succeded."
  (if answer
      (progn
        (lyskom-format-insert 'you-are-now string)
        (setq lyskom-is-administrator is-administrator))
    (lyskom-insert-string 'nope)))	;+++ lyskom-errno


;;; ================================================================
;;;         S{tt loginmeddelande - Set message of the day

;;; Author: Inge Wallin


(def-kom-command kom-set-motd ()
  "Set the notice for the server.
You can only use this command if you have administrative rights.

See `kom-enable-adm-caps'."
  (interactive)
  (let* ((old-motd-text-stat (and (server-info->motd-of-lyskom lyskom-server-info)
                                 (blocking-do 'get-text-stat (server-info->motd-of-lyskom lyskom-server-info))))
         (old-motd-text (and (server-info->motd-of-lyskom lyskom-server-info)
                             (blocking-do 'get-text (server-info->motd-of-lyskom lyskom-server-info))))
         (str (and old-motd-text 
                   old-motd-text-stat
                   (text->decoded-text-mass old-motd-text old-motd-text-stat)))
         (recpt (if old-motd-text-stat
                    (lyskom-get-recipients-from-misc-list
                     (text-stat->misc-info-list old-motd-text-stat))
                  (apply 'nconc (mapcar (lambda (x) (list 'RECPT x)) 
                                        (and lyskom-server-info
                                             (server-info->kom-news-conf lyskom-server-info)
                                             (not (eq 0 (server-info->kom-news-conf lyskom-server-info)))
                                             (list (server-info->kom-news-conf lyskom-server-info))))))))

    (lyskom-edit-text
     lyskom-proc
     (apply 'lyskom-create-misc-list recpt)
     (if (and str (string-match "\n" str))
         (substring str 0 (1- (match-end 0)))
       "")
     (if (and str (string-match "\n" str))
         (substring str (match-end 0))
       "")
     'lyskom-set-motd-2)))


;; Should really fix lyskom-edit text instead of the ugly IGNORE

(defun lyskom-set-motd-2 (text-no ignore)
  "Set motd of LysKOM to the newly created text TEXT-NO."
  (lyskom-insert-before-prompt
   (lyskom-format 'setting-motd text-no))
  (initiate-set-motd-of-lyskom 'background 'lyskom-set-motd-3
                               text-no text-no))


(defun lyskom-set-motd-3 (result text-no)
  "Handle the return from the initiate-set-motd-of-lyskom call."
  (if result
      (progn
        (lyskom-insert-before-prompt
         (lyskom-get-string (if (zerop text-no)
                                'removed-motd 
                              'set-motd-success)))
        (set-server-info->motd-of-lyskom lyskom-server-info text-no))
    (lyskom-insert-before-prompt
     (lyskom-get-string 'set-motd-failed))))


;;; ================================================================
;;;       Ta bort loginmeddelande - Remove message of the day

;;; Author: Inge Wallin


(def-kom-command kom-remove-motd ()
  "Remove the notice for the LysKOM server.
You can only use this command if you have administrative rights.

See `kom-enable-adm-caps'."
  (interactive)
  (lyskom-insert-string 'removing-motd)
  (initiate-set-motd-of-lyskom 'background 'lyskom-set-motd-3
                               0 0))

;;; ================================================================
;;;                  Kasta ut - force logout

;;; Author: Inge Wallin


(def-kom-command kom-force-logout ()
  "Force another session to log out.
You can log out any sessions logged on as users you are the
supervisor of. With administrative rights you can log out any
user."
  (interactive)
  (let ((session (car-safe (lyskom-read-session-no 'who-to-throw-out
                                                   nil nil t))))
    (cond ((> session 0)
           (lyskom-format-insert 'throwing-out session)
           (lyskom-report-command-answer
            (blocking-do 'disconnect session)))
          ((< session 0)
           (lyskom-format-insert 'person-not-logged-in-r (- session) nil))
          (t nil))))

;;; ================================================================
;;;                  Skjut upp l{sning - postpone

;;; Author: Per Cederqvist


(def-kom-command kom-postpone (today)
  "Postpone the reading of all but the last N texts in the current
conference. The postponed texts will be removed from the read list for
this session but return in your next session or when you do a
`kom-recover'.

A numeric prefix argument is the number of texts to read now. Without
a prefix argument this command will prompt for the number of texts."
  (interactive (list
                (cond
                 ((null current-prefix-arg)
                  (lyskom-read-number 'postpone-prompt kom-postpone-default))
                 (t (prefix-numeric-value current-prefix-arg)))))

  (let ((len (read-list-length lyskom-reading-list))
        (finished nil))
    (while (and (not finished)
                (> len 0))
      (let ((type (read-info->type (read-list->first lyskom-reading-list))))
        (cond 
         ((memq type lyskom-review-types-list)
          (read-list-rotate lyskom-reading-list))
         ((memq type lyskom-comment-types-list)
          (set-read-list-del-first lyskom-reading-list))
         ((eq type 'CONF)
	  (text-list->trim-head
	   (read-info->text-list (read-list->first lyskom-reading-list))
	   today)
          (setq finished t))
         ((eq type 'RE-EDIT-TEXT))
         ((eq type 'PRI-SESSION))
         (t
          (signal 'lyskom-internal-error '("lyskom-remove-comment-chains")))))
      (-- len)))

  ;; Delete the 'CONF entry if we selected 0 entries.
  (cond
   ((zerop today)
    (read-list-delete-text nil lyskom-reading-list)
    (read-list-delete-text nil lyskom-to-do-list))))



;;; ================================================================
;;;              S{tt l{sniv} - Sess session priority

;;; Author: David K}gedal

(def-kom-command kom-set-session-priority (priority)
  "Set the priority level of the current session and refetch all
memberships. Conferences whose priorities are lower than the session
priority will not be included when reading. This is useful to separate
conferences to read while at work from conferences to not read at
work.

A numeric prefix argument specifies the new session priority. Without
prefix argument, you will be prompted for a priority.

See `kom-default-session-priority'."
  (interactive "P")
  (let ((pri (or priority
                 (lyskom-read-num-range 0
                                255
                                (lyskom-get-string 'set-session-priority)
                                t
                                (or kom-default-session-priority 100)))))
    (setq lyskom-session-priority pri)
    (lyskom-refetch)))



;;; ================================================================
;;;              Begrav lyskom-sessionen - kom-bury

;;; Author: Linus Tolke

(defun kom-bury () 
  "Puts the kom-session in the background."
  (interactive)
  (let ((session-name (buffer-name (current-buffer)))
        (buffer (current-buffer)))
    (bury-buffer)
    (while (and (string-match (regexp-quote session-name)
                              (buffer-name (current-buffer)))
                (not (eq buffer (current-buffer))))
      (bury-buffer))))


(defun lyskom-buffer-p (buf &optional may-be-dead)
  "Returns non-nil if BUF is an active LysKOM buffer
If optional second argument MAY-BE-DEAD is non-nil, this function returns t
whether the session is alive or not. Otherwise it checks that the session
is alive."
  (when (buffer-live-p buf)
    (save-current-buffer
      (set-buffer buf)
      (and (eq major-mode 'lyskom-mode)
           (boundp 'lyskom-proc)
           lyskom-proc
           (processp lyskom-proc)
           (or (and may-be-dead
                    (memq (process-status lyskom-proc) '(run open closed)))
               (memq (process-status lyskom-proc) '(run open)))))))


(defun lyskom-next-kom (buffer-list-name direction)
  "Internal version of kom-next-kom
BUFFER-LIST-NAME is the list of buffers to rotate through. It must be a 
name since this function may modify the list.
DIRECTION should be one of 'forward or 'backward and is the direction to
rotate through the buffer list.
Return-value: 'no-session if there is no suitable session to switch to
              'same-session if the current buffer is the only suitable one
              nil if everything went well"

  ;; Clean the buffer lists 

  (lyskom-clean-all-buffer-lists)

  (let ((current (lyskom-buffer-root-ancestor (current-buffer)))
        (buffer-list (symbol-value buffer-list-name))
        (result nil))

    ;; Figure out the start buffer. The target buffer will be the
    ;; following buffer in lyskom-buffer-list

    (cond
     ((null buffer-list) (setq result 'no-session
                               current nil))

     ;; If we are in a lyskom buffer that is in lyskom-buffer-list
     ;; switch from the current buffer. If unlisted, list it.
     ((lyskom-buffer-p current) 
      (unless (memq current buffer-list)
        (setq buffer-list (cons current buffer-list))))

     ;; If we are in a non-LysKOM buffer, the start buffer is the
     ;; last one in the list
     (t (setq current (car buffer-list))))

    ;; Rotate the buffer list so the target buffer is first

    (when current
      (cond ((eq direction 'forward)
             (setq buffer-list 
                   (lyskom-rotate-list buffer-list
                                       (car (cdr (memq current buffer-list))))))
            (t
             (setq buffer-list
                   (lyskom-rotate-list buffer-list
                                       (or (car (lyskom-preceding-cons 
                                                 buffer-list
                                                 current))
                                           (car (last buffer-list)))))))
      (set buffer-list-name buffer-list)

      ;; If the current buffer and the target buffer are the same
      ;; do nothing. Otherwise, flip around the buffers

      (if (eq (current-buffer) (car buffer-list))
          (setq result 'same-session)
        (lyskom-switch-to-kom-buffer (car buffer-list) current)
        (setq result nil)))
    result))

(def-kom-emacs-command kom-next-kom ()
  "Pop up the next LysKOM-session.
This command can be run from any buffer to go to the next available
LysKOM session.

See `kom-previous-kom' and `kom-next-unread-kom' for related commands."
  (interactive)
  (let ((result (lyskom-next-kom 'lyskom-buffer-list 'forward)))
    (cond ((eq result 'no-session)
           (error (lyskom-get-string 'no-lyskom-session)))
          ((eq result 'same-session)
           (if kom-next-kom-running-as-kom-command
               (lyskom-insert-before-prompt (lyskom-get-string
                                             'no-other-lyskom-r))
             (error (lyskom-get-string 'no-lyskom-session))))
          (t nil))))

(def-kom-emacs-command kom-previous-kom ()
  "Pop up the previous LysKOM-session.
This command can be run from any buffer to go to the next available
LysKOM session.

See `kom-next-kom' and `kom-next-unread-kom' for related commands."
  (interactive)
  (let ((result (lyskom-next-kom 'lyskom-buffer-list 'backward)))
    (cond ((eq result 'no-session)
           (error (lyskom-get-string 'no-lyskom-session)))
          ((eq result 'same-session)
           (if kom-previous-kom-running-as-kom-command
               (lyskom-insert-before-prompt (lyskom-get-string
                                             'no-other-lyskom-r))
             (error (lyskom-get-string 'no-lyskom-session))))
          (t nil))))


(def-kom-emacs-command kom-next-unread-kom ()
  "Pop up the next LysKOM-session with unread texts.
This command can be run from any buffer to go to the next available
LysKOM session.

See `kom-next-kom' and `kom-previous-kom' for related commands."
  (interactive)
  (let ((result (lyskom-next-kom 'lyskom-sessions-with-unread 'forward)))
    (cond ((eq result 'no-session)
           (if kom-next-unread-kom-running-as-kom-command
               (lyskom-insert-before-prompt (lyskom-get-string 
                                             'no-unread-lyskom-r))
             (error (lyskom-get-string 'no-unread-lyskom))))
          ((eq result 'same-session)
           (if kom-next-unread-kom-running-as-kom-command
               (lyskom-insert-before-prompt
                (lyskom-get-string
                 ;; Are there are any other sessions at all?
                 (if (= 1 (length lyskom-buffer-list))
                     'no-other-lyskom-r
                   'no-other-unread-lyskom-r)))
             (error (lyskom-get-string 'no-lyskom-session))))
          (t nil))))

(defun kom-modeline-next-unread-kom ()
  "Pop up the previous unread lyskom session, if there is one"
  (interactive)
  (lyskom-next-kom 'lyskom-sessions-with-unread 'forward))

(defun kom-modeline-select-unread-kom (event)
  "Pop up a menu of sessions with unreads"
  (interactive "@e")
  (when lyskom-sessions-with-unread
    (let ((unreads
           (mapcar (lambda (buffer)
                     (save-current-buffer
                       (set-buffer buffer)
                       (vector
                        (lyskom-format "%#1P, %#2s%#3?b%[ (%#4s)%]%[%]"
                                       lyskom-pers-no
                                       (lyskom-session-nickname)
                                       (memq buffer lyskom-sessions-with-unread-letters)
                                       (lyskom-get-string 'unread-letters))
                        (list 'lyskom-switch-to-kom-buffer buffer)
                        :active t)))
                   lyskom-sessions-with-unread)))
      (popup-menu
       (cons (lyskom-get-string 'sessions-with-unreads) unreads)
       event))))



(defun lyskom-switch-to-kom-buffer (buffer &optional current)
  (when buffer
    (when (and kom-bury-buffers
               (or (null current)
                   (eq current (current-buffer))))
      (kom-bury))

    ;; Switch to the new buffer
    (switch-to-buffer buffer)))






;;; ============================================================
;;; Var finns kommandot                 (kom-where-is)
;;; Author: David Byers

(def-kom-emacs-command kom-where-is (cmd)
  "Show on which key a LysKOM command is. Prompts for a command name
and displays all key bindings for the command. This is similar to the
Emacs function `where-is', but reads localized LysKOM command names,
not function names."
  (interactive (list (lyskom-read-extended-command)))
  (let ((w (where-is-internal cmd))
        (msg nil))
    (cond ((null cmd)
           (setq msg (lyskom-format (lyskom-get-string 'where-is-doesnt-exist)
                                    (lyskom-command-name cmd))))
          ((null w)
           (setq msg (lyskom-format (lyskom-get-string 'where-is-on-no-key)
                                    (lyskom-command-name cmd))))
          (t (setq msg (lyskom-format (lyskom-get-string 'where-is-on-key)
                                      (lyskom-command-name cmd)
                                      (mapconcat
                                       (lambda (x) 
                                         (format "`%s'" (key-description x)))
                                       w ", ")))))
    (if kom-where-is-running-as-kom-command
        (lyskom-insert-before-prompt (concat msg "\n"))
      (message msg))))


;;;============================================================
;;;  Visa user-arean                    (kom-show-user-area)
;;;
;;;  Author: David Byers

(def-kom-command kom-show-user-area ()
  "Get and display the user area of the current person. The user area
is a regular text used to store all settings. The format of this text
is documented in the LysKOM protocol specification. This command is
primarily intended for use by developers."
  (interactive)
  (let ((pers-stat (blocking-do 'get-pers-stat lyskom-pers-no)))
    (lyskom-view-text (pers-stat->user-area pers-stat)
                      nil nil nil nil nil)
    (lyskom-wait-queue 'main)))

(def-kom-command kom-delete-user-area ()
  "Remove the user area of the current person. The user area
is where all settings are stored. Deleting it means all settings
are lost. Do not delete the user area unless you're sure you really
want to."
  (interactive)
  (lyskom-format-insert 'delete-user-area-warning
			`(face ,kom-warning-face))
  (when (lyskom-ja-or-nej-p (lyskom-get-string 'delete-user-area-confirm))
    (let* ((pers-stat (blocking-do 'get-pers-stat lyskom-pers-no))
	   (user-area (pers-stat->user-area pers-stat)))
      (lyskom-insert 'deleting-user-area)
      (lyskom-report-command-answer (blocking-do 'delete-text user-area))
      (lyskom-insert 'removing-user-area)
      (lyskom-report-command-answer (blocking-do 'set-user-area lyskom-pers-no 0))
      )))
  


;;;============================================================
;;; Bli anonym

(def-kom-command kom-become-anonymous ()
  "Become pseudo-anonymous. 

When this mode is in effect, your movements in LysKOM will not be
reported as usual, and when writing texts you will have the option of
sending them anonymously. Depending on what you do, it may still be
possible to deduce that you are the author of an anonymous text.

To summarize, texts you create while in this mode should be difficult
to trace back to you, if you are careful. It is not foolproof.

Use `kom-become-nonanonymous' to return to normal mode."
  (interactive)
  (if lyskom-is-anonymous
      (lyskom-insert 'you-are-already-anonymous)
;    (initiate-pepsi 'main nil 0)
    (setq lyskom-is-anonymous t)
    (lyskom-tell-server kom-mercial)
    (lyskom-insert 'you-are-anonymous)
    (lyskom-update-prompt t)))

(def-kom-command kom-become-nonanonymous ()
  "Leave pseudo-anonymous mode.

See `kom-become-anonymous' for information on anonymous mode."
  (interactive)
  (if lyskom-is-anonymous
      (progn (when (and lyskom-current-conf 
                        (not (zerop lyskom-current-conf)))
               (initiate-pepsi 'main nil lyskom-current-conf))
             (setq lyskom-is-anonymous nil)
             (lyskom-update-prompt t)
             (lyskom-insert 'you-are-nonanonymous))
    (lyskom-insert 'you-are-already-nonanonymous)))

;;;============================================================
;;;   Ändra mötestyp                    (kom-change-conf-type)
;;;
;;;   Author: Tomas Abrahamsson & David Byers

(def-kom-command kom-change-conf-type ()
  "Change type of a conference.
Using this command you can set all flags of a conference, with
the exception of the letterbox flag (which cannot be modified)."
  (interactive)
  (let* ((uconf-stat (lyskom-read-uconf-stat 'what-conf-to-change
                                             '(conf pers) nil "" t))
         (type (uconf-stat->conf-type uconf-stat))
         (box (conf-type->letterbox type))
         (ori (conf-type->original type))
         (pro (conf-type->rd_prot type))
         (sec (conf-type->secret type))
         (ano (conf-type->anarchy type))
         (ope (conf-type->forbid-secret type)))
    (lyskom-format-insert 
     'change-type-prompt
     uconf-stat
     (mapconcat 'identity
                (delq nil
                      (list (and box (lyskom-get-string 'Mailbox))
                            (and sec (lyskom-get-string 'Protected))
                            (and ori (lyskom-get-string 'no-comments))
                            (and pro (lyskom-get-string 'closed))
                            (and ano (lyskom-get-string 'allow-anon))
                            (and (not ope) (lyskom-get-string 'allow-secret))))
                ", "))
    (let* ((open (lyskom-j-or-n-p (lyskom-get-string 'anyone-member)))
           (secret (if (not open)
                       (lyskom-j-or-n-p (lyskom-get-string 'secret-conf))))
           (orig (lyskom-j-or-n-p (lyskom-get-string 'comments-allowed)))
           (anarchy (lyskom-j-or-n-p (lyskom-get-string 'anonymous-allowed)))
           (secmem (and (lyskom-have-feature long-conf-types)
                        (not (lyskom-j-or-n-p (lyskom-get-string 'secret-members-allowed))))))
      (cache-del-conf-stat (uconf-stat->conf-no uconf-stat))
      (cache-del-uconf-stat (uconf-stat->conf-no uconf-stat))
      (if (not (blocking-do 
                'set-conf-type
                (uconf-stat->conf-no uconf-stat)
                (lyskom-create-conf-type (not open)
                                         (not orig)
                                         secret
                                         (conf-type->letterbox
                                          (uconf-stat->conf-type  uconf-stat))
                                         anarchy
                                         (if (lyskom-have-feature long-conf-types)
                                             secmem
                                           (conf-type->forbid-secret
                                            (uconf-stat->conf-type uconf-stat)))
                                         (conf-type->rsv2
                                          (uconf-stat->conf-type uconf-stat))
                                         (conf-type->rsv3
                                          (uconf-stat->conf-type uconf-stat)))))
          (progn (lyskom-insert-string 'nope)
                 (lyskom-insert-error))
        (lyskom-insert-string 'done)))))



;;; ============================================================
;;; Ändra språk
;;;

(defun kom-change-global-language ()
  "Use kom-change-language instead."
  (interactive)
  (kom-change-language t))

(defun kom-change-local-language ()
  "Use kom-change-language instead"
  (interactive)
  (kom-change-language))

(def-kom-command kom-change-language (&optional global)
  "Change the current language in the current LysKOM session.
With a prefix argument, also make changes that would affect all 
sessions, such as key bindings.

The selected language is not saved between sessions. To permanently
set and save language settings. use `kom-customize' instead.

See `kom-default-language'.

Lisp documentation:
The optional argument GLOBAL indicates that the change should have a
global effect, including changes to key binding."
  (interactive "P")
  (let* ((completion-ignore-case t)
         (table (lyskom-available-language-list))
         (language (lyskom-completing-read
                    (lyskom-get-string 'which-language)
                    (lyskom-maybe-frob-completion-table table)
                    nil
                    t
                    nil
                    'lyskom-language-history)))
    (when (lyskom-string-assoc language table)
      (lyskom-set-language (cdr (lyskom-string-assoc language table)) 'local)
      (when global
        (lyskom-set-language (cdr (lyskom-string-assoc language table)) 'global))

      (lyskom-format-insert 
       'language-set-to 
       (lyskom-language-name (cdr (lyskom-string-assoc language table)))))))



(defun lyskom-available-language-list ()
  "Return an alist suitable for completing read of available language names."
  (let ((tmp 
         (mapcar (lambda (el)
		   (cons (car el) (eval (cdr el))))
		 (get 'lyskom-language-codes 'lyskom-language-var)))
        (codes (mapcar 'car lyskom-languages))
        (result nil))
    (mapc (lambda (code)
            (mapcar 
             (function
              (lambda (codelist)
                (when (assq code codelist)
                  (setq result
                        (cons (cons (cdr (assq code codelist))
                                    code)
                              result)))))
             tmp))
          codes)
    result))


;;; ============================================================
;;; Beräkna

(lyskom-with-external-functions (calc-eval)

  (def-kom-command kom-calculate (&optional exprx)
    "Calculate a mathematical expression.
This function requires the `calc' package to be installed, and is
really only a simple interface to the basic functionality of calc."
    (interactive)
    (when (lyskom-try-require 'calc 
                              (lyskom-get-string 'need-library))
      (let* ((expr (or exprx
                       (lyskom-read-from-minibuffer 
                        (lyskom-get-string 'calc-expression)
                        nil nil nil 'lyskom-expression-history)))
             (result (calc-eval expr)))
        (cond ((stringp result)
               (lyskom-format-insert-before-prompt
                "%#1s = \n    %#2s\n" expr result))
              (t (lyskom-format-insert-before-prompt
                  "%#1s = \n%#2s^ %#3s\n"
                  expr
                  (make-string (car result) ?\ )
                  (car (cdr result)))))))))

;;; ============================================================
;;; Ändra namn

(def-kom-command kom-set-personal-label ()
  "This command is obsolete, broken and doesn't work. Don't use it.
Sets a personal label on an object of some kind."
  (interactive)
  (let* ((completions (list (cons (lyskom-get-string 'Conference) 'conf)
                            (cons (lyskom-get-string 'Person) 'pers)
                            (cons (lyskom-get-string 'Text) 'text)))
         (completion-ignore-case t)
         (type (cdr (lyskom-string-assoc
                     (completing-read (lyskom-get-string 'label-what-kind)
                                      (lyskom-maybe-frob-completion-table 
                                       completions)
                                      nil t)
                     completions)))
         (objno nil)
         (label nil)
         (object nil)
         (secret nil)
         (aux nil))
    (cond ((eq type 'text)
           (setq objno (lyskom-read-number 'label-what-text))
           (setq object (blocking-do 'get-text-stat objno))
           (when (null object)
             (lyskom-error 'no-such-text-no objno))
           (setq aux (text-stat-find-aux object 10 lyskom-pers-no))
           (setq secret (not (lyskom-j-or-n-p 
                              (lyskom-get-string 'label-secret))))
           (setq label
                  (lyskom-read-from-minibuffer 
                   (lyskom-get-string 'label-what-label)))
           (blocking-do 
            'modify-text-info
            objno
            (mapcar 'aux-item->aux-no aux)
            (list
             (lyskom-create-aux-item 0 10 0 0 
                              (lyskom-create-aux-item-flags
                               nil nil secret nil nil nil nil nil)
                              0 label)))
           (cache-del-text-stat objno))

          ((memq type '(conf pers))
           (setq object
                 (lyskom-read-conf-stat (if (eq type 'pers)
                                            'label-what-pers
                                          'label-what-conf)
                                        (if (eq type 'pers)
                                            '(pers)
                                          '(all))
                                         nil nil t))
           (setq objno (conf-stat->conf-no object))
           (setq aux (conf-stat-find-aux object 10 lyskom-pers-no))
           (setq secret (not (lyskom-j-or-n-p
                              (lyskom-get-string 'label-secret))))
           (setq label
                 (lyskom-read-from-minibuffer 
                  (lyskom-get-string 'label-what-label)))
           (blocking-do 
            'modify-conf-info
            objno
            (mapcar 'aux-item->aux-no aux)
            (list
             (lyskom-create-aux-item 0 10 0 0 
                              (lyskom-create-aux-item-flags
                               nil nil secret nil nil nil nil nil)
                              0 label)))
           (cache-del-conf-stat objno)))))


(def-kom-command kom-fast-reply (text-no)
  "Add a remark to a text.
Note that remarks may not be seen by all users. Users are not notified
when remarks are created, and not all clients support remarks.

See `kom-agree' for a variant of this command.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-fast-reply-no)))
  (if text-no
      (progn (lyskom-format-insert 'fast-replying text-no)
             (lyskom-fast-reply text-no
                                (lyskom-read-string 
                                 (lyskom-get-string 'fast-reply-prompt)
                                 nil
                                 'lyskom-fast-reply-history)))
    (lyskom-insert-string 'confusion-what-to-reply-to)))



(defun lyskom-default-agree-string (&optional text)
  (unless text (setq text kom-agree-text))
  (cond ((null text) (lyskom-get-string 'default-agree-string))
        ((stringp text) text)
        ((functionp text) (funcall text))
        ((listp text) (lyskom-default-agree-string
                       (elt text (random (length kom-agree-text)))))))

(def-kom-command kom-agree (text-no)
  "Add a predefined remark to a text.
Note that remarks may not be seen by all users. Users are not notified
when remarks are created, and not all clients support remarks.

The remark to add is defined by `kom-agree-text'.

See `kom-fast-reply' for a general variant of this command.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-agree-no)))
  (if text-no
      (progn (lyskom-format-insert 'agreeing text-no)
             (lyskom-fast-reply text-no
                                (lyskom-read-string
                                 (lyskom-get-string 'agree-prompt)
                                 (lyskom-default-agree-string)
                                 'lyskom-fast-reply-history)))
    (lyskom-insert-string 'confusion-what-to-agree-to)))


(defun lyskom-fast-reply (text-no message)
  "To text TEXT-NO add MESSAGE as a fast reply."
  (cache-del-text-stat text-no)
  (if (lyskom-check-fast-reply-message message)
      (lyskom-report-command-answer
       (blocking-do 'modify-text-info
                    text-no
                    nil
                    (list (lyskom-create-aux-item 0 2 0 0 
                                                  (lyskom-create-aux-item-flags
                                                   nil nil nil nil nil nil nil nil)
                                                  0 message))))
    (lyskom-insert 'nope)
    (lyskom-insert 'fast-reply-too-long)))

(defun lyskom-check-fast-reply-message (message)
  "Return non-nil if MESSAGE is a valid fast reply."
  (not (string-match "\n" message)))


;;; ============================================================
;;; Various aux-item stuff

(def-kom-command kom-add-no-comments (&optional text-no)
  "Add a request for no comments to the selected text. 

Such a request is advisory; clients may ignore them. Be restrictive
with requests for no comments as other users may find them annoying or
even insulting \(they can be seen as the LysKOM equivalent of telling
someone to shut up and get out after making them listen to you).

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-no-comments-no)))
  (let ((text-stat (blocking-do 'get-text-stat text-no)))

    ;; Make sure there is a text there in the first place

    (if (null text-stat)
        (lyskom-format-insert 'no-such-text-no text-no)

      ;; Make sure that the text doesn't already have this kind of item
      ;; created by the same person

      (if (lyskom-match-aux-items (text-stat->aux-items text-stat) 
                                  (lambda (el) 
                                    (and (eq (aux-item->tag el) 4)
                                         (eq (aux-item->creator el)
                                             lyskom-pers-no))))
          (lyskom-format-insert 'already-no-comments text-no)

        ;; If the author of the text is not the current user, ask if the
        ;; user wants to try anyway (it might work...)

        (if (or (eq (text-stat->author text-stat) lyskom-pers-no)
                (lyskom-j-or-n-p 'not-author-try-anyway-p))
            (progn (lyskom-format-insert 'adding-no-comments
                                         text-no)
                   (lyskom-report-command-answer
                    (blocking-do 'modify-text-info
                                 text-no
                                 nil
                                 (list
                                  (lyskom-create-aux-item 
                                   0 4 nil nil
                                   (lyskom-create-aux-item-flags
                                    nil nil nil nil nil nil nil nil) 0 ""))))
                   (cache-del-text-stat text-no)))))))


(def-kom-command kom-add-private-answer (text-no)
  "Add a request for private replies only to the selected text.
Note that such requests are advisory; clients may ignore them.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-private-answer-no)))
  (if text-no
      (let ((text-stat (blocking-do 'get-text-stat text-no)))

        ;; Make sure there is a text there in the first place

        (if (null text-stat)
            (lyskom-format-insert 'no-such-text-no text-no)

          ;; Make sure that the text doesn't already have this kind of item
          ;; created by the same person

          (if (lyskom-match-aux-items (text-stat->aux-items text-stat) 
                                      (lambda (el) 
                                        (and (eq (aux-item->tag el) 5)
                                             (eq (aux-item->creator el)
                                                 lyskom-pers-no))))
              (lyskom-format-insert 'already-private-answer text-no)

            ;; If the author of the text is not the current user, ask if the
            ;; user wants to try anyway (it might work...)

            (if (or (eq (text-stat->author text-stat) lyskom-pers-no)
                    (lyskom-j-or-n-p 'not-author-try-anyway-p))
                (progn (lyskom-format-insert 'adding-private-answer
                                             text-no)
                       (lyskom-report-command-answer
                        (blocking-do 'modify-text-info
                                     text-no
                                     nil
                                     (list
                                      (lyskom-create-aux-item 
                                       0 5 nil nil
                                       (lyskom-create-aux-item-flags
                                        nil nil nil nil nil nil nil nil) 0 ""))))
                       (cache-del-text-stat text-no))))))
    (lyskom-insert 'confusion-what-to-comment)))

(def-kom-command kom-add-request-confirm (text-no)
  "Add confirmation request to the selected text. 
Note that such requests are advisory; clients may ignore them. Use
confirmation requests very sparingly. If unmotivated use of them
becomes widespread, then they will be ignored even when they are used
appropriately.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-request-confirm-no)))
  (if text-no
      (let ((text-stat (blocking-do 'get-text-stat text-no)))

        ;; Make sure there is a text there in the first place

        (if (null text-stat)
            (lyskom-format-insert 'no-such-text-no text-no)

          ;; Make sure that the text doesn't already have this kind of item
          ;; created by the same person

          (if (lyskom-match-aux-items (text-stat->aux-items text-stat) 
                                      (lambda (el) 
                                        (and (eq (aux-item->tag el) 6)
                                             (eq (aux-item->creator el)
                                                 lyskom-pers-no))))
              (lyskom-format-insert 'already-request-confirm text-no)

            ;; If the author of the text is not the current user, ask if the
            ;; user wants to try anyway (it might work...)

            (if (or (eq (text-stat->author text-stat) lyskom-pers-no)
                    (lyskom-j-or-n-p 'not-author-try-anyway-p))
                (progn (lyskom-format-insert 'adding-request-confirm
                                             text-no)
                       (lyskom-report-command-answer
                        (blocking-do 'modify-text-info
                                     text-no
                                     nil
                                     (list
                                      (lyskom-create-aux-item 
                                       0 6 nil nil
                                       (lyskom-create-aux-item-flags
                                        nil nil nil nil nil nil nil nil) 0 ""))))
                       (cache-del-text-stat text-no))))))
    (lyskom-insert 'confusion-what-to-request-confirmation)))

(def-kom-command kom-review-mail-headers (text-no)
  "Show all mail headers of an imported message. Mail headers are also
shown when you use the `kom-review-noconversion' command.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 
                      'review-mail-headers-to-what)))
  (if text-no
      (let* ((text-stat (blocking-do 'get-text-stat text-no))
             (headers (and text-stat (lyskom-get-aux-item (text-stat->aux-items text-stat) 24)))
             (lyskom-transforming-external-text t))
        (cond ((null text-stat) (lyskom-format-insert 'no-such-text-no text-no))
              ((null headers) (lyskom-format-insert 'no-mail-headers text-no))
              (t (lyskom-format-insert 'mail-headers-for text-no)
                 (mapcar (lambda (el) 
                           (let ((kom-autowrap nil))
                             (lyskom-format-insert "%#1t" (aux-item->data el))
                             (lyskom-insert "\n")))
                         headers))))
    (lyskom-insert 'confusion-what-to-review-mail-headers)))


;;; ============================================================
;;; Keep-alive
;;; Author: ceder, byers


(def-kom-var lyskom-keep-alive-timers nil
  "List of all active keep alive timers"
  local)

(defun lyskom-keep-alive-callback (buffer)
  (condition-case callback-error
    (save-current-buffer (set-buffer buffer)
                         (if (eq (process-status lyskom-proc) 'open)
                             (initiate-get-time 'keep nil)
                           (lyskom-stop-keep-alive)
                           (message "Terminating keep-alive")))
    (error (message "Error during keep-alive: %s" callback-error)
           (lyskom-stop-keep-alive))))

(def-kom-command kom-keep-alive ()
  "Keep the LysKOM session alive by sending a request every once in a
while. The variable `kom-keep-alive-interval' controls the frequency
of the request. This command may be useful for users with brain-dead
broadband access that disconnects when the line has been inactive for
too long.

Use `kom-stop-keep-alive' to turn off this mode."
  (interactive)
  (set-buffer lyskom-buffer)
  (lyskom-stop-keep-alive)
  (lyskom-keep-alive)
  (unless (eq (current-buffer) lyskom-buffer)
    (lyskom-message 'start-keep-alive kom-keep-alive-interval))
  (lyskom-format-insert 'start-keep-alive kom-keep-alive-interval)
  (lyskom-insert "\n"))

(defun lyskom-keep-alive ()
  (setq lyskom-keep-alive-timers
        (cons
         (add-timeout kom-keep-alive-interval
                      'lyskom-keep-alive-callback
                      (current-buffer)
                      kom-keep-alive-interval)
         lyskom-keep-alive-timers)))

(def-kom-command kom-stop-keep-alive ()
  "Stop sending periodic requests to keep the session alive.

See `kom-keep-alive' for more information."
  (interactive)
  (lyskom-stop-keep-alive)
  (unless (eq (current-buffer) lyskom-buffer)
    (lyskom-message "%#1s" 'stop-keep-alive))
  (lyskom-insert 'stop-keep-alive)
  (lyskom-insert "\n"))


(defun lyskom-stop-keep-alive ()
  (mapc 'disable-timeout lyskom-keep-alive-timers)
  (setq lyskom-keep-alive-timers nil))

(defun lyskom-buffer-kill-keep-alive-hook ()
  (if lyskom-keep-alive-timers
      (message "Terminating keep-alive for buffer %s" (buffer-name (current-buffer))))
  (lyskom-stop-keep-alive))

(add-hook 'kill-buffer-hook 'lyskom-buffer-kill-keep-alive-hook)

;;; ========================================================================
;;;   Check (if Person is a) member (of Conference)
;;;   Quickly finds out whether a person is a member of a given conference

;;; Author: Johan Sundström

(def-kom-command kom-is-person-member-of-conference (&optional pers-no conf-no)
  "Check if a particular person is a member of a particular conference."
  (interactive)
  (let* ((pers-no
          (or pers-no
              (lyskom-read-conf-no 'pers-to-check-mship-for
                                   '(pers) nil nil t)))
         (conf-stat
          (if conf-no 
              (blocking-do 'get-conf-stat conf-no)
            (lyskom-read-conf-stat 'conf-to-check-mship-of
                                   '(all) nil nil t)))
	 (mship (lyskom-is-member (conf-stat->conf-no conf-stat) pers-no)))
    (if (null mship)
        (lyskom-format-insert 'pers-is-not-member-of-conf pers-no (conf-stat->conf-no conf-stat))
      (if (membership-type->passive (membership->type mship))
          (progn
            (lyskom-format-insert 'pers-is-passive-member-of-conf
                                  pers-no conf-stat)
            (lyskom-format-insert 'pers-will-receive-async
                                  (membership-type->message-flag
                                   (membership->type mship))))
        (lyskom-format-insert 'pers-is-member-of-conf pers-no conf-stat)
        (lyskom-format-insert 'pers-will-receive-async
                              (membership-type->message-flag
                               (membership->type mship)))
        (lyskom-format-insert 'pers-mship-priority
                              (membership->priority mship))
        (when kom-deferred-printing
          (lyskom-format-insert 
           'pers-is-member-of-conf-2
           (lyskom-format-time
            'date-and-time
            (membership->last-time-read mship))
           (lyskom-create-defer-info
            'query-read-texts
            (list pers-no (conf-stat->conf-no conf-stat) t 0)
            (lambda (membership defer-info)
              (if (null membership)
                  (lyskom-replace-deferred 
                   defer-info (lyskom-get-string 'Unknown-number))
                (let ((conf-stat (defer-info->data defer-info)))
                  (lyskom-replace-deferred defer-info 
                                           (number-to-string 
                                            (- (+ (conf-stat->first-local-no conf-stat)
                                                  (conf-stat->no-of-texts conf-stat))
                                               (membership->last-text-read membership)
                                               (length (membership->read-texts membership))
                                               1))))))
            nil nil "%#1s" conf-stat))))
      )))

(def-kom-command kom-will-person-read-text (pers-no text-no)
  "Check if a particular person is a member of any recipient of a text..
If a prefix argument is given, that text will be checked.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-conf-no 'pers-to-check-will-read-for
                                          '(all) nil nil t)
                     (lyskom-read-text-no-prefix-arg 'text-to-check-will-read-for)))
  (let* ((text-stat (blocking-do 'get-text-stat text-no))
         (recipients (and text-stat (lyskom-text-recipients text-stat)))
         (result nil))
    (lyskom-traverse rcpt recipients
      (let ((mship (lyskom-is-member rcpt pers-no)))
        (when mship
          (if (membership-type->passive (membership->type mship))
              (setq result 'passive)
            (setq result t)
            (lyskom-traverse-break)))))

    (cond ((null result)
           (lyskom-format-insert 'pers-is-not-member-of-rcpt
                                 pers-no text-stat))
          ((eq result 'passive)
           (lyskom-format-insert 'pers-is-passive-member-of-rcpt
                                 pers-no text-stat))
          (t (lyskom-format-insert 'pers-is-member-of-rcpt
                                   pers-no text-stat)))))


;;; ================================================================
;;; Help

(def-kom-command kom-help (&optional section)
  "Run the built-in help system."
  (interactive)
  (let* ((alternatives (delq nil
                             (mapcar (lambda (section)
                                       (unless (string= "" (elt section 1))
                                         (cons (elt section 1) 
                                               (elt section 0))))
                                     lyskom-help-data)))
         (completion-ignore-case t)
         (section section))
    (while (null section)
      (setq section
            (lyskom-string-assoc
             (lyskom-completing-read (lyskom-get-string 'help-with-what)
                                     alternatives
                                     nil
                                     t
                                     nil
                                     'lyskom-help-history)
             alternatives)))
    (lyskom-format-insert 'help-for (car section))
    (lyskom-help-show-section (cdr section))))


(def-kom-command kom-make-review-mark-as-read ()
  "Makes all review commands mark texts as read. Overrides the value
of the configurable variable `kom-review-marks-texts-as-read' in the
current buffer."
  (interactive)
  (setq kom-review-marks-texts-as-read t))

(def-kom-command kom-make-review-not-mark-as-read ()
  "Makes all review commands not mark texts as read. Overrides the
value of the configurable variable `kom-review-marks-texts-as-read' in
the current buffer."
  (interactive)
  (setq kom-review-marks-texts-as-read nil))

;;; ================================================================
;;;             Jämför två texter - Compare two texts
;;; Run ediff-buffers on two texts.

;;; Author: Per Cederqvist


(defun lyskom-create-text-buffer (text-no text text-stat)
  "Create and return a buffer containing TEXT."
  (let ((buf (generate-new-buffer (format "%d" text-no))))
    (set-buffer buf)
    (insert (text->decoded-text-mass text text-stat))
    ;; Add a terminating newline.  Ediff works better that way, and it
    ;; should not harm any other applications of this function.
    (insert "\n")
    (set-buffer-modified-p nil)
    buf))

(defvar diff-command)
(defvar ediff-diff-program)
(def-kom-command kom-compare-texts (old new)
  "Show differences between two texts, OLD and NEW.

When called interactively, it will prompt for the NEW text first,
defaulting to the last viewed texts.  The OLD text number will default
to the first text that NEW is a comment or footnote to.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg') for the NEW text."
  (interactive
   (let* ((n (lyskom-read-text-no-prefix-arg 'diff-what-text-new))
	  (new-stat (blocking-do 'get-text-stat n))
	  (o (lyskom-read-number
	      'diff-what-text-old
	      (if (null new-stat)
		  (lyskom-error (lyskom-get-string 'no-such-text-no n))
		(car (lyskom-text-stat-commented-texts new-stat))))))
     (list o n)))
  (blocking-do-multiple ((old-text (get-text old))
			 (new-text (get-text new))
			 (old-text-stat (get-text-stat old))
			 (new-text-stat (get-text-stat new)))
    (cond
     ((or (null old-text) (null old-text-stat))
      (lyskom-format-insert 'no-such-text-no old))
     ((or (null new-text) (null new-text-stat))
      (lyskom-format-insert 'no-such-text-no new))
     (t
      (condition-case nil
	  (ediff-buffers
	   (lyskom-create-text-buffer old old-text old-text-stat)
	   (lyskom-create-text-buffer new new-text new-text-stat))
	(file-error (lyskom-error (lyskom-get-string 'external-program-missing)
				  (cond ((boundp 'ediff-diff-program)
					 ediff-diff-program)
					((boundp 'diff-command) diff-command)
					(t "diff (gissningsvis)")))))))))

;;; ================================================================
;;;             Se diff - View diff
;;; Run diff on two texts and insert the result.

;;; Author: Per Cederqvist


(defun lyskom-create-temp-file (text-no text text-stat)
  "Create a temporary file containing TEXT, and return its file name."
  (let ((file (make-temp-file (format "kom-diff-%d." text-no)))
	(buf (lyskom-create-text-buffer text-no text text-stat)))
    (write-region (point-min) (point-max) file)
    (kill-buffer buf)
    file))

(defvar diff-command)

(def-kom-command kom-diff-texts (old new &optional switches)
  "Show differences between two texts, OLD and NEW.

When called interactively, it will prompt for the NEW text first,
defaulting to the last viewed texts. The OLD text number will default
to the first text that NEW is a comment or footnote to.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg') for the NEW text."
  (interactive
   (let* ((n (lyskom-read-text-no-prefix-arg 'diff-what-text-new))
	  (new-stat (blocking-do 'get-text-stat n))
	  (o (lyskom-read-number
	      'diff-what-text-old
	      (if (null new-stat)
		  (lyskom-error (lyskom-get-string 'no-such-text-no n))
		(car (lyskom-text-stat-commented-texts new-stat))))))
     (list
      o n
      (if current-prefix-arg
	  (list (read-string "Diff switches: "
			     (if (stringp diff-switches)
				 diff-switches
			       (mapconcat 'identity diff-switches " "))))
	nil))))

  (blocking-do-multiple ((old-text (get-text old))
			 (new-text (get-text new))
			 (old-text-stat (get-text-stat old))
			 (new-text-stat (get-text-stat new)))

    (cond
     ((or (null old-text) (null old-text-stat))
      (lyskom-format-insert 'no-such-text-no old))
     ((or (null new-text) (null new-text-stat))
      (lyskom-format-insert 'no-such-text-no new))
     (t
      (let* ((buf (current-buffer))
	     (oldfile (lyskom-create-temp-file old old-text old-text-stat))
	     (newfile (lyskom-create-temp-file new new-text new-text-stat))
	     (args (list "-L" (format "%d\t%s" old
				      (lyskom-format-time
				       'timeformat-yyyy-mm-dd-hh-mm-ss
				       (text-stat->creation-time
					old-text-stat)))
			 "-L" (format "%d\t%s" new
				      (lyskom-format-time
				       'timeformat-yyyy-mm-dd-hh-mm-ss
				       (text-stat->creation-time
					new-text-stat)))
			 oldfile newfile)))

	(if switches
	    (setq args (append
			(lyskom-split-string (if (consp switches)
                                                 (mapconcat 'identity switches " ")
                                               switches))
			args))
	  (if diff-switches
	      (setq args (append
			  (lyskom-split-string (if (consp diff-switches)
                                                   (mapconcat 'identity diff-switches
                                                              " ")
                                                 diff-switches))
			  args))))
	    

	(set-buffer buf)
	(lyskom-insert
	 (with-temp-buffer
	   (apply 'call-process (if (boundp 'diff-command)
				    diff-command
				  "diff")
		  nil t nil args)
	   (buffer-string)))
	(delete-file oldfile)
	(delete-file newfile))))))
      
    

;;; ================================================================
;;;             Visa URL - Show URL
;;; View an URL in the current browser.

;;; Author: Per Cederqvist

(def-kom-command kom-view-url-in-text (n)
  "Display the first URL in the current text of a LysKOM buffer.
With a prefix argument, view the Nth URL in the text."
  (interactive "p")
  (save-excursion
    (let ((end (point)))
      (kom-backward-text)
      (re-search-forward lyskom-url-protocol-regexp end nil n)
      (kom-button-press))))

;;; ================================================================
;;;             Skapa aux-item

(def-kom-command kom-create-aux-item ()
  "Creates arbitrary aux-items.
This command is primarily targeted at advanced users and developers
who want access to aux-item-based features before they are implemented
properly in the client."
  (interactive)
  (let* ((completions
          (mapcar (lambda (x) (cons (lyskom-get-string x) x))
                  '(server conference text)))
         (completion-ignore-case t)
         (object-type 
          (cdr (lyskom-string-assoc 
                (lyskom-completing-read 
                 (lyskom-get-string 'what-kind-to-add-aux-to) 
                 completions nil t)
                completions)))
         (object-id (cond ((eq object-type 'server) nil)
                           ((eq object-type 'conference)
                            (lyskom-read-conf-no 'which-conf-to-add-aux-to
                                                 '(pers conf) nil nil t))
                           ((eq object-type 'text)
                            (lyskom-read-number 'which-text-to-add-aux-to 
                                                (if (and lyskom-current-text
                                                         (not (zerop lyskom-current-text)))
                                                    lyskom-current-text
                                                  nil)))))
         (tag (lyskom-read-number 'which-aux-item-tag))
         (inherit (lyskom-j-or-n-p 'which-aux-item-inherit))
         (secret (lyskom-j-or-n-p 'which-aux-item-secret))
         (anonymous (lyskom-j-or-n-p 'which-aux-item-anonymous))
         (rsv1 (lyskom-j-or-n-p 'which-aux-item-rsv1))
         (rsv2 (lyskom-j-or-n-p 'which-aux-item-rsv2))
         (rsv3 (lyskom-j-or-n-p 'which-aux-item-rsv3))
         (rsv4 (lyskom-j-or-n-p 'which-aux-item-rsv4))
         (inherit-limit (lyskom-read-number 'which-aux-item-inherit-limit))
         (data (lyskom-read-string (lyskom-get-string 'which-aux-item-data)))
         (flags (lyskom-create-aux-item-flags nil inherit secret anonymous
                                              rsv1 rsv2 rsv3 rsv4))
         (item (lyskom-create-aux-item 0 tag 0 0 flags inherit-limit data)))
    
    (lyskom-report-command-answer 
     (cond ((eq object-type 'server) 
            (blocking-do 'modify-server-info nil (list item)))
           ((eq object-type 'conference)
            (progn (cache-del-conf-stat object-id)
                   (blocking-do 'modify-conf-info object-id nil (list item))))
           ((eq object-type 'text)
            (progn (cache-del-text-stat object-id)
                   (blocking-do 'modify-text-info object-id nil (list item))))))
    (when (eq object-type 'server)
      (setq lyskom-server-info (blocking-do 'get-server-info)))))


;;; ================================================================
;;; Status för LysKOM
;;;
;;; Skriv ut:
;;; * Serverns kanoniska namn (canonical-name eller ud kom-server-alist)
;;; * Serverns DNS-namn/IP och port
;;; * Serverns programvara och version
;;; * Högsta existerande inläggsnummer
;;; * Antal sessioner
;;; * Serverns tid
;;;

(def-kom-command kom-status-server ()
  "Show status information for the LysKOM server."
  (interactive)
  (blocking-do-multiple ((server-info (get-server-info))
                         (server-version (get-version-info))
                         (server-time (get-time))
                         (highest-text (find-previous-text-no lyskom-max-int))
                         (first-text (find-next-text-no 0))
                         (session-info (who-is-on-dynamic  t t 0))
                         (stats-desc (get-stats-description))
                         (text-stats (get-stats "texts"))
                         (conf-stats (get-stats "confs"))
                         (pers-stats (get-stats "persons"))
                         (boottime-info (get-boottime-info)))
    (setq lyskom-server-info (blocking-do 'get-server-info))
    (setq lyskom-server-version-info (blocking-do 'get-version-info))

    (let* ((aux-items (server-info->aux-item-list lyskom-server-info))
           (canonical-name-aux (car (lyskom-get-aux-item aux-items 31)))
           (invisible-sessions 0)
           (anonymous-sessions 0)
           (active-sessions 0)
           (inactive-sessions 0)
           (unknown-activity-sessions 0)
           (total-sessions (length session-info))
           (idle-hide (* 60 (if (numberp kom-idle-hide) kom-idle-hide 30)))
           (kom-extended-status-information
            (lyskom-extended-status-override 'server))
           momentary-index periodic-index)

      (setq aux-items (delq canonical-name-aux aux-items))

      ;; Compute the index into statistics that represents the
      ;; momentary value (when equals zero) and the periodic
      ;; value (when is at least five minutes).

      (when stats-desc
        (let ((min-time
               (apply 'min (listify-vector (stats-description->when
                                            stats-desc))))
              (max-time
               (apply 'max (listify-vector (stats-description->when
                                            stats-desc))))
              (i 0)
              (min-diff lyskom-max-int)
              max-time-index)
          (lyskom-traverse val (stats-description->when stats-desc)
            (when (eq val min-time) (setq momentary-index i))
            (when (eq val max-time) (setq max-time-index i))
            (when (and (>= val 300)
                       (< (- val 300) min-diff))
              (setq min-diff (- val 300)
                    periodic-index i))
            (setq i (1+ i)))
          (unless periodic-index (setq periodic-index max-time-index))))

      ;; ----------------------------------------
      ;; Compute session statistics
      (lyskom-traverse session session-info

        ;; Record anonymity
        (when (zerop (dynamic-session-info->person session))
          (setq anonymous-sessions (1+ anonymous-sessions)))

        ;; Record activity
        (if (session-flags->user_active_used (dynamic-session-info->flags session))
            (if (> (dynamic-session-info->idle-time session) idle-hide)
                (setq inactive-sessions (1+ inactive-sessions))
              (setq active-sessions (1+ active-sessions)))
          (setq unknown-activity-sessions (1+ unknown-activity-sessions)))

        ;; Record invisibility
        (when (session-flags->invisible (dynamic-session-info->flags session))
          (setq invisible-sessions (1+ invisible-sessions)))
        )

      ;; ----------------------------------------
      ;; Print header

      (lyskom-format-insert 'server-status-header
			    (lyskom-session-nickname)
			    lyskom-server-name
			    lyskom-server-port)

      ;; ----------------------------------------
      ;; Print software name and version

      (lyskom-format-insert 'server-status-version
                            (version-info->server-software server-version)
                            (version-info->software-version server-version))
      (lyskom-format-insert 'server-status-protocol
                            (version-info->protocol-version server-version))

      ;; ----------------------------------------
      ;; Print canonical name, if we have one

      (when canonical-name-aux
        (let ((canonical-name nil)
              (canonical-port nil))
          (if (string-match ":" (aux-item->data canonical-name-aux))
              (setq canonical-name (substring (aux-item->data canonical-name-aux) 0 (match-beginning 0))
                    canonical-port (substring (aux-item->data canonical-name-aux) (1+ (match-beginning 0))))
            (setq canonical-name (aux-item->data canonical-name-aux)))
          (lyskom-format-insert 'server-status-server canonical-name canonical-port)))

      ;; ----------------------------------------
      ;; Print start time and such

      (when boottime-info
        (lyskom-format-insert 'server-status-boot-time
                            (let ((kom-print-relative-dates nil))
                              (lyskom-format-time 
                               'date-and-time
                               (static-server-info->boot-time
                                boottime-info))))

        (lyskom-format-insert
         'server-status-save-time
         (let ((kom-print-relative-dates nil))
           (lyskom-format-time 
            'date-and-time
            (static-server-info->save-time
             boottime-info)))
         (cond ((equal "clean" (static-server-info->db-status boottime-info)) nil)
               (t (condition-case nil
                      (lyskom-get-string (intern (concat "db-status-" (static-server-info->db-status boottime-info))))
                    (error (static-server-info->db-status boottime-info))))))
         )

      ;; ----------------------------------------
      ;; Print time
      (lyskom-format-insert 'server-status-time
                            (let ((kom-print-relative-dates nil))
                              (lyskom-format-time 'date-and-time server-time)))

      ;; ----------------------------------------
      ;; Print session statistics

      (lyskom-format-insert 'server-status-sessions
                            total-sessions
                            active-sessions
                            inactive-sessions
                            unknown-activity-sessions
                            invisible-sessions
                            anonymous-sessions
                            (/ idle-hide 60))

      ;; ----------------------------------------
      ;; Print stats for confs and persons

      (when pers-stats
        (lyskom-format-insert 'server-status-pers
                              (stats->average
                               (elt pers-stats momentary-index))
                              (lyskom-format-time-units
                               (stats->ascent-rate
                                (elt pers-stats periodic-index)))
                              (static-server-info->existing-persons
                               boottime-info)))

      (when conf-stats
        (lyskom-format-insert 'server-status-confs
                              (stats->average
                               (elt conf-stats momentary-index))
                              (lyskom-format-time-units
                               (stats->ascent-rate
                                (elt conf-stats periodic-index)))
                              (static-server-info->existing-confs
                               boottime-info)))


      ;; ----------------------------------------
      ;; Print info on text numbers

      (when text-stats
        (lyskom-format-insert 'server-status-texts
                              (stats->average
                               (elt text-stats momentary-index))
                              (lyskom-format-time-units
                               (stats->ascent-rate
                                (elt text-stats periodic-index)))
                              (static-server-info->existing-texts
                               boottime-info)))
      (lyskom-format-insert 'server-status-first-text first-text)
      (lyskom-format-insert 'server-status-last-text 
                            highest-text
                            (when boottime-info
                              (static-server-info->highest-text-no
                               boottime-info)))


      ;; ----------------------------------------
      ;; Print remaining aux-items
      (lyskom-traverse-aux item aux-items
        (if (lyskom-aux-item-definition-field item 'status-print)
            (lyskom-aux-item-call item 'status-print item 'server)
          (lyskom-format-insert 'status-aux-item
                                (format "%d/%d" 
                                        (aux-item->aux-no item)
                                        (aux-item->tag item))
                                (aux-item->creator item)
                                (lyskom-aux-item-terminating-button item 'server))
          ))

      ;; ----------------------------------------
      ;; Print statistics

      (when (lyskom-extended-status-information 'raw-server-stats)
        (let* ((stats (lyskom-get-server-stats))
               (what (server-stats->what stats))
               (maxlen (and stats (apply 'max (mapcar 'length what))))
               (fmt (and maxlen (format "%%=%d#1s " maxlen))))
          (when stats
            (lyskom-format-insert 'status-server-stats)
            (lyskom-insert (lyskom-format fmt ""))
            (lyskom-traverse period (server-stats->when stats)
              (lyskom-format-insert "  %=8#1s" 
                                    (if (eq 0 period)
                                        (lyskom-get-string 'current-average)
                                      (lyskom-format-units period 
                                                           '((604800 . "w")
                                                             (86400 . "d")
                                                             (3600 . "h")
                                                             (60 . "m"))
                                                           "s"))))
            (lyskom-insert "\n")
            (lyskom-traverse item (server-stats->values stats)
              (let ((start (point-max))
                    (inhibit-read-only t))
                (lyskom-format-insert fmt (car item))
                (lyskom-traverse val (cdr item)
                  (lyskom-format-insert "  %=8.2.7#1f" (stats->average val)))
                (lyskom-format-insert "\n")
                (add-text-properties start (point-max) `(face ,kom-mark-face)))

              (lyskom-format-insert fmt "")
              (lyskom-insert " ")
              (let ((index 0))
                (lyskom-traverse val (cdr item)
                  (if (eq (elt (server-stats->when stats) index) 0)
                      (lyskom-insert "          ")
                    (lyskom-format-insert " %=8.2.7#1f+"
                                          (stats->ascent-rate val)))
                  (setq index (1+ index))))
              (lyskom-format-insert "\n")

              (lyskom-format-insert fmt "")
              (lyskom-insert " ")
              (let ((index 0))
                (lyskom-traverse val (cdr item)
                  (if (eq (elt (server-stats->when stats) index) 0)
                      (lyskom-insert "          ")
                    (lyskom-format-insert " %=8.2.7#1f-"
                                          (stats->descent-rate val)))
                  (setq index (1+ index))))
              (lyskom-format-insert "\n")
              ))))


      ;; ----------------------------------------
      ;; Print MOTD (if there is one)
      (when (not (zerop (server-info->motd-of-lyskom server-info)))
        (lyskom-insert 'server-status-has-motd)
        (lyskom-view-text (server-info->motd-of-lyskom server-info)))

)))


;;; ================================================================
;;; Rekommendera möte

(def-kom-command kom-recommend-conference ()
  "Recommend a conference to new LysKOM users.
Thie command can only be used if you have administrative rights
to the LysKOM server."
  (interactive)
  (let* ((conf-stat (lyskom-read-conf-stat 'recommend-which-conf
                                           '(conf) nil nil t))
         (priority (and (lyskom-j-or-n-p 'recommend-set-priority-q)
                        (lyskom-read-num-range 0 255 'priority-q)))
         (mship-type (and priority
                          (lyskom-j-or-n-p 'recommend-set-mship-type-q)
                          (lyskom-read-membership-type)))

         (aux-item (lyskom-create-aux-item 
                    0 29 nil nil
                    (lyskom-create-aux-item-flags nil nil nil nil
                                                  nil nil nil nil)
                    0
                    (mapconcat 'lyskom-format-object
                               (delq nil
                                     (list (conf-stat->conf-no conf-stat)
                                           priority
                                           mship-type))
                               " "))))
    (lyskom-format-insert 'recommending-conf
                          conf-stat
                          priority
                          (and mship-type
                               (lyskom-return-membership-type mship-type)))
    (lyskom-report-command-answer (blocking-do 'modify-server-info
                                               nil
                                               (list aux-item)))))


;;; ================================================================
;;; Add send-comments-to
;;;

(def-kom-command kom-redirect-comments ()
  "Add extended information to your letterbox that causes some other
conference to be added as a recipient to comments to your texts when
someone posts them to a conference you are not a member of.

Note that this is advisory only; clients may ignore your redirection."
  (interactive)
  (let* ((conf-stat (lyskom-read-conf-stat 'redirect-for-whom
                                           '(pers) nil nil t))
         (redirect-to (lyskom-read-conf-stat 'redirect-to-which-conf
                                             '(pers conf) nil nil t))
         (old-redirect (car (lyskom-get-aux-item 
                             (conf-stat->aux-items conf-stat)
                             33))))
    (lyskom-format-insert 'redirecting-comments-to
                          conf-stat redirect-to old-redirect)
    (lyskom-report-command-answer
     (blocking-do 'modify-conf-info
                  (conf-stat->conf-no conf-stat)
                  (and old-redirect (list (aux-item->aux-no old-redirect)))
                  (list (lyskom-create-aux-item 
                         0
                         33
                         nil nil
                         (lyskom-create-aux-item-flags
                          nil nil nil nil 
                          nil nil nil nil)
                         0
                         (format "%d" (conf-stat->conf-no redirect-to)))))
     nil
     '((illegal-aux-item . kom-redirect-comments-e48)
       (aux-item-permission . kom-redirect-comments-e48))))
  )


;;; ================================================================
;;; Kamikaze functions!

(def-kom-command kom-join-all-conferences ()
  "Join all conferences.

You want to be really careful doing this. It will take a while and
is probably not what you really want to do." 
  (interactive)
  (let* ((conf-nos (lyskom-get-all-conferences t))
         (confirm-each (or (lyskom-j-or-n-p 'confirm-each-join)
                           (not (lyskom-j-or-n-p 
                                 (lyskom-format 'no-confirm-each-sure
                                                (length conf-nos))))))
         (mship-type (unless (lyskom-j-or-n-p 'confirm-each-msg)
                       (lyskom-create-membership-type
                        nil nil nil (lyskom-j-or-n-p 'receive-each-msg)
                        nil nil nil nil)))

         (no-of-unread (lyskom-read-num-range-or-date 
                        0 
                        lyskom-max-int
                        (lyskom-format 'initial-unread)
                        nil
                        t
                        nil))
         (kom-membership-default-priority
          (lyskom-read-num-range 0 255 
                                 (lyskom-get-string 'priority-q)
                                 nil nil)))
    (while conf-nos
      (unless (or (lyskom-get-membership (car conf-nos) t)
                  (and confirm-each
                       (not (lyskom-j-or-n-p (lyskom-format 'confirm-join 
                                                            (car conf-nos))))))
        (lyskom-add-member-by-no (car conf-nos) lyskom-pers-no 
                                 no-of-unread mship-type)
        (sit-for 0))
      (setq conf-nos (cdr conf-nos)))))



(def-kom-command kom-leave-all-conferences ()
  "Leave all conferences.

This command will ignore certain conferences. It will not leave your
letterbox, secret or closed conferences without confirmation. Note
that this command could take a very long time to complete."
  (interactive)
  (let* ((auto-regular nil)
         (auto-secret nil)
         (auto-closed nil)
         (confs nil))
    (lyskom-message (lyskom-get-string 'getting-all-confs))
    (let* ((xlist (lyskom-get-all-conferences))
           (count (length xlist))
           (index 0))
      (setq confs (delq nil
                        (mapcar (lambda (conf-no)
                                  (setq index (1+ index))
                                  (lyskom-message
                                   (lyskom-format 'getting-all-confs-progress index count))
                                  (when (or (lyskom-try-get-membership conf-no t)
                                            (lyskom-is-member conf-no lyskom-pers-no))
                                    conf-no))
                                xlist)))
      (lyskom-message (lyskom-get-string 'getting-all-confs-done)))

    (while confs
      (let* ((conf (car confs))
             (conf-stat (blocking-do 'get-conf-stat conf))
             (conf-type (conf-stat->conf-type conf-stat)))
        (setq lyskom-last-viewed (point-max))
        (lyskom-format-insert 'unsubscribe-to-2
                              conf-stat
                              (lyskom-conf-type-marker conf-stat))
        (when (cond 
               ;; Won't auto-unsub letterbox
               ((= (conf-stat->conf-no conf-stat) lyskom-pers-no)
                (lyskom-insert (lyskom-get-string
                                'unsub-all-skipping-letterbox))
                nil)

               ;; Won't unsub if we are supervisor
               ((lyskom-i-am-supervisor conf-stat t)
                (lyskom-insert-string 'unsub-all-skipping-supervised)
                nil)

               ;; Check secret conferences
               ((conf-type->secret conf-type)
                (if auto-secret
                    t
                  (let ((ans (lyskom-a-or-b-or-c-p (lyskom-format 'unsub-secret-conf-q conf-stat)
                                                   '(abc-yes abc-no unsub-all-secret)
                                                   'abc-no)))
                    (cond ((eq ans 'abc-no)
                           (lyskom-insert-string 'cancelled) nil)
                          ((eq ans 'abc-yes) t)
                          ((eq ans 'unsub-all-secret)
                           (setq auto-secret t)
                           t)))))

               ;; Skip closed only if doit is 
               ((conf-type->rd_prot conf-type)
                (if auto-closed
                    t
                  (let ((ans (lyskom-a-or-b-or-c-p (lyskom-format 'unsub-closed-conf-q conf-stat)
                                                   '(abc-yes abc-no unsub-all-closed)
                                                   'abc-no)))
                    (cond ((eq ans 'abc-no) 
                           (lyskom-insert-string 'cancelled) nil)
                          ((eq ans 'abc-yes) t)
                          ((eq ans 'unsub-all-closed)
                           (setq auto-closed t)
                           t)))))

               ;; Regular conference
               (t 
                (if auto-regular
                    t
                  (let ((ans (lyskom-a-or-b-or-c-p (lyskom-format 'unsub-open-conf-q conf-stat)
                                                   '(abc-yes abc-no unsub-all-open)
                                                   'abc-no)))
                    (cond ((eq ans 'abc-no) 
                           (lyskom-insert-string 'cancelled) nil)
                          ((eq ans 'abc-yes) t)
                          ((eq ans 'unsub-all-open)
                           (setq auto-regular t)
                           t))))))


          ;; How's that for a really long condition? Lots of
          ;; cool side effects and user interactions. Don't
          ;; you just love functional programming?

          ;; Now we've either printed a message or we've
          ;; decided to unsubscribe.

          (lyskom-sub-member (blocking-do 'get-conf-stat lyskom-pers-no)
                             conf-stat
                             t)

          ))
      (sit-for 0)
      (setq confs (cdr confs)))
    ))


(def-kom-command kom-limit-import (conf-stat)
  "Specify restrictions on importing e-mail. Note that such restrictions
are advisory; clients may ignore them."
  (interactive (list (lyskom-read-conf-stat 'limit-import-to-conf
                                            '(conf pers) nil nil t)))
  (when conf-stat
    (let* ((what (lyskom-a-or-b-or-c-p 'limit-import-of-what
                                       '(abc-regexp abc-spam abc-html abc-everything)
                                       'abc-spam))
           (data (cond ((eq what 'abc-spam) "spam")
                       ((eq what 'abc-html) "html")
                       ((eq what 'abc-everything) "all"))))
      (when (or (lyskom-is-supervisor (conf-stat->conf-no conf-stat)
                                      lyskom-pers-no)
                (lyskom-j-or-n-p 'limit-import-not-super))
        (cond 
	 ((and data (memq what '(abc-spam abc-html abc-everything)))
	  (lyskom-format-insert 'limiting-import
                                (substring (lyskom-get-string what) 1)
                                conf-stat)
          (lyskom-report-command-answer
           (blocking-do 'modify-conf-info
                        (conf-stat->conf-no conf-stat)
                        nil
                        (list
                         (lyskom-create-aux-item
                          0 35 nil nil
                          (lyskom-create-aux-item-flags nil nil nil nil 
                                                        nil nil nil nil)
                          0 data))))
          (cache-del-conf-stat (conf-stat->conf-no conf-stat)))
	 ((eq what 'abc-regexp)
	  (let ((regexp (lyskom-read-string 
			 (lyskom-get-string 'limit-import-regexp-q)
			 nil nil)))
	    (lyskom-format-insert 'limiting-import-regexp
				  regexp
				  conf-stat)
	    (lyskom-report-command-answer
	     (blocking-do 'modify-conf-info
			  (conf-stat->conf-no conf-stat)
			  nil
			  (list
			   (lyskom-create-aux-item
			    0 10105 nil nil
			    (lyskom-create-aux-item-flags nil nil nil nil
							  nil nil nil nil)
			    0 regexp))))
	    (cache-del-conf-stat (conf-stat->conf-no conf-stat)))))))))


(def-kom-command kom-change-message-flag (uconf-stat)
  "Specify whether to receive group messages to a particular conference."
  (interactive (list (lyskom-read-uconf-stat 'set-message-flag-for-conf
                                            '(membership) nil
                                            lyskom-current-conf t)))
  (when uconf-stat
    (let* ((mship (lyskom-get-membership (uconf-stat->conf-no uconf-stat) t)))
      (if (null mship)
          (lyskom-format-insert 'not-member-of-conf uconf-stat)
        (set-membership-type->message-flag
         (membership->type mship)
         (lyskom-j-or-n-p (lyskom-format 'set-message-flag-to-what
                                         uconf-stat)))
        (lyskom-format-insert 'setting-message-flag
                              (membership-type->message-flag
                               (membership->type mship))
                              uconf-stat)
        (lyskom-report-command-answer
         (blocking-do 'set-membership-type
                      lyskom-pers-no
                      (uconf-stat->conf-no uconf-stat)
                      (membership->type mship)))
        (lp--update-buffer (uconf-stat->conf-no uconf-stat))))))

(def-kom-command kom-list-new-conferences (arg)
  "List conferences created since the last time this command
was given. With prefix argument, prompt for a date to start at."
  (interactive "P")
  (lyskom-list-new-conferences 'lyskom-last-known-conf-no
                               'conferences
                               (lambda (el)
                                 (not (conf-type->letterbox
                                       (conf-stat->conf-type el))))
                               arg))

(def-kom-command kom-list-new-persons (arg)
  "List persons created since the last time this command
was given. With prefix argument, prompt for a date to start at."
  (interactive "P")
  (lyskom-list-new-conferences 'lyskom-last-known-pers-no
                               'persons
                               (lambda (el)
                                 (conf-type->letterbox
                                  (conf-stat->conf-type el)))
                               arg))

(defun lyskom-list-new-conferences (varsym obj filter &optional list-from-date)
  "List conferences created since a particular point in history.

VARSYM is the name of a variable that should be bound to a cons of the 
last conference number displayed and the date when it was displayed 
\(lyskom-time structure).

OBJ is the type of object. It should be a symbol that can be passed
to lyskom-get-string to get a text representation of the object type.

FILTER is a function called on each conference. It should return
non-nil for conferences that should be displayed.

If optional LIST-FROM-DATE is non-nil, prompt for a date and list
all conferences from that date forward."
  (interactive)
  (let* ((last-conf-no (blocking-do 'first-unused-conf-no))
         (var (or (and (null list-from-date) 
                       (symbol-value varsym))
                  (let* ((n (lyskom-read-date 
                             (lyskom-format 'list-confs-from-date
                                            (lyskom-get-string obj))
                             t))
                         (date (and n (lyskom-create-time 0 0 0 (elt n 2)
                                                          (elt n 1) (elt n 0)
                                                          0 0 nil)))
                         (conf (and n (lyskom-find-conf-by-date date))))
                    (cond 

                     ;; No date -- we want it all!
                     ((null n) (cons 1 nil))

                     ;; Date but no conf -- no confs to list!
                     ((null conf) (cons last-conf-no date))

                     ;; Date and conf -- list from this conf
                     (t (cons (conf-stat->conf-no conf) date))))))
         (conf-no (car var))
         (time-string (condition-case nil
                          (and (cdr var) (lyskom-format-time
                                          'date-and-time (cdr var)))
                        (error nil))))

    (if (null last-conf-no)
        (lyskom-format-insert 'no-support-in-server))

    ;; There's a serious bug here: we can't cancel!
    ;; That *has* to be fixed.

    (let ((count (cons 0 (make-marker)))
          (calls nil))
      (condition-case nil
          (progn 
            (while (< conf-no last-conf-no)
              (setq calls 
                    (cons
                     (initiate-get-conf-stat 
                      'main
                      (lambda (conf filter count time-string)
                        (when (and conf (funcall filter conf))
                          (when (eq (car count) 0)
                            (lyskom-format-insert 'new-conferences-since 
                                                  time-string (lyskom-get-string obj)))
                          (rplaca count (1+ (car count)))
                          (lyskom-format-insert "%5#1m %#2c %#1M\n"
                                                conf
                                                (lyskom-list-conf-membership-char
                                                 (conf-stat->conf-no conf)))
                          (set-marker (cdr count) (point))))
                      conf-no filter count time-string)
                     calls))
              (setq conf-no (1+ conf-no)))
            (lyskom-wait-queue 'main))
        (quit 
         (lyskom-message (lyskom-get-string 'canceling-command))
         (lyskom-cancel-calls calls)
         (signal 'quit nil)))
      (when (marker-position (cdr count))
        (goto-char (cdr count))
	(set-marker (cdr count) nil))

      (cond ((eq 0 (car count))
             (lyskom-format-insert 'no-new-conferences 
                                   time-string (lyskom-get-string obj))) 

            (t 
               (when (or kom-auto-confirm-new-conferences
                         (lyskom-j-or-n-p (lyskom-format 'mark-confs-as-known
                                                         (lyskom-get-string obj)
                                                         (car count))))
                 (set varsym (cons conf-no (lyskom-current-client-time)))
                 (lyskom-save-options lyskom-buffer
                                      nil
                                      nil
                                      nil)))))))




(def-kom-command kom-change-privileges (&optional set-all)
  "Change privileges for a user.
Using this command you can change all privileges for a user."
  (interactive "P")
  (let* ((uconf-stat (lyskom-read-uconf-stat 'what-pers-privs-to-change
                                             '(pers) nil "" t))
         (pers-stat (blocking-do 'get-pers-stat
                                 (uconf-stat->conf-no uconf-stat)))
         (privs (pers-stat->privileges pers-stat)))

    (lyskom-format-insert
     'change-pers-privs-prompt
     uconf-stat
     (lyskom-privilege-string privs nil "\n    "))

    (let* ((wheel (lyskom-j-or-n-p (lyskom-get-string 'set-wheel-priv-q)))
           (admin (lyskom-j-or-n-p (lyskom-get-string 'set-admin-priv-q)))
           (statistic (lyskom-j-or-n-p (lyskom-get-string 'set-statistic-priv-q)))
           (create-pers (lyskom-j-or-n-p (lyskom-get-string 'set-create-pers-priv-q)))
           (create-conf (lyskom-j-or-n-p (lyskom-get-string 'set-create-conf-priv-q)))
           (change-name (lyskom-j-or-n-p (lyskom-get-string 'set-change-name-priv-q)))
           (flg7 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg7-priv-q)) (privs->flg7 privs)))
           (flg8 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg8-priv-q)) (privs->flg8 privs)))
           (flg9 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg9-priv-q)) (privs->flg9 privs)))
           (flg10 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg10-priv-q)) (privs->flg10 privs)))
           (flg11 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg11-priv-q)) (privs->flg11 privs)))
           (flg12 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg12-priv-q)) (privs->flg12 privs)))
           (flg13 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg13-priv-q)) (privs->flg13 privs)))
           (flg14 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg14-priv-q)) (privs->flg14 privs)))
           (flg15 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg15-priv-q)) (privs->flg15 privs)))
           (flg16 (if set-all (lyskom-j-or-n-p (lyskom-get-string 'set-flg16-priv-q)) (privs->flg16 privs)))
           (new-privs (lyskom-create-privs wheel admin statistic create-pers 
                                           create-conf change-name flg7 flg8
                                           flg9 flg10 flg11 flg12
                                           flg13 flg14 flg15 flg16)))

      (if (not (blocking-do 
                'set-priv-bits
                (pers-stat->pers-no pers-stat)
                new-privs))
          (progn (lyskom-insert-string 'nope)
                 (lyskom-insert-error))
        (when (setq pers-stat 
                    (cache-get-pers-stat (pers-stat->pers-no pers-stat)))
          (set-pers-stat->privileges pers-stat new-privs))
        (lyskom-insert-string 'done)))))




;;; ================================================================
;;; Temporary function for when we moved kom-extended-command from a
;;; to x.

(defun kom-obsolete-extended-command-binding ()
  "Temporary function for when we moved kom-extended-command from a"
  (interactive)
  (lyskom-format-insert-before-prompt "\
%#1@----------------------------------------------------------------
Tangenten för inmatning av kommandon har flyttat från a till x.

Använd x istället för a om du vill ange LysKOM-kommandon vid
namn. För att undvika att du kör oönskade LysKOM-kommandon så
ignoreras all inmatning tills du trycker enter nästa gång. 

Tryck på enter för att fortsätta.
----------------------------------------------------------------
"
                                      `(face ,kom-warning-face))
  ;; Beep both visibly and audibly, if possible.  We *want* to be annoying.
  (let ((visible-bell t))
    (ding))
  (let ((visible-bell nil))
    (ding))
  (read-from-minibuffer 
   (lyskom-format "%#1@Tryck return eller enter för att gå vidare: "
                  `(face ,kom-warning-face))))


(defun kom-obsolete-who-is-on-in-conference ()
  "Temporary function for when we moved kom-who-is-on-in-conference from l v to 
v m"
  (interactive)
  (lyskom-insert-before-prompt "Kommandot \"Vilka (är inloggade i) möte\" är flyttat till v m\n"))


(defun kom-apropos (re do-all)
  "List LysKOM-related symbols whose name or documentation matches a regexp.
With prefix argument, also list symbols that are not part of the semi-stable
interface (i.e. symbols whose name starts with \"lyskom\").

This command is intended primarily for developers and advanced users."
  (interactive "sLysKOM-apropos (regexp): \nP")
  (message "Searching for %s..." re)
  (let ((result nil)
        (case-fold-search t))
    (mapatoms
     (lambda (atom)
       (when (and (or (boundp atom) (fboundp atom))
                  (or (and do-all (string-match "^lyskom-" (symbol-name atom)))
                      (string-match "^kom-" (symbol-name atom)))
                  (or (string-match re (symbol-name atom))
                      (and (fboundp atom)
                           (string-match re (or (documentation atom) "")))
                      (and (boundp atom)
                           (string-match re (or
                                             (documentation-property 
                                              atom 'variable-documentation)
                                             "")))))
         (setq result (cons atom result)))))
    (setq result 
          (sort result (lambda (a b)
                         (string-lessp (symbol-name a)
                                       (symbol-name b)))))
    (message "Searching for %s...formatting..." re)
    (if result
        (let ((buffer (get-buffer-create "*LysKOM-Apropos*")))
          (save-selected-window
            (let ((inhibit-read-only t))
              (set-buffer buffer)
              (help-mode)
              (lyskom-view-mode)
              (erase-buffer)
              (insert "\
In this buffer, go to the name of the symbol and type * to
get full documentation.

")
              (lyskom-traverse el result
                (lyskom-apropos-insert el))
	      (lyskom-read-only-mode 1))
            (message "Searching for %s...formatting...done" re)
            (goto-char (point-min))
            (display-buffer buffer)))
      (message "Nothing found that matches \"%s\"" re))))


(defun lyskom-apropos-insert (sym)
  (let ((start (point)))
    (insert (symbol-name sym))
    (add-text-properties start (point) 
                         `(face bold 
                           mouse-face highlight
                           lyskom-button t
                           lyskom-button-text ,(symbol-name sym)
                           lyskom-button-type func
                           lyskom-buffer ,(current-buffer)
                           lyskom-button-arg (lyskom-apropos-item ,sym)))
    (insert "\n")
    (lyskom-apropos-insert-docstring 'func
                                     sym
                                     (when (fboundp sym)
                                       (documentation sym)))
    (lyskom-apropos-insert-docstring 'var
                                     sym 
                                     (when (boundp sym)
                                       (documentation-property 
                                        sym 'variable-documentation)))
    (insert "\n")))

(defun lyskom-apropos-insert-docstring (type sym doc)
  (when doc
    (insert "    ")
    (let ((start (point)))
      (cond ((eq type 'func) (insert "Func: "))
            ((eq type 'var)  (insert "Var:  ")))
      (when (not (eq start (point)))
        (add-text-properties start (point)
                             `(face italic
                                    mouse-face highlight
                                    lyskom-button t
                                    lyskom-button-text ""
                                    lyskom-button-type func
                                    lyskom-buffer ,(current-buffer)
                                    lyskom-button-arg (lyskom-apropos-item 
                                                       (,sym ,type)))))

      (insert
       (lyskom-truncate-string-to-width
        (if (string-match "\\(\r\\|\n\\)" doc)
            (substring doc 0 (string-match "\\(\r\\|\n\\)" doc))
          doc)
        (- (window-width) 12)))
      (insert "\n"))))


(defun lyskom-apropos-item (args)
  (let ((sym (elt args 0))
        (type (elt args 1)))
    (cond ((eq type 'func) (describe-function sym))
          ((eq type 'var)  (describe-variable sym))
          (t  (cond ((fboundp sym)
                     (describe-function sym))
                    ((boundp sym)
                     (describe-variable sym)))))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: review.el
;;;;
;;;; This file contains functions to review articles in different ways.
;;;; Both the review commands themselves, the functions called by them
;;;; and the underlying functions are here.
;;;;
;;;; Most, if not all, of these functions are written by Linus Tolke.
;;;;


(eval-when-compile
  (require 'lyskom-command "command"))

(put 'lyskom-cant-review-error 'error-conditions '(error lyskom-error lyskom-review-error))
(put 'lyskom-cant-review-error 'error-message "Can't review")
(put 'lyskom-review-error 'error-condiions '(error lyskom-error))
(put 'lyskom-review-error 'error-message "Review error")


;;; ======================================================================
;;; State-saving variables
;;;
;;; All these need to be buffer-local

(def-kom-var lyskom-last-review-by nil local)
(def-kom-var lyskom-last-review-to nil local)
(def-kom-var lyskom-last-review-num nil local)
(def-kom-var lyskom-last-review-pmark nil local)
(def-kom-var lyskom-last-review-cmark nil local)
(def-kom-var lyskom-last-review-filter nil local)
(def-kom-var lyskom-last-review-saved-result-list nil local)
(def-kom-var lyskom-last-review-saved-by-list nil local)
(def-kom-var lyskom-last-review-saved-to-list nil local)
(def-kom-var lyskom-last-review-saved-result-size 0 local)
(def-kom-var lyskom-last-review-saved-smallest nil local)
(def-kom-var lyskom-last-review-saved-largest nil local)
(def-kom-var lyskom-last-review-comments nil local)
(def-kom-var lyskom-have-review nil local)

(def-kom-var lyskom-last-unread-by nil local)
(def-kom-var lyskom-last-unread-to nil local)
(def-kom-var lyskom-last-unread-num nil local)
(def-kom-var lyskom-last-unread-pmark nil local)
(def-kom-var lyskom-last-unread-cmark nil local)
(def-kom-var lyskom-last-unread-filter nil local)
(def-kom-var lyskom-last-unread-saved-result-list nil local)
(def-kom-var lyskom-last-unread-saved-by-list nil local)
(def-kom-var lyskom-last-unread-saved-to-list nil local)
(def-kom-var lyskom-last-unread-saved-result-size 0 local)
(def-kom-var lyskom-last-unread-saved-smallest nil local)
(def-kom-var lyskom-last-unread-saved-largest nil local)
(def-kom-var lyskom-have-unread nil local)

(defvar lyskom-default-review-filter nil
  "Default filter for reviewing texts")


(defun lyskom-remove-zeroes (a)
  "Returns a copy of list where all zeroes are removed."
  (delq 0 (copy-sequence a)))


;;; ================================================================
;;; Temoporary change of mark-as-read

(defun lyskom-maybe-run-mouse-2 (command)
  (lyskom-xemacs-or-gnu 
   nil
   (when (and (eq command 'mouse-drag-region)
	      unread-command-events
	      (eventp (car unread-command-events))
	      (eq (lyskom-event-type 
		   (car unread-command-events)) 'mouse-2))
     (let* ((sequence (vector (car unread-command-events)))
	    (command (or (lookup-key (current-local-map) 
				     sequence)
			 (lookup-key global-map sequence))))
       (setq unread-command-events (cdr unread-command-events))
       (when (commandp command) 
	 (call-interactively command))))))

(defun kom-toggle-mark-as-read-prefix ()
  "Read one key sequence and run one command with state of
kom-review-marks-texts-as-read toggled."
  (interactive)
  (let* ((kom-review-marks-texts-as-read (not kom-review-marks-texts-as-read))
	 (sequence (read-key-sequence
		    (format "%s: " 
			    (lyskom-get-string
			     (if kom-review-marks-texts-as-read 
				 'review-marking-as-read
			       'review-not-marking-as-read)))))
	 (command (or (lookup-key (current-local-map) sequence)
		      (lookup-key global-map sequence))))
    (when (or (null command) (eq command 'kom-mouse-null))
      (setq sequence (read-key-sequence ""))
      (setq command (or (lookup-key (current-local-map) sequence)
			(lookup-key global-map sequence))))
    (when (commandp command) 
      (call-interactively command))
    (lyskom-maybe-run-mouse-2 command)))

(defun kom-toggle-cache-prefix ()
  "Read one key sequence and run one command with state of
kom-review-marks-texts-as-read toggled."
  (interactive)
  (let* ((kom-review-uses-cache (not kom-review-uses-cache))
         (sequence (read-key-sequence
                    (format "%s: " 
                            (lyskom-get-string
                             (if kom-review-uses-cache 
                                 'review-using-cache
                               'review-not-using-cache)))))
         (command (lookup-key (current-local-map) sequence)))
    (when (commandp command)
      (call-interactively command))
    (lyskom-maybe-run-mouse-2 command)))

;; (defun kom-toggle-topic-prefix ()
;;   "Read one key sequence and run one command with the review
;; filter set to only show texts that are not comments."
;;   (interactive)
;;   (let* ((lyskom-default-review-filter 'lyskom-review-filter-roots)
;; 	 (sequence (read-key-sequence
;; 		    (format "%s: " "Enbart urinlägg")))
;; 	 (command (lookup-key (current-local-map) sequence)))
;;     (when (commandp command)
;;       (call-interactively command))))



;;; ================================================================
;;;              ]terse av, till - Review by X to Conference Y.

;;; Author: David Byers


(def-kom-command kom-review-all ()
  "Review all texts written by a particular author to a particular
conference. This can also be accomplished by using `kom-review-by-to'
and specifying zero texts.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive)
  (lyskom-tell-internat 'kom-tell-review)
  (lyskom-review-by-to 0))

(def-kom-command kom-review-all-roots ()
  "Review all texts that are not comments written by a particular
author to a particular conference. This can also be accomplished
by using `kom-review-roots-by-to' and specifying zero texts.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive)
  (lyskom-tell-internat 'kom-tell-review)
  (lyskom-review-by-to 0 'lyskom-review-filter-roots))

(def-kom-command kom-unread-all ()
  "Mark all texts written by a particular author to a particular
conference as unread. This can also be accomplished by using
`kom-unread-by-to' and specifying zero texts."
  (interactive)
  (lyskom-unread-by-to 0))

(def-kom-command kom-unread-all-roots ()
  "Mark all texts that are not comments written by a particular
author to a particular conference as unread. This can also be
accomplished by using `kom-unread-roots-by-to' and specifying
zero texts."
  (interactive)
  (lyskom-unread-by-to 0 'lyskom-review-filter-roots))

(def-kom-command kom-review-more (count)
  "Review more texts using the same critera as the last review
performed with `kom-review-by-to'. The review will be resumed where
the previous review finished.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive "P")
  (if (not lyskom-have-review)
      (lyskom-format-insert 'no-review-done)
    (let* ((count (or count
		      (lyskom-read-number
		       (lyskom-get-string 'review-how-many-more)
		       (abs lyskom-last-review-num))))
           (info (progn (if (and (listp count)
                                 (integerp (car count))
                                 (null (cdr count)))
                            (setq count (car count)))
                        (cond ((zerop count) 
                               (setq count nil)
                               (lyskom-get-string 'review-rest))
                              ((> count 0)
                               (lyskom-format (lyskom-get-string 'review-more)
                                              count)))))
           (by lyskom-last-review-by)
           (to lyskom-last-review-to)
	   (filter lyskom-last-review-filter))

      (lyskom-format-insert 'review-more-info-by-to
                            info
                            (if (zerop by)
                                (lyskom-get-string 'anybody)
                              by)
                            (if (zerop to)
                                (lyskom-get-string 'all-confs)
                              to)
			    (when filter
			      (funcall filter 'description)))
    
      (condition-case arg
          (let ((list (lyskom-get-texts-by-to by to count t nil filter)))
            (setq lyskom-last-review-num 
                  (if (< lyskom-last-review-num 0)
                      (- count)
                    count))
            (if list
                (lyskom-review-enter-read-info
                 (lyskom-create-read-info
                  'REVIEW
                  nil
                  (lyskom-review-get-priority)
                  (lyskom-create-text-list list)
                  nil t) t)
              (lyskom-insert-string 'no-such-text)))
        (lyskom-review-error (if arg
                                 nil
                               (lyskom-insert-string 'no-such-text)))))))

(def-kom-command kom-unread-more (count)
  "Mark more texts unread using the same critera as the last 
mark unread performed with `kom-unread-by-to'."
  (interactive "P")
  (if (not lyskom-have-unread)
      (lyskom-format-insert 'no-unread-done)
    (let* ((count (or count
		      (lyskom-read-number
		       (lyskom-get-string 'unread-how-many-more)
		       (abs lyskom-last-unread-num))))
           (info (progn (if (and (listp count)
                                 (integerp (car count))
                                 (null (cdr count)))
                            (setq count (car count)))
                        (cond ((zerop count) 
                               (setq count nil)
                               (lyskom-get-string 'unread-rest))
                              ((> count 0)
                               (lyskom-format (lyskom-get-string 'unread-more)
                                              count)))))
           (by lyskom-last-unread-by)
           (to lyskom-last-unread-to)
	   (filter lyskom-last-review-filter))

      (lyskom-format-insert 'unread-more-info-by-to
                            info
                            (if (zerop by)
                                (lyskom-get-string 'anybody)
                              by)
                            (if (zerop to)
                                (lyskom-get-string 'all-confs)
                              to)
			    (when filter
			      (funcall filter 'description)))
    
      (condition-case arg
          (let* ((lyskom-last-review-num count)
		 (list (lyskom-get-texts-by-to by to count t t filter)))
            (setq lyskom-last-unread-num 
                  (if (< lyskom-last-unread-num 0)
                      (- count)
                    count))
            (if list
              (lyskom-traverse text-no list
                (unless (lyskom-mark-unread text-no)
                  (lyskom-format-insert 'cant-mark-text-unread
                                        text-no
                                        (lyskom-get-error-text lyskom-errno))))
              (lyskom-insert-string 'no-such-text)))
        (lyskom-review-error (if arg
                                 nil
                               (lyskom-insert-string 'no-such-text)))))))



(def-kom-command kom-review-first (&optional count)
  "Review the first N texts written by a particular author to some
conference. With no author specified, review texts by all authors.
With zero texts specified, review all text. With no conference
specified, review texts to all conferences. With a negative number of
texts, review the last N texts instead of the first (you can use
`kom-review-by-to' instead.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive "P")
  (lyskom-review-by-to (- (or count
                           (lyskom-read-number
                            (lyskom-get-string 'review-how-many) 1)))))

(def-kom-command kom-review-first-roots (&optional count)

  "Review the first N texts that are not comments written by a
particular author to some conference. With no author specified,
review texts by all authors. With zero texts specified, review
all text. With no conference specified, review texts to all
conferences. With a negative number of texts, review the last N
texts instead of the first (you can use `kom-review-roots-by-to'
instead.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive "P")
  (lyskom-review-by-to (- (or count
                           (lyskom-read-number
                            (lyskom-get-string 'review-how-many-roots) 1)))
		       'lyskom-review-filter-roots))

(def-kom-command kom-unread-first (&optional count)
  "Mark the first N texts written by a particular author to some
conference as unread. With no author specified, review texts by all 
authors. With zero texts specified, review all text. With no conference
specified, review texts to all conferences. With a negative number of
texts, review the last N texts instead of the first (you can use
`kom-unread-by-to' instead."
  (interactive "P")
  (lyskom-unread-by-to (- (or count
                           (lyskom-read-number
                            (lyskom-get-string 'unread-how-many) 1)))))

(def-kom-command kom-unread-first-roots (&optional count)
  "Mark the first N texts that are not comments written by a
particular author to some conference as unread. With no author
specified, review texts by all authors. With zero texts
specified, review all text. With no conference specified, review
texts to all conferences. With a negative number of texts, review
the last N texts instead of the first (you can use
`kom-unread-roots-by-to' instead."
  (interactive "P")
  (lyskom-unread-by-to (- (or count
                           (lyskom-read-number
                            (lyskom-get-string 'unread-how-many-roots) 1)))
		       'lyskom-review-filter-roots))


(def-kom-command kom-review-by-to (&optional count)
  "Review the last N texts written by a particular author to some
conference. With no author specified, review texts by all authors.
With zero texts specified, review all texts. With no conference
specified, review texts to all conferences. With a negative number of
texts, review the last N texts instead of the first (you can use
`kom-review-first' instead.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive "P")
  (lyskom-review-by-to (or count
                           (lyskom-read-number
                            (lyskom-get-string 'review-how-many) 1))))

(def-kom-command kom-review-roots-by-to (&optional count)
  "Review the last N texts, excluding comments, written by a particular
author to some conference. With no author specified, review texts by all
authors. With zero texts specified, review all texts. With no conference
specified, review texts to all conferences. With a negative number of
texts, review the last N texts instead of the first (you can use
`kom-review-first-roots' instead.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive "P")
  (lyskom-review-by-to (or count
                           (lyskom-read-number
                            (lyskom-get-string 'review-how-many-roots) 1))
		       'lyskom-review-filter-roots))

(defun lyskom-review-by-to (count &optional filter)
  "Common function for kom-review-by-to and kom-review-first"
  (setq filter (or filter lyskom-default-review-filter))
  (let* ((info (progn (if (and (listp count)
                               (integerp (car count))
                               (null (cdr count)))
                          (setq count (car count)))
                      (cond ((zerop count) 
                             (setq count nil)
                             (lyskom-get-string 'everything))
                            ((> count 0)
                             (lyskom-format 'latest-n count))
                            ((< count 0)
                             (lyskom-format 'first-n
                                            (- count))))))
         (by (lyskom-read-conf-no
	      `(review-by-whom ,info ,(and filter (funcall filter 'description)))
	      '(pers) t nil t))
         (to (lyskom-read-conf-no
	      `(review-to-conf ,info ,(and filter (funcall filter 'description)))
	      '(all) t nil t)))

    (if (not (zerop to))
        (cache-del-conf-stat to))
    (if (not (zerop by)) 
        (cache-del-pers-stat by))

    (lyskom-format-insert 'review-info-by-to
                          info
                          (if (zerop by)
                              (lyskom-get-string 'anybody)
                            by)
                          (if (zerop to)
                              (lyskom-get-string 'all-confs)
                            to)
			  (when filter
			    (funcall filter 'description)))

    (setq lyskom-last-review-by by)
    (setq lyskom-last-review-to to)
    (setq lyskom-last-review-num count)
    (setq lyskom-last-review-pmark nil)
    (setq lyskom-last-review-cmark nil)
    (setq lyskom-last-review-filter filter)
    (setq lyskom-last-review-saved-result-list nil)
    (setq lyskom-last-review-saved-by-list nil)
    (setq lyskom-last-review-saved-to-list nil)
    (setq lyskom-last-review-saved-result-size 0)
    (setq lyskom-last-review-saved-smallest nil)
    (setq lyskom-last-review-saved-largest nil)
    (setq lyskom-have-review 'lyskom-review-by-to)

    (condition-case arg
        (let ((list (lyskom-get-texts-by-to by to count 
					    nil nil 
					    filter)))
          (if list
              (lyskom-review-enter-read-info
               (lyskom-create-read-info
                'REVIEW
                nil
                (lyskom-review-get-priority)
                (lyskom-create-text-list list)
                nil t) t)
            (lyskom-insert-string 'no-such-text)))
      (lyskom-review-error (if arg
                               nil 
                             (lyskom-insert-string 'no-such-text))))))


(def-kom-command kom-unread-by-to (&optional count)
  "Mark the last N texts written by a particular author to some
conference as unread. With no author specified, review texts by all 
authors. With zero texts specified, review all text. With no conference
specified, review texts to all conferences. With a negative number of
texts, review the last N texts instead of the first (you can use
`kom-unread-first' instead)."
  (interactive "P")
  (lyskom-unread-by-to (or count
                           (lyskom-read-number
                            (lyskom-get-string 'unread-how-many) 1))))

(def-kom-command kom-unread-roots-by-to (&optional count)
  "Mark the last N texts, excluding comments, written by a
particular author to some conference as unread. With no author
specified, review texts by all authors. With zero texts
specified, review all texts. With no conference specified, review
texts to all conferences. With a negative number of texts, review
the last N texts instead of the first (you can use
`kom-unread-first-roots' instead)."
  (interactive "P")
  (lyskom-unread-by-to (or count
                           (lyskom-read-number
                            (lyskom-get-string 'unread-how-many-roots) 1))
		       'lyskom-review-filter-roots))
  

  

(defun lyskom-unread-by-to (count &optional filter)
  "Common function for kom-review-by-to and kom-review-first"
  (setq filter (or filter lyskom-default-review-filter))
  (let* ((info (progn (if (and (listp count)
                               (integerp (car count))
                               (null (cdr count)))
                          (setq count (car count)))
                      (cond ((zerop count) 
                             (setq count nil)
                             (lyskom-get-string 'everything))
                            ((> count 0)
                             (lyskom-format 'latest-n count))
                            ((< count 0)
                             (lyskom-format 'first-n
                                            (- count))))))
         (by (lyskom-read-conf-no 
	      `(unread-by-whom ,info ,(and filter (funcall filter 'description)))
	      '(pers) t nil t))
         (to (lyskom-read-conf-no 
	      `(unread-to-conf ,info ,(and filter (funcall filter 'description)))
	      '(all) t nil t)))

    (if (not (zerop to))
        (cache-del-conf-stat to))
    (if (not (zerop by)) 
        (cache-del-pers-stat by))

    (lyskom-format-insert 'unread-info-by-to
                          info
                          (if (zerop by)
                              (lyskom-get-string 'anybody)
                            by)
                          (if (zerop to)
                              (lyskom-get-string 'all-confs)
                            to)
			  (when filter
			    (funcall filter 'description)))

    (setq lyskom-last-unread-by by)
    (setq lyskom-last-unread-to to)
    (setq lyskom-last-unread-num count)
    (setq lyskom-last-unread-pmark nil)
    (setq lyskom-last-unread-cmark nil)
    (setq lyskom-last-unread-filter filter)
    (setq lyskom-last-unread-saved-result-list nil)
    (setq lyskom-last-unread-saved-by-list nil)
    (setq lyskom-last-unread-saved-to-list nil)
    (setq lyskom-last-unread-saved-result-size 0)
    (setq lyskom-last-unread-saved-smallest nil)
    (setq lyskom-last-unread-saved-largest nil)
    (setq lyskom-have-unread t)

    (condition-case arg
        (let ((list (lyskom-get-texts-by-to by to count 
					    nil t
					    filter)))
          (if list
              (lyskom-traverse text-no list
                (unless (lyskom-mark-unread text-no)
                  (lyskom-format-insert 'cant-mark-text-unread
                                        text-no
                                        (lyskom-get-error-text lyskom-errno))))
            (lyskom-insert-string 'no-such-text)))
      (lyskom-review-error (if arg
                               nil 
                             (lyskom-insert-string 'no-such-text))))))






;;; ================================================================
;;; lyskom-get-texts-by-to
;;; Author: David Byers
;;;
;;; Call lyskom-get-texts-by, lyskom-get-texts-to or 
;;; lyskom-get-texts-by-and-to to get NUM texts by person 
;;; BY to conference TO.
;;;


(defun lyskom-get-texts-by-to (by to num &optional again do-unread filter)
  "Get NUM texts written by person number BY in conference number TO

If optional argument AGAIN is non-nil, then repeat the last review.

If optional argument DO-UNREAD is non-nil, this call is for
marking texts unread, not reviewing them. If optional argument
FILTER is non-nil, then only accept those texts for which
FILTER (called with a text-no) returns non-nil.

Args: BY TO NUM AGAIN DO-UNREAD"
  (cond ((and (zerop by) 
              (zerop to)) (lyskom-get-texts-globally num again 
						     do-unread filter))
        ((zerop to) (lyskom-get-texts-by by num again nil filter))
        ((zerop by) (lyskom-get-texts-to to num again nil filter))
        ((and (eq by lyskom-pers-no)
              (not (eq to by))
              (let ((conf (blocking-do 'get-conf-stat to)))
                (and (conf-type->letterbox (conf-stat->conf-type conf))
                     (null (map->text-nos 
                            (blocking-do 'get-map
                                         (conf-stat->conf-no conf)
                                         (conf-stat->first-local-no conf)
                                         1))))))
         (lyskom-get-texts-by-generic 
          by num 
	  (lambda (x to filter)
	    (let ((found nil))
	      (and (or (null filter)
		       (funcall filter x))
		   (lyskom-traverse misc (text-stat->misc-info-list x)
		     (setq found 
			   (or found
			       (and (memq (misc-info->type misc)
					  lyskom-recpt-types-list)
				    (eq (misc-info->recipient-no misc) to))))))
	      found))
          (list to filter)
          again))
         (t (lyskom-get-texts-by-and-to by to num again nil nil filter))))


;;; ============================================================
;;; lyskom-check-review-access
;;; Author: David Byers
;;;
;;; Check that we can access the conference map. If we can't some
;;; review functions are just not much fun
;;;

(defun lyskom-check-review-access (conf pers)
  "Check that we can review texts to CONF by PERS.
CONF is a conf-stat or t if we know we can access that conference.
PERS is a pers-stat or t if we know we can access that person.
This function signals an error if review is impossible"
  (cond ((null conf) 
         (lyskom-format-insert 'review-conf-gone)
         (signal 'lyskom-cant-review-error t))
        ((null pers)
         (lyskom-format-insert 'review-pers-gone)
         (signal 'lyskom-cant-review-error t))
        ((lyskom-conf-stat-p conf)
         (cond ((= 0 (conf-stat->no-of-texts conf))
                (lyskom-format-insert 'review-cant-read-empty)
                (signal 'lyskom-cant-review-error t))
               ((null (map->text-nos 
                       (blocking-do 'get-map
                                    (conf-stat->conf-no conf)
                                    (conf-stat->first-local-no conf)
                                    1)))
                (if (conf-type->letterbox
                     (conf-stat->conf-type conf))
                    (lyskom-format-insert 'review-cant-read-letterbox)
                  (lyskom-format-insert 'review-cant-read-conf))
                (signal 'lyskom-cant-review-error t))))))



;;; ============================================================
;;; lyskom-get-texts-globally
;;; Author: Per Cederquist, David Byers
;;;

(defun lyskom-get-texts-globally (num &optional again do-unread filter)
  "Get the last NUM texts created in LysKOM. If AGAIN is non-nil, keep
going from where we were before."
  (cond ((and again (null num))
         (setq num lyskom-last-review-num))
        ((and again (< lyskom-last-review-num 0)) 
         (setq num (- num)))
        ((null num) 
         (lyskom-format-insert
          (if do-unread 'cant-unread-everything 'cant-review-everything))
         (signal 'lyskom-cant-review-error t)))

  (let ((result nil)
        (textno (cond (again lyskom-last-review-cmark)
                      ((< num 0) 1)
                      (t (lyskom-maxint))))
        (op (if (< num 0)
                'find-next-text-no
              'find-previous-text-no))
	(batch-size (abs num)))

    ;; Get the texts in batches of how many we have left to get
    ;; With the filter, this gets inefficient as we start getting
    ;; close to the number of texts we want. 

    (while (and (not (eq textno 0)) (not (null textno))
		(< (length result) (abs num)))

      ;; Inner loop to get a batch of texts

      (let (batch)
	(while (and (not (eq textno 0))
		    (not (null textno))
		    (< (length batch) batch-size))
	  (setq textno (blocking-do op textno))
	  (when textno (setq batch (cons textno batch))))
	  
	;; Now filter the results.

	(setq batch (lyskom-review-filter-results batch filter))

	;; We now have the batch (sometimes in reverse order), so we
	;; put it onto the front of the result list. It would probably
	;; be better to retain a reasonable batch size and save any
	;; results not used, but the benefit is not obvious, and the
	;; work required is...

	(setq result (nconc batch result))
	(setq batch-size (- (abs num) (length result)))))

    (setq lyskom-last-review-cmark textno)
    (if (< num 0)
        (nreverse result)
      result)))


;;; ================================================================
;;; lyskom-get-letters-to
;;; Author: David Byers
;;; 
;;; Get letters by self that have a specified letterbox as
;;; recipient. Do this by linearly searching selfs letterbox
;;; map and look as every single doggone text-stat. What a drag.
;;;
;;; +++ FIXME: This is just get-texts-by right now. Need to filter stuff.
;;;

(defun lyskom-get-letters-to (persno recipient num &optional again
				     pstart filter)
  "Get NUM texts written by PERSNO. Args: persno num

Cannot be called from a callback."
  (let ((persstat (blocking-do 'get-pers-stat persno)))
    (lyskom-check-review-access t persstat)

    (cond ((and again (null num)) (setq num lyskom-last-review-num))
          ((and again (< lyskom-last-review-num 0)) (setq num (- num))))

    (let* ((plow (or pstart (pers-stat->first-created-text persstat)))
           (phigh (1- (+ plow (pers-stat->no-of-created-texts persstat))))
           (result (if again
                       lyskom-last-review-saved-result-list
                     nil))
           (increment (if num (abs num)))
           (mark (cond (again lyskom-last-review-pmark)
                        ((and num (< num 0)) plow)
                        (t phigh)))
           (found nil)
           (start nil))
    

      (if (null num)
          (setq num (1+ phigh)
                mark phigh
                increment (1+ phigh)))

      (while (and (<= mark phigh)
                  (>= mark plow)
                  (> (abs num) (length result)))

        (setq increment (min lyskom-fetch-map-nos increment))
        (setq start (if (< num 0)
                        mark
                      (- mark (1- increment))))
        (if (< start 0)
            (progn
              (setq increment (- increment start))
              (setq start 0)))
      
        (setq found (lyskom-remove-zeroes
		     (listify-vector
		      (map->text-nos
		       (blocking-do 'get-created-texts
				    persno
				    start
				    increment)))))

        (let ((collector (make-collector)))
	  (lyskom-traverse x found
	    (initiate-get-text-stat 
	     'main 
	     (lambda (x collector pers-no pred)
	       (if (and x (or (null pred) (funcall pred x))
			(lyskom-is-recipient x pers-no))
		   (collector-push (text-stat->text-no x) collector)))
	     x collector recipient filter))

	  (lyskom-wait-queue 'main)
	  (setq found (nreverse (collector->value collector))))

        (if (> num 0)
            (setq result (nconc found result)
                  mark (- mark increment))
          (setq result (nconc result found)
                mark (+ mark increment)))
        (if (null found)
            (setq increment (min lyskom-fetch-map-nos (* increment 2)))
          (setq increment (- (abs num) (length result)))))

      (setq lyskom-last-review-pmark mark)

      (if (> num 0)
          (progn
            (setq lyskom-last-review-saved-result-list 
                  (lyskom-nfirst (- (length result) num) result))
            (nthcdr (max 0 (- (length result) num)) result))
        (progn
          (setq lyskom-last-review-saved-result-list
                (nthcdr (- num) result))
          (lyskom-nfirst (- num)  result))))))






;;; ================================================================
;;; lyskom-get-texts-by-and-to
;;; Author: David Byers
;;;
;;; Note: We can't assume that the conference's map of texts is
;;; sorted. If we could, it would be possible to simplify this
;;; function considerably without making it slower. 
;;;
;;; Problem: Construct part of the intersection between the user's
;;; created texts (the by-list) and the texts in a conference (the
;;; to-list) without bogging down the client, server or network and
;;; do it quickly!
;;;
;;; Idea: Construct the intersection incrementally without doing more
;;; work comparing things than we would if we had the full maps to
;;; start with.
;;;
;;; Solution: Get one segment of the by-list (call the nth segment
;;; by_n) and to-list (call the nth segment to_n) at a time until we
;;; are done or until both are exhausted. 
;;;
;;; In each iteration do the following: Calculate the intersection
;;; between to_n with each of the previous by_i leaving r_n:
;;;         r_n = \prod_{i=1}^{n-1} by_i \cap to_n
;;; where \prod denotes list concatenation. Next calculate the
;;; intersection of by_n with each to_i 0<i<n in turn, concatenating
;;; the result to the corresponding r_i:
;;;         r_i \larrow r_i * (by_n \cap to_i) ; 0<i<n
;;; At this point, the concatenation of all r_n, \prod r_n, is the
;;; result of the intersection between the segments of the by-list and
;;; to-list we have retrieved so far.
;;;
;;; If the total size of the result equals or exceeds the size we
;;; requested, finish the loop and return the results.
;;;
;;;
;;; Althogh the solution may seem a bit complicated, it's really not
;;; that bad once you think about how it's done. The good thing about
;;; it is that it does as little work as possible comparing elements.
;;; The bad part is that it constructs a little too many new cons
;;; cells, although reversing most of the lists does help.
;;;
;;; The following optimisations have been implemented:
;;;
;;; - Exploit the fact that the user's map is sorted by aborting the
;;; search if we have the full conference map and the lowest number we
;;; have from the user's map is lower than the lowest number in the
;;; conference map. Highest numbers apply when we are searching from
;;; the front.

;;; +++ FIXME: For users that have written very little in high-traffic
;;; conferences, and where the request cannot be fulfilled (if num is
;;; too high) this function ends up scanning the entire conference
;;; map. At some point it is probably faster to get all the text-stats
;;; in the user's map (when we've retrieved the map we know how many
;;; there are) and look at the recipients rather than calculate the
;;; intersection. 
;;;

(defun lyskom-get-texts-by-and-to (persno confno num 
                                          &optional again pstart cstart filter)
  "Get NUM texts written by person PERSNO with conference CONFNO as a
recipient. If optional AGAIN is non-nil, continue from where we were.
Args: persno confno num &optional again pstart cstart"
  (blocking-do-multiple ((persstat (get-pers-stat persno))
                         (confstat (get-conf-stat confno)))
    (cond

     ;;
     ;; Special case: reviewing to a letterbox or conference we're not
     ;; a member of and have no access to.
     ;;

     ((and (eq lyskom-pers-no persno)
           confstat
           (or (conf-type->letterbox (conf-stat->conf-type confstat))
               (and (conf-type->rd_prot (conf-stat->conf-type confstat))
                    (null (map->text-nos 
                           (blocking-do 'get-map
                                        (conf-stat->conf-no confstat)
                                        (conf-stat->first-local-no confstat)
                                        1)))))
           (not (eq persno confno)))
      (lyskom-get-letters-to persno confno num again pstart filter))

     ;;
     ;; General case
     ;;

     (t
      (lyskom-check-review-access confstat persstat)
      (cond ((and again (null num)) (setq num lyskom-last-review-num))
            ((and again (< lyskom-last-review-num 0) (setq num (- num)))))
      (let* ((result-list (if again
                              lyskom-last-review-saved-result-list
                            nil))
             (by-list (if again
                          lyskom-last-review-saved-by-list
                        nil))
             (to-list (if again 
                          lyskom-last-review-saved-to-list 
                        nil))
             (result-size (if again
                              lyskom-last-review-saved-result-size
                            0))
             (by nil)
             (to nil)
             (increment lyskom-fetch-map-nos)
             (plow (or pstart (pers-stat->first-created-text persstat)))
             (phigh (1- (+ plow (pers-stat->no-of-created-texts persstat))))
             (pmark (cond (again lyskom-last-review-pmark)
                          ((and num (< num 0)) plow)
                          (t phigh)))
             (clow (or cstart (conf-stat->first-local-no confstat)))
             (chigh (1- (+ clow (conf-stat->no-of-texts confstat))))
             (cmark (cond (again lyskom-last-review-cmark)
                          ((and num (< num 0)) clow)
                          (t chigh)))
             (smallest (if again lyskom-last-review-saved-smallest nil))
             (largest (if again lyskom-last-review-saved-largest nil))
             (abort-loop nil))

        (if (null num)
            (setq num (1+ phigh)))

        (while (and (or (and (<= pmark phigh)
                             (>= pmark plow))
                        (and (<= cmark chigh)
                             (>= cmark clow)))
                    (> (abs num) result-size)
                    (not abort-loop))

          (setq by (and (<= pmark phigh)
                        (>= pmark plow)
                        (lyskom-remove-zeroes
                         (listify-vector
                          (map->text-nos
                           (blocking-do 'get-created-texts
                                        (pers-stat->pers-no persstat)
                                        (if (< num 0)
                                            pmark
                                          (max 0 (- pmark (1- increment))))
                                        increment)))))
                to (and (<= cmark chigh)
                        (>= cmark clow)
                        (lyskom-remove-zeroes
                         (listify-vector
                          (map->text-nos
                           (blocking-do 'get-map
                                        (conf-stat->conf-no confstat)
                                        (if (< num 0)
                                            cmark
                                          (max 0 (- cmark (1- increment))))
                                        increment))))))

          (if (> num 0)
              (if (and smallest by
                       (> smallest (car by)))
                  (setq abort-loop t))
            (if (and largest by
                     (< largest (car (nthcdr (1- (length by)) by))))
                (setq abort-loop t)))

          ;;
          ;;    Add intersection between new TO and old BYs
          ;;    to the results list.
          ;;

          (setq result-list
                (cons (apply 'nconc
                             (mapcar 
			      (lambda (x)
				(lyskom-review-filter-results
				 (lyskom-intersection to x)
				 filter))
                              by-list))
                      result-list))

          ;;
          ;;    Add new BY and TO to the by-list and to-list
          ;;

          (setq by-list (cons by by-list)
                to-list (cons to to-list))
      

          ;;
          ;;    Add intersections between new BY and all TOs
          ;;

          (setq result-list
                (lyskom-mapcar2 (lambda (x y)
				  (lyskom-review-filter-results
				   (lyskom-intersection y
							(nconc x by))
				   filter))
                                result-list
                                to-list))

          (setq result-size (apply '+ (mapcar 'length result-list)))

          ;;
          ;;    Adjust the marks
          ;;

          (if (> num 0)
              (setq pmark (- pmark increment)
                    cmark (- cmark increment))
            (setq pmark (+ pmark increment)
                  cmark (+ cmark increment)))


          ;;
          ;;  If we have exhausted the conference, calculate smallest and
          ;;  largest
          ;;

          (if (and (null smallest)
                   (null largest)
                   (or (> cmark chigh)
                       (< cmark clow)))
              (setq smallest 
                    (apply 'min
                           (mapcar (lambda (x)
				     (if x
					 (apply 'min x)
				       (lyskom-maxint)))
                                   to-list))
                    largest
                    (apply 'max
                           (mapcar (lambda (x)
				     (if x
					 (apply 'max x)
				       -1))
                                   to-list))))


          ;;
          ;;    This is the end of the while loop
          ;;

          )

        (setq lyskom-last-review-pmark pmark)
        (setq lyskom-last-review-cmark cmark)
        (setq lyskom-last-review-saved-by-list by-list)
        (setq lyskom-last-review-saved-to-list to-list)
        (setq lyskom-last-review-saved-smallest smallest)
        (setq lyskom-last-review-saved-largest largest)

        ;;
        ;;  Extract results
        ;;

        (setq result-list
              (apply 'nconc (if (< num 0)
                                (nreverse result-list)
                              result-list)))

        ;;
        ;;  Save discarded results and return retained results
        ;;

        (if (> num 0)
            (progn
              (setq lyskom-last-review-saved-result-list
                    (lyskom-nfirst (- (length result-list) num) result-list))
              (setq lyskom-last-review-saved-result-size
                    (length  lyskom-last-review-saved-result-list))
              (setq lyskom-last-review-saved-result-list
                    (cons lyskom-last-review-saved-result-list
                          (make-list (- (length by-list) 1) nil)))
              (nthcdr (max 0 (- (length result-list) num)) result-list))

          (progn
            (setq lyskom-last-review-saved-result-list 
                  (nthcdr (- num) result-list))
            (setq lyskom-last-review-saved-result-size
                  (length lyskom-last-review-saved-result-list))
            (setq lyskom-last-review-saved-result-list
                  (cons lyskom-last-review-saved-result-list
                        (make-list (- (length by-list) 1) nil)))
            (lyskom-nfirst (- num) result-list))))))))



;;; ===============================================================
;;; lyskom-get-texts-by, lyskom-get-texts-to
;;; Author: David Byers
;;;
;;; These functions get data in chunks, starting with the number of
;;; texts requested. If they come up empty (which is common when
;;; scanning from the beginning of a map), the increment is
;;; exponentially increased up to a maximum of 150. 
;;;
;;; lyskom-get-texts-by is also careful to filter out those texts that
;;; are not readable, hence the added complexity in that function.
;;;

(defun lyskom-get-texts-by (persno num &optional again pstart filter)
  "Get NUM texts written by PERSNO. Args: persno num"
  (let* ((persstat (blocking-do 'get-pers-stat persno)))
    (lyskom-check-review-access t persstat)
    (lyskom-get-texts-by-generic persno num filter nil again pstart)))

(defun lyskom-get-texts-by-generic (persno num pred args 
                                           &optional again pstart)
  "Get NUM texts written by PERSNO. Args: persno num

Cannot be called from a callback."
  (let* ((persstat (blocking-do 'get-pers-stat persno))
         (user-area (pers-stat->user-area persstat)))

    (cond ((and again (null num)) (setq num lyskom-last-review-num))
          ((and again (< lyskom-last-review-num 0)) (setq num (- num))))

    (let* ((plow (or pstart (pers-stat->first-created-text persstat)))
           (phigh (1- (+ plow (pers-stat->no-of-created-texts persstat))))
           (result (if again
                       lyskom-last-review-saved-result-list
                     nil))
           (increment (if num (abs num)))
           (mark (cond (again lyskom-last-review-pmark)
                        ((and num (< num 0)) plow)
                        (t phigh)))
           (collector nil)
           (found nil)
           (start nil))
    

      (if (null num)
          (setq num (1+ phigh)
                mark phigh
                increment (1+ phigh)))

      (while (and (<= mark phigh)
                  (>= mark plow)
                  (> (abs num) (length result)))

        (setq increment (min lyskom-fetch-map-nos increment))
        (setq start (if (< num 0)
                        mark
                      (- mark (1- increment))))
        (if (< start 0)
            (progn
              (setq increment (- increment start))
              (setq start 0)))
      
        (setq found (delq user-area
			  (lyskom-remove-zeroes
			   (listify-vector
			    (map->text-nos
			     (blocking-do 'get-created-texts
					  persno
					  start
					  increment))))))

	(when pred
	  (let ((collector (make-collector)))
	    (lyskom-traverse x found
	      (initiate-get-text-stat 'main 
				      (lambda (x collector pred args)
					(when (and x (apply pred x args))
					  (collector-push 
					   (text-stat->text-no x)
					   collector)))
				      x collector pred args))

	    (lyskom-wait-queue 'main)
	    (setq found (nreverse (collector->value collector)))))

        (if (> num 0)
            (setq result (nconc found result)
                  mark (- mark increment))
          (setq result (nconc result found)
                mark (+ mark increment)))
        (if (null found)
            (setq increment (min lyskom-fetch-map-nos (* increment 2)))
          (setq increment (- (abs num) (length result)))))

      (setq lyskom-last-review-pmark mark)

      (if (> num 0)
          (progn
            (setq lyskom-last-review-saved-result-list 
                  (lyskom-nfirst (- (length result) num) result))
            (nthcdr (max 0 (- (length result) num)) result))
        (progn
          (setq lyskom-last-review-saved-result-list
                (nthcdr (- num) result))
          (lyskom-nfirst (- num)  result))))))

(defun lyskom-get-texts-to (confno num &optional again cstart filter)
  "From CONFNO get NUM texts.

If optional AGAIN is non-nil, repeat last review. If optional CSTART
is non-nil, then start from that local number. If optional FILTER
is non-nil, then only retrieve those texts for which FILTER (called 
with the text-stat) returns non-nil."
  (let ((confstat (blocking-do 'get-conf-stat confno)))

    (lyskom-check-review-access confstat t)

    (cond ((and again (null num)) (setq num lyskom-last-review-num))
          ((and again (< lyskom-last-review-num 0)) (setq num (- num))))

    (let* ((clow (or cstart (conf-stat->first-local-no confstat)))
           (chigh (1- (+ clow (conf-stat->no-of-texts confstat))))
           (result (if again 
                       lyskom-last-review-saved-result-list
                     nil))
           (start nil)
           (increment (and num (abs num)))
           (mark (cond (again lyskom-last-review-cmark)
                       ((and num (< num 0)) clow)
                       (t chigh))))

      (if (null num)
          (setq num (1+ chigh)
                increment (1+ chigh)
                mark chigh))

      (while (and (<= mark chigh)
                  (>= mark clow)
                  (> (abs num) (length result)))

        (setq increment (min lyskom-fetch-map-nos increment))
        (setq start (if (< num 0)
                        mark
                      (- mark (1- increment))))
        (if (< start 0)
            (progn
              (setq increment (- increment start))
              (setq start 0)))

        (let ((found (lyskom-remove-zeroes
                      (listify-vector
                       (map->text-nos
                        (blocking-do 'get-map
                                     confno
                                     start
                                     increment))))))
	  (when filter
	    (let ((collector (make-collector)))
	      (lyskom-traverse x found
		(initiate-get-text-stat 'main
					(lambda (x collector pred)
					  (when (and x (funcall pred x))
					    (collector-push
					     (text-stat->text-no x)
					     collector)))
					x collector filter))
	      (lyskom-wait-queue 'main)
	      (setq found (nreverse (collector->value collector)))))
					

          (if (> num 0)
              (setq result (nconc found result)
                    mark (- mark increment)
                    increment (- (abs num) (length result)))
            (setq result (nconc result found)
                  mark (+ mark increment)))
          (if (null found)
              (setq increment (min lyskom-fetch-map-nos (* increment 2)))
            (setq increment (- (abs num) (length result))))))

      (setq lyskom-last-review-cmark mark)

      (if (> num 0)
          (progn
            (setq lyskom-last-review-saved-result-list 
                  (lyskom-nfirst (- (length result) num) result))
            (nthcdr (max 0 (- (length result) num)) result))
        (progn
          (setq lyskom-last-review-saved-result-list
                (nthcdr (- num) result))
          (lyskom-nfirst (- num)  result))))))




;;; ============================================================
;;;                 Återse baklänges
;;;

(def-kom-command kom-review-backward ()
  "Toggles the reviewing order. If you are currently reviewing texts
oldest to newest, review newest to oldest instead. When reviewing
newest to oldest, change to oldest to newest.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive)
  (cond
   ((and (not (read-list-isempty lyskom-reading-list))
	 (memq (read-info->type (read-list->first lyskom-reading-list))
		 '(REVIEW REVIEW-MARK)))
    (let* ((info (read-list->first lyskom-reading-list))
	   (list (read-info->text-list info))
	   (texts (text-list->texts list))
	   (forward (read-info->forward info)))
      (set-text-list->texts list (nreverse texts))
      (set-read-info->forward info (not forward))
      (lyskom-format-insert 'you-review 
			    (lyskom-get-string (if (not forward)
						   'forward
						 'backward)))))
   (t
    (lyskom-insert-string 'illegal-command))))


;;; ================================================================
;;;                   ]terse tr{det - review tree

;;; Author: Linus Tolke


(def-kom-command kom-review-tree (&optional text-no)
  "Recursively review all comments to the selected text.
This command will descend recursively in the comment tree, as when
reading texts normally. Unlike when reading normally, filters are
not applied and circular structures are not dealt with gracefully.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg').

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-tree-q)))
  (lyskom-tell-internat 'kom-tell-review)
  (if text-no
      (progn
        (unless kom-review-uses-cache
          (cache-del-text-stat text-no))
        
        (let ((ts (blocking-do 'get-text-stat text-no)))
          (lyskom-follow-comments ts
                                  nil 'review
                                  (lyskom-review-get-priority)
                                  t)))
    (lyskom-insert-string 'read-text-first)))


(def-kom-command kom-unread-tree (text-no)
    "Recursively mark all comments to the selected text as unread.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'unread-tree-q)))
  (lyskom-unread-tree text-no nil))

(defun lyskom-unread-tree (text-no visited-list)
  "Perform the function of kom-unread-tree."
  (let* ((text-stat (blocking-do 'get-text-stat text-no))
         (worklist (and text-stat
                        (lyskom-text-comments text-stat))))
    (if (null (car worklist))
        (lyskom-format-insert 'no-such-text-no text-no)
      (while worklist
        (let ((cur (car worklist)))
          (setq worklist (cdr worklist))
          (unless (memq cur visited-list)
            (setq visited-list (cons cur visited-list)
                  cur (blocking-do 'get-text-stat cur))
            (when cur
              (when (delq nil (mapcar 'lyskom-get-membership
                                      (lyskom-text-recipients cur)))
                (lyskom-format-insert 'marking-text-unread
                                      (text-stat->text-no cur))
                (lyskom-report-command-answer
                 (lyskom-mark-unread (text-stat->text-no cur))))
              (lyskom-traverse text-no (lyskom-text-comments cur)
                (unless (or (memq text-no visited-list)
                            (memq text-no worklist)
                  (setq worklist (cons text-no worklist)))))))))))
  visited-list)



(def-kom-command kom-find-root (text-no)
  "Finds the root text of the tree containing the selected text.
When there is more than one root, all will be included in a review
operation.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg').

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive (list (lyskom-read-text-no-prefix-arg 'find-root-q)))
  (lyskom-tell-internat 'kom-tell-review)
  (cond
   (text-no
    (unless kom-review-uses-cache
      (cache-del-text-stat text-no))
    (let* ((ts (blocking-do 'get-text-stat text-no))
	   (r (lyskom-find-root ts t)))
      (cond ((> (length r) 1)
             (lyskom-format-insert-before-prompt
              (lyskom-get-string 'more-than-one-root) ts)
             (lyskom-review-enter-read-info
              (lyskom-create-read-info
               'REVIEW
               nil
               (lyskom-review-get-priority)
               (lyskom-create-text-list r)
               nil t) t))
            (r (unless kom-review-uses-cache
                 (cache-del-text-stat (car r)))
               (lyskom-view-text (car r)))
            (t (lyskom-insert-string 'could-not-find-root)))
      )
    )
   (t
    (lyskom-insert-string 'confusion-what-to-find-root))))

(def-kom-command kom-unread-root (text-no)
  "Finds the root text of the tree containing the selected text
and marks it as unread.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'unread-root-q)))
  (if text-no
      (let* ((ts (blocking-do 'get-text-stat text-no))
             (r (lyskom-find-root ts t)))
        (if r
            (lyskom-traverse text-no r
              (lyskom-format-insert 'marking-text-unread text-no)
              (lyskom-report-command-answer
               (lyskom-mark-unread text-no)))
          (lyskom-insert-string 'could-not-find-root)))
    (lyskom-insert-string 'confusion-what-to-unread-root)))


(def-kom-command kom-find-root-review (text-no)
  "Finds the root of the comment tree containing the selected texts
and then recursively reviews all its comments. For texts with a single
root, this is equivalent to doing `kom-find-root' followed by
`kom-review-tree'.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg').

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive (list (lyskom-read-text-no-prefix-arg 'find-root-review-q)))
  (lyskom-tell-internat 'kom-tell-review)
  (cond
   (text-no
    (unless kom-review-uses-cache
      (cache-del-text-stat text-no))
    (let* ((ts (blocking-do 'get-text-stat text-no))
           (start (lyskom-find-root ts t)))
      (cond ((> (length start) 1)
             (lyskom-format-insert-before-prompt
              (lyskom-get-string 'more-than-one-root-review) ts)
             (lyskom-review-tree (car start)))
            (start (lyskom-review-tree (car start)))
            (t (lyskom-insert-string 'could-not-find-root)))))
   (t (lyskom-insert-string 'confusion-what-to-find-root-review))))

(def-kom-command kom-unread-root-review (text-no)
  "Finds the root of the comment tree containing the selected texts
and then recursively marks all its comments as unread. For texts with
a single root, this is equivalent to doing `kom-find-root' followed by
`kom-unread-tree'.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'unread-root-review-q)))
  (if text-no
      (let* ((ts (blocking-do 'get-text-stat text-no))
             (start (lyskom-find-root ts t))
             (marked nil))
        (lyskom-traverse text-no start
          (setq marked (lyskom-unread-tree text-no marked))))
    (lyskom-insert-string 'confusion-what-to-unread-root-review)))


(defun lyskom-find-root (text-stat &optional all)
  "Finds the root text of the tree containing TEXT-STAT.
If optional ALL is non-nil, then return a list of all roots, otherwise
just a list of the firt root found."
  (lyskom-find-dag-roots 
   (lambda (text-stat)
     (delq nil
	   (mapcar (lambda (misc)
		     (cond ((eq (misc-info->type misc) 'COMM-TO) 
			    (misc-info->comm-to misc))
			   ((eq (misc-info->type misc) 'FOOTN-TO) 
			    (misc-info->footn-to misc))))
		   (text-stat->misc-info-list text-stat))))
   (lambda (text-no)
     (unless kom-review-uses-cache
       (cache-del-text-stat text-no))
     (or (cache-get-text-stat text-no)
         (blocking-do 'get-text-stat text-no)))
   (text-stat->text-no text-stat)
   (not all)))




(defun lyskom-review-tree (text)
  "Takes a TEXT as an arg, shows the text and the tree of all comments.
Does a lyskom-end-of-command.
Text is a text-no."
  (cond
   ((integerp text)
    (unless kom-review-uses-cache
      (cache-del-text-stat text))
    (lyskom-view-text text nil t nil (lyskom-review-get-priority) t))
   (t
    (signal 'lyskom-internal-error
	    (list 'lyskom-review-tree
		  "Called with incorrect argument."
		  text)))))


;;; ================================================================
;;;                     ]terse n{sta -  review next

;;; Author: Linus Tolke


(def-kom-command kom-review-next ()
  "Resumes an interupted review by moving all suspended review entries
to the front. This command may be less-than-useful depending on how
review priorities are set, and the setting of
`kom-higher-priority-breaks'.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive)
  (lyskom-tell-internat 'kom-tell-review)
  (let ((len (read-list-length lyskom-reading-list))
	(finished nil))
    (while (and (not finished)
		(> len 1))
      (let ((type (read-info->type (read-list->first lyskom-reading-list))))
	(if (and (not (eq type 'REVIEW))
		 (not (eq type 'REVIEW-TREE))
		 (not (eq type 'REVIEW-MARK)))
	    (read-list-rotate lyskom-reading-list)
	  (setq finished t)))
      (setq len (1- len)))))


;;; ================================================================
;;;                    ]terse stacken - Review stack

;;; Author: Linus


(def-kom-command kom-review-stack ()
  "Displays a list of suspended review commands, and possibly regular
read commands that have been suspended by reviewing.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive)
  (if (read-list->all-entries lyskom-reading-list)
      (mapcar
       (lambda (info)
	 (let ((un (text-list->length (read-info->text-list info)))
	       (type (read-info->type info))
	       (cto (read-info->comm-to info))
	       (conf (read-info->conf-stat info)))
	   (cond
	    ((eq type 'COMM-IN)
	     (lyskom-format-insert 'view-many-comments cto un))
	    ((eq type 'CONF)
	     (lyskom-format-insert 'view-texts-in-conf un conf))
	    ((eq type 'REVIEW)
	     (lyskom-format-insert 'review-n-texts un))
	    ((eq type 'REVIEW-TREE)
	     (lyskom-format-insert 'review-many-comments cto un))
	    ((eq type 'REVIEW-MARK)
	     (lyskom-format-insert 'review-marked un)))))
       (read-list->all-entries lyskom-reading-list))
    (cond (lyskom-current-conf 
           (lyskom-format-insert 'you-have-no-unreads lyskom-current-conf))
          (t (lyskom-insert 'not-reading-anywhere)))))



;;; ================================================================
;;;                      ]terse hoppa - review clear

;;; Author: Linus Tolke


(def-kom-command kom-review-clear ()
  "Cancel all active and suspended review commands.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive)
  (let ((found nil))
    (if (not (read-list-isempty lyskom-reading-list))
        (while (memq (read-info->type (read-list->first lyskom-reading-list))
                       lyskom-review-types-list)
          (set-read-list-del-first lyskom-reading-list)
          (setq found t)))
    (if (not (read-list-isempty lyskom-to-do-list))
        (while (memq (read-info->type (read-list->first lyskom-to-do-list))
                       lyskom-review-types-list)
          (set-read-list-del-first lyskom-to-do-list)
          (setq found t)))

    (unless found (lyskom-insert 'not-reviewing))))


;;; ================================================================
;;;          ]terse det kommenterade - View commented text

;;; Author: Inge Wallin


(def-kom-command kom-review-comments (text-no)
  "Review all comments to the selected text. This command only
reviews one level of comments. To see the entire comment tree, use
`kom-review-tree' instead.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg').

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-comments-q)))
  (lyskom-tell-internat 'kom-tell-review)
  (cond (text-no
         (unless kom-review-uses-cache
           (cache-del-text-stat text-no))
         (lyskom-review-comments
          (list (blocking-do 'get-text-stat text-no))))
        (t (lyskom-insert-string 'read-text-first))))

(def-kom-command kom-review-more-comments ()
  "Review all comments to texts recently viewed with `kom-review-comments'. 
This command only reviews one level of comments. To see the entire comment
tree, use `kom-review-tree' instead.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive)
  (lyskom-tell-internat 'kom-tell-review)
  (cond
   (lyskom-last-review-comments
    (unless kom-review-uses-cache
      (mapc 'cache-del-text-stat lyskom-last-review-comments))
    (lyskom-review-comments
     (let ((collector (make-collector)))
       (mapc (lambda (text-no)
                 (initiate-get-text-stat 'main
                                         'collector-push
                                         text-no
                                         collector))
               lyskom-last-review-comments)
       (lyskom-wait-queue 'main)
       (delq nil (nreverse (collector->value collector))))))
   (t (lyskom-insert-string 'no-review-done))))


(defun lyskom-review-comments (text-stat-list)
  "Handles the return from the initiate-get-text-stat, displays and builds list."
  (let ((text-nos nil))
    (lyskom-traverse text-stat text-stat-list
      (let* ((misc-info-list (and text-stat
                                  (text-stat->misc-info-list text-stat)))
             (misc-infos (and misc-info-list
                              (append (lyskom-misc-infos-from-list 
                                       'FOOTN-IN misc-info-list)
                                      (lyskom-misc-infos-from-list 
                                       'COMM-IN misc-info-list))))
             (all-text-nos (and misc-infos
                                (mapcar
				 (lambda (misc-info)
				   (if (equal (misc-info->type misc-info)
					      'COMM-IN)
				       (misc-info->comm-in misc-info)
				     (misc-info->footn-in misc-info)))
                                 misc-infos))))
        ;; Only try to review texts that we can read.
        (while all-text-nos
          (unless kom-review-uses-cache
            (cache-del-text-stat (car all-text-nos)))
          (if (blocking-do 'get-text-stat (car all-text-nos))
              (setq text-nos (cons (car all-text-nos) text-nos)))
          (setq all-text-nos (cdr all-text-nos)))))

    (setq text-nos (nreverse text-nos))
    (if text-nos
	(progn
	  (lyskom-format-insert 'review-text-no (car text-nos))
	  (when (cdr text-nos)
            (lyskom-review-enter-read-info
             (lyskom-create-read-info
              'REVIEW nil (lyskom-review-get-priority)
              (lyskom-create-text-list (cdr text-nos))
              lyskom-current-text) t))
          (setq lyskom-last-review-comments text-nos)
          ;; Don't check the no-cache thing here since we already
          ;; did earlier. We may end up slightly out of sync with
          ;; the server, but not so anyone will really notice.
	  (lyskom-view-text (car text-nos)))
      (lyskom-insert-string 'no-such-text))))


(def-kom-command kom-review-cross-references (text-no)
  "Review all texts cross-referenced from the selected text.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg').

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-xrefs-q)))
  (lyskom-tell-internat 'kom-tell-review)
  (cond (text-no
         (unless kom-review-uses-cache
	   (cache-del-text-stat text-no))
         (lyskom-review-cross-references
          (blocking-do 'get-text-stat text-no)))
        (t (lyskom-insert-string 'read-text-first))))

(defun lyskom-cross-referenced-text (aux-item-xref)
  (and (string-match "^T\\([0-9]+\\)" (aux-item->data aux-item-xref))
       (lyskom-string-to-number (match-string 1 (aux-item->data aux-item-xref)))))

(defun lyskom-review-cross-references (text-stat)
  "Handles the return from the initiate-get-text-stat,
displays and builds list."
  (let* ((all-xrefs (lyskom-get-aux-item (text-stat->aux-items text-stat) 3))
	 (only-txts (mapcar 'lyskom-cross-referenced-text all-xrefs))
	 (to-review '()))

    ;; Only try to review texts that we can read.
    (while only-txts
      (when (car only-txts)
	(unless kom-review-uses-cache
	  (cache-del-text-stat (car only-txts)))
	(when (blocking-do 'get-text-stat (car only-txts))
	  (setq to-review (cons (car only-txts) to-review))))
      (setq only-txts (cdr only-txts)))
    (setq to-review (nreverse to-review))

    (if to-review
	(progn
	  (lyskom-format-insert 'review-text-no (car to-review))
	  (when (cdr to-review)
	    (lyskom-review-enter-read-info
	     (lyskom-create-read-info
	      'REVIEW nil (lyskom-review-get-priority)
	      (lyskom-create-text-list (cdr to-review))
	      lyskom-current-text) t))
          ;; Don't check the no-cache thing here since we already
          ;; did earlier. We may end up slightly out of sync with
          ;; the server, but not so anyone will really notice.
	  (lyskom-view-text (car to-review)))
      (lyskom-insert-string 'no-such-text))))




(def-kom-command kom-unread-comments (text-no)
  "Mark all comments to the selected text text as unread. This
command only marks one level of comments as unread. To mark the 
entire comment tree unread, use `kom-unread-tree' instead.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'unread-comments-q)))
  (cond (text-no
         (lyskom-unread-comments
          (blocking-do 'get-text-stat text-no)))
        (t (lyskom-insert-string 'read-text-first))))


(defun lyskom-unread-comments (text-stat)
  "Handles the return from the initiate-get-text-stat, displays and builds list."
  (let* ((misc-info-list (and text-stat
			      (text-stat->misc-info-list text-stat)))
	 (misc-infos (and misc-info-list
			  (append (lyskom-misc-infos-from-list 
				   'FOOTN-IN misc-info-list)
				  (lyskom-misc-infos-from-list 
				   'COMM-IN misc-info-list))))
	 (all-text-nos (and misc-infos
                            (mapcar
                             (lambda (misc-info)
                               (if (equal (misc-info->type misc-info)
                                          'COMM-IN)
                                   (misc-info->comm-in misc-info)
                                 (misc-info->footn-in misc-info)))
                             misc-infos)))
         text-nos)
    ;; Only try to review texts that we can read.
    (while all-text-nos
      (if (blocking-do 'get-text-stat (car all-text-nos))
          (setq text-nos (cons (car all-text-nos) text-nos)))
      (setq all-text-nos (cdr all-text-nos)))
    (setq text-nos (nreverse text-nos))

    (if text-nos
        (lyskom-traverse text-no text-nos
          (lyskom-format-insert 'marking-text-unread text-no)
          (lyskom-report-command-answer
           (lyskom-mark-unread text-no)))
      (lyskom-insert-string 'no-such-text))))



;;; ================================================================
;;;          ]terse igen - kom-review-last-normally-read
;;;
;;; Author: Linus Tolke


(def-kom-command kom-review-last-normally-read (no)
  "Reviews the N texts most recently read using normal commands.
After reviewing a number of texts, this will show the N texts
read prior to that.

See `kom-review-uses-cache', `kom-review-priority' and
`kom-review-marks-texts-as-read' for information on settings that affect
all review-related functions."
  (interactive 
   (list 
    (lyskom-read-number (lyskom-get-string 'read-normally-read) 1)))
  (lyskom-tell-internat 'kom-tell-review)
  (let* ((text-nos (nreverse (lyskom-nfirst no lyskom-normally-read-texts))))
    (if text-nos
	(progn
	  (lyskom-format-insert 'review-text-no (car text-nos))
	  (if (cdr text-nos)
	      (lyskom-review-enter-read-info
               (lyskom-create-read-info
                'REVIEW nil (lyskom-review-get-priority)
                (lyskom-create-text-list (cdr text-nos))
                lyskom-current-text) t))
          (unless kom-review-uses-cache
            (cache-del-text-stat (car text-nos)))
	  (lyskom-view-text (car text-nos)))
      (lyskom-format-insert 'no-such-text))))


(def-kom-command kom-unread-last-normally-read (no)
  "Marks the N texts most recently read using normal commands unread.
After marking a number of texts unread, this will mark the N texts
read prior to that."
  (interactive 
   (list (lyskom-read-number (lyskom-get-string 'read-normally-read) 1)))
  (let* ((text-nos (nreverse (lyskom-nfirst no lyskom-normally-read-texts))))
    (if text-nos
        (lyskom-traverse text-no text-nos
          (lyskom-format-insert 'marking-text-unread text-no)
          (lyskom-report-command-answer
           (lyskom-mark-unread text-no)))
      (lyskom-format-insert 'no-such-text))))


;;; ============================================================
;;;        Återse omodifierat - Review a non-converted text

;;; Author: Linus Tolke
;;; Modified by: Johan Sundström

(def-kom-command kom-review-noconversion (text-no)
  "Displays the selected text without any conversions or filtering
\(i.e. no conversion of HTML, no automatic line breaks, all header
lines etc).

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-noconversion-q)))
  (if text-no
      (let ((lyskom-format-special nil)
            (kom-smileys nil)
	    (kom-show-footnotes-immediately nil)
            (kom-show-mx-date-as-local nil)
            (kom-print-relative-dates nil)
            (kom-print-seconds-in-time-strings t)
            (kom-autowrap nil))
          (unless kom-review-uses-cache
            (cache-del-text-stat text-no))
        (lyskom-view-text text-no))
    (lyskom-insert 'confusion-what-to-view)))

;;; ============================================================
;;;        Återse konverterat - Review a text converted

(def-kom-command kom-review-converted (text-no)
  "Displays the selected text with all conversions enabled, even if
some or all of them have are disabled by current settings.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-converted-q)))
  (if text-no
      (let ((kom-smileys t)
            (kom-autowrap t)
            (kom-format-html-authors '((t . t))))
        (unless kom-review-uses-cache
          (cache-del-text-stat text-no))
        (lyskom-view-text text-no))
    (lyskom-insert 'confusion-what-to-view)))

;;; ============================================================
;;;        Återse rot13 - Review a text rot13:ed

(def-kom-command kom-review-rot13 (text-no)
  "Displays the selected text rot13:ed.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'review-rot13-q)))
  (if text-no
      (unwind-protect
          (progn (add-hook 'kom-view-text-hook 'lyskom-filter-rot13)
                 (unless kom-review-uses-cache (cache-del-text-stat text-no))
                 (lyskom-view-text text-no))
        (remove-hook 'kom-view-text-hook 'lyskom-filter-rot13)
        (remove-hook 'kom-view-text-hook 'lyskom-filter-rot13 t))
    (lyskom-insert 'confusion-what-to-view)))

(defun lyskom-review-get-priority ()
  "Get the priority to use for reviewing texts."
  (or kom-review-priority (lyskom-get-current-priority)))

(defun lyskom-review-enter-read-info (read-info before)
  "Enter READ-INFO into lyskom-reading-list and lyskom-to-do-list."
  (read-list-enter-read-info read-info lyskom-reading-list before)
  (read-list-enter-read-info read-info lyskom-to-do-list before))


(defun lyskom-review-filter-roots (text-stat)
  "Return non-nil if TEXT-STATE is a comment"
  (cond ((lyskom-text-stat-p text-stat)
	 (not (lyskom-traverse misc (text-stat->misc-info-list text-stat)
		(when (memq (misc-info->type misc)
			    '(COMM-TO FOOTN-TO))
		  (lyskom-traverse-break t)))))
	((eq text-stat 'description)
	 (lyskom-get-string 'review-filter-roots))))

(defun lyskom-review-filter-results (results filter)
  "Filter review RESULTS according to FILTER"
  (if filter
      (let ((collector (make-collector)))
	(lyskom-traverse x results
	  (initiate-get-text-stat 'main
				  (lambda (x collector pred)
				    (when (and x (funcall pred x))
				      (collector-push
				       (text-stat->text-no x)
				       collector)))
				  x collector filter))
	(lyskom-wait-queue 'main)
	(nreverse (collector->value collector)))
    results))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: edit-text.el
;;;;
;;;; This file contains functions which lets the LysKOM user edit
;;;; a text in a window. It also defines a new mode - lyskom-edit-mode.
;;;;

;;;; ================================================================

;;; Set variables to make lyskom-edit-mode a minor mode. This
;;; simplifies some stuff a lot

(defvar lyskom-edit-mode nil
  "Mode variable for lyskom-edit-mode")
(make-variable-buffer-local 'lyskom-edit-mode)

(defvar lyskom-edit-sending-mode nil
  "Mode variable for lyskom-edit-sending-mode")
(make-variable-buffer-local 'lyskom-edit-sending-mode)

(defvar lyskom-edit-sent-mode nil
  "Mode variable for lyskom-edit-sent-mode")
(make-variable-buffer-local 'lyskom-edit-sent-mode)

(put 'lyskom-edit-mode 'permanent-local t)

(or (assq 'lyskom-edit-mode minor-mode-alist)
    (setq minor-mode-alist (cons '(lyskom-edit-mode " LysKOM Edit")
                                 minor-mode-alist)))

(or (assq 'lyskom-edit-sending-mode minor-mode-alist)
    (setq minor-mode-alist (cons '(lyskom-edit-sending-mode " LysKOM Sending")
                                 minor-mode-alist)))

(or (assq 'lyskom-edit-sent-mode minor-mode-alist)
    (setq minor-mode-alist (cons '(lyskom-edit-sent-mode " LysKOM Sent")
                                 minor-mode-alist)))

(or (assq 'lyskom-edit-mode minor-mode-map-alist)
    (setq minor-mode-map-alist
	  (cons (cons 'lyskom-edit-mode lyskom-edit-mode-map)
		minor-mode-map-alist)))

(or (assq 'lyskom-edit-sending-mode minor-mode-map-alist)
    (setq minor-mode-map-alist
	  (cons (cons 'lyskom-edit-sending-mode lyskom-edit-mode-map)
		minor-mode-map-alist)))

(or (assq 'lyskom-edit-sent-mode minor-mode-map-alist)
    (setq minor-mode-map-alist
	  (cons (cons 'lyskom-edit-sending-mode lyskom-edit-mode-map)
		minor-mode-map-alist)))



(defvar lyskom-edit-mode-name "LysKOM edit"
  "Name of the mode.")

(defvar lyskom-edit-text-sent nil
  "Non-nil when a text has been sent")

(defvar lyskom-is-dedicated-edit-window nil
  "Status variable for an edit-window.")

(defvar lyskom-edit-handler nil
  "Status variable for an edit-buffer.
See lyskom-edit-handler-data and lyskom-edit-handler-buffer.")

(defvar lyskom-edit-handler-data nil
  "Status variable for an edit-buffer.
See lyskom-edit-handler and lyskom-edit-handler-buffer.")

(defvar lyskom-edit-handler-buffer nil
  "Buffer to run the edit-handler in.
See lyskom-edit-handler and lyskom-edit-handler-data.")

(defvar lyskom-edit-return-to-configuration nil
  "Status variable for an edit-buffer.")

(def-komtype lyskom-text-headers
  ((subject :read-only t)
   (misc-info :read-only t)
   (aux-items :read-only t)))
   

;;; Error signaled by lyskom-edit-parse-headers
(put 'lyskom-edit-text-abort 'error-conditions
     '(error lyskom-error lyskom-edit-error lyskom-edit-text-abort))

(put 'lyskom-unknown-header 'error-conditions
     '(error lyskom-error lyskom-edit-error lyskom-unknown-header))

(put 'lyskom-no-subject 'error-conditions
     '(error lyskom-error lyskom-edit-error lyskom-no-subject))

(put 'lyskom-edit-error 'error-conditions
     '(error lyskom-error lyskom-edit-error))

(defun lyskom-edit-text (proc misc-list subject body
			      &optional handler &rest data)
  "Edit a text in a new buffer.
PROC is the associated process.
MISC-LIST is the default misc-list.
SUBJECT is subject (a string).
BODY is the default text-body (a string, normally empty.)
HANDLER is a function to call when the text has been created.
DATA is extra data to send to the function. HANDLER is called with
	(apply HANDLER text-no DATA)
where text-no is the number of the text.
Does lyskom-end-of-command."
  (setq lyskom-is-writing t)
  (lyskom-dispatch-edit-text proc misc-list subject body
                             handler data))


(defun lyskom-dispatch-edit-text (proc misc-list subject body
				       &optional handler &rest data)
  "Same as lyskom-edit-text except that it doesn't set lyskom-is-writing."
  (let* ((saved-buffer lyskom-buffer)
         (buffer (lyskom-get-buffer-create 'write-texts
                                           (concat 
                                            (buffer-name (process-buffer proc))
                                            "-edit")))
         (config (current-window-configuration)))

    (lyskom-display-buffer buffer)
    (text-mode)
    (lyskom-ignore-errors
      (run-hooks 'lyskom-edit-mode-mode-hook))
    (lyskom-edit-mode)
    (make-local-variable 'lyskom-edit-handler)
    (make-local-variable 'lyskom-edit-handler-data)
    (make-local-variable 'lyskom-edit-handler-buffer)
    (make-local-variable 'lyskom-edit-return-to-configuration)
    (setq lyskom-edit-handler handler)
    (setq lyskom-edit-handler-data data)
    (setq lyskom-edit-handler-buffer saved-buffer)
    (setq lyskom-edit-return-to-configuration config)
    (buffer-disable-undo)
    (lyskom-edit-insert-miscs misc-list subject body)
    (buffer-enable-undo)
    (goto-char (point-min))
    (re-search-forward (regexp-quote (lyskom-get-string
				      'header-subject))
		       (point-max)
		       'end)
    (if (not (looking-at "\\s-*$"))
        (goto-char (point-max)))
    (lyskom-message "%s" (lyskom-get-string 'press-C-c-C-c)))
  (set-buffer lyskom-buffer)
  )


(defun lyskom-edit-find-separator (&optional move-point)
  "Return the end position of the separator line.
If MOVE-POINT is non-nil, move point to the start of the separator."
  (prog1
      (save-excursion
        (goto-char (point-min))
        (and (re-search-forward
              (concat "^"
                      (regexp-quote
                       (substitute-command-keys
                        (lyskom-get-string 'header-separator)))
                      "$")
              nil t)))
    (when (and move-point (match-beginning 0))
      (goto-char (match-beginning 0)))))


(defun lyskom-edit-insert-miscs (misc-list subject body &optional aux-list)
  "Insert MISC-LIST into header of text.
RECPT		-> Mottagare: <%d> %s.
CC-RECPT	-> Extra kopia: <%d> %s.
BCC-RECPT       -> Blind kopia: <%d> %s.
COMM-TO		-> Kommentar till text %d.
FOOTN-TO	-> Fotnot till text %d.
nil             -> Ingenting."
  (let ((edit-buffer (current-buffer))
	(where-put-misc (point-min-marker))
	(main-buffer lyskom-buffer))
    (set-buffer main-buffer)
    (setq misc-list (cdr misc-list))
    (while (not (null misc-list))
      (let ((key (car (car misc-list)))
	    (data (cdr (car misc-list))))
	(cond
	 ((eq key 'RECPT)
	  (lyskom-edit-insert-misc-conf (blocking-do 'get-conf-stat data)
					(lyskom-get-string 'recipient)
					where-put-misc data))
	 ((eq key 'CC-RECPT)
	  (lyskom-edit-insert-misc-conf (blocking-do 'get-conf-stat data)
					(lyskom-get-string 'carbon-copy)
					where-put-misc data))
	 ((eq key 'BCC-RECPT)
	  (lyskom-edit-insert-misc-conf (blocking-do 'get-conf-stat data)
					(lyskom-get-string 'blank-carbon-copy)
					where-put-misc data))
	 ((eq key 'COMM-TO)
	  (lyskom-edit-get-commented-author (blocking-do 'get-text-stat data)
					    (lyskom-get-string 'comment)
					    where-put-misc data))
	 ((eq key 'FOOTN-TO)
	  (lyskom-edit-get-commented-author (blocking-do 'get-text-stat data)
					    (lyskom-get-string 'footnote)
					    where-put-misc data)))
	(setq misc-list (cdr misc-list))))
    (mapc (lambda (item)
            (let ((data (lyskom-aux-item-call
                         item '(edit-insert print)
                         item lyskom-pers-no)))
              (when data
                (lyskom-princ
                 (lyskom-format "%#1@%[%#3s%] %#2s\n" 
                                (lyskom-default-button 'aux-edit-menu 
                                                       (cons edit-buffer
                                                             (copy-marker where-put-misc)))
                                data
                                (lyskom-get-string 'aux-item-prefix))
                 where-put-misc))))
          aux-list)
    (unless kom-edit-hide-add-button
      (lyskom-princ (lyskom-format "%[%#1@%#2s%]\n"
                                   (lyskom-default-button 'add-recipient-or-xref
                                                          edit-buffer)
                                   (lyskom-get-string 'add-recpt-button-text))
                    where-put-misc))
    (lyskom-princ (lyskom-format 'text-mass subject 
				 (substitute-command-keys
				  (lyskom-get-string 'header-separator))
				 body 
				 (lyskom-get-string 'header-subject)
                                 '(read-only t)
                                 '(read-only t front-sticky nil start-open t rear-nonsticky nil end-open nil)
                                 '(read-only t rear-nonsticky t end-open t front-sticky t start-open nil)
                                 )
		  where-put-misc)
    (set-buffer edit-buffer)
    (goto-char where-put-misc)
    (set-marker where-put-misc nil)))


(defun lyskom-edit-insert-misc-conf (conf-stat string stream number)
  "Insert Recipient:, Carbon copy: or Blind Carbon copy: in
edit-buffer.
CONF-STAT is the conf-stat of the conference that is about to be put in,
STRING is the string that is inserted.
STREAM is the buffer or a marker telling the position.
NUMBER is the number of the person. Used if the conf-stat is nil."
  (lyskom-princ
   (lyskom-format "%[%#1@%#2s%]: <%#3m> %#4M\n" 
                  (lyskom-default-button
                   'recpt-type
                   (list (or (conf-stat->conf-no conf-stat) number)
                         (marker-buffer stream))
                   (list (lyskom-get-string 'recpt-type-popup-title)
                         string))
                  string
                  (or conf-stat number)
                  (or conf-stat ""))
   stream))

(defun lyskom-edit-get-commented-author (text-stat string stream number)
  (if text-stat
      (let ((mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 17)))
            (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 16))))
        (lyskom-edit-insert-commented-author 
         (if (or mx-from mx-author)
             (lyskom-format-mx-author mx-from mx-author)
           (blocking-do 'get-conf-stat (text-stat->author text-stat)))
         string stream number))
    (lyskom-edit-insert-commented-author nil string stream number)))


(defun lyskom-edit-insert-commented-author (conf-stat string stream number)
  (lyskom-princ (lyskom-format 'comment-to-by
                               string
                               number
                               (if conf-stat
                                   (lyskom-format 'by conf-stat)
                                 ""))
                stream))




(defun lyskom-create-misc-list (&rest misc-pairs)
  "Create a misc-list.
Note that a misc-list is very different from a misc-info-list.
A misc-list is used when creating a new text, and sent to the server.
A misc-info-list is what is received from the server. Although the server
has the same format for the two things, this client uses two quite
different formats.

The arguments to this function is any number of pairs of data. The first
item in each pair should be one of RECPT, CC-RECPT, COMM-TO or FOOTN-TO.
The second item should be the corresponding conf- or text-no.

The result is a list of dotted pairs:
	('RECPT . conf-no)
	('CC-RECPT . conf-no)
	('BCC-RECPT . conf-no)
	('COMM-TO . text-no)
	('FOOTN-TO . text-no).
First element is a type-tag."
   (let ((result (cons 'MISC-LIST nil)))
    (while (not (null misc-pairs))
      (setq result (cons (cons (car misc-pairs)
				(car (cdr misc-pairs)))
			  result))
      (setq misc-pairs (cdr (cdr misc-pairs))))
    (nreverse result)))


;;; ================================================================
;;;                   lyskom-edit-mode

(defvar lyskom-edit-mode-hook nil
  "*List of functions to be called when entering lyskom-edit-mode.
Watch out! None of these functions are allowed to do kill-all-local-variables
because kom-edit-send and other functions depend on some variables to be able
to enter the text in the correct lyskom-process.")

(defvar lyskom-edit-mode-mode-hook nil
  "*List of functions to be called when entering lyskom-edit-mode.
Watch out! None of these functions are allowed to do kill-all-local-variables
because kom-edit-send and other functions depend on some variables to be able
to enter the text in the correct lyskom-process.

This one differs from lyskom-edit-mode-hook in that it is called before
the lyskom-special key bindings are added.")


(defun lyskom-edit-mode (&optional arg)
  "\\<lyskom-edit-mode-map>Mode for editing texts for LysKOM.
Commands:
\\[kom-edit-send]   sends the text when you are ready. The buffer will be
	  deleted if (and only if) the server accepts the text.
\\[kom-edit-quit]   aborts the editing. You will get back to the LysKOM buffer.

\\[kom-edit-show-commented]   shows the commented text in a temporary buffer.

\\[kom-edit-add-recipient]   asks for another recipient and adds him to the header.
\\[kom-edit-add-copy]   as \\[kom-edit-add-recipient] but adds him as copy-recipient.
\\[kom-edit-add-cross-reference]   asks for what to refer to and adds a cross reference to it.

\\[kom-edit-insert-commented]   inserts the commented or footnoted text.
\\[kom-edit-insert-text]   inserts the shown text, you tell the number.
\\[kom-edit-insert-link]   asks for what to link to and inserts a link to it.

Even though this is a minor mode, it's not intended to be turned on and off,
so it's not as clean as it ought to be."
  (interactive "P")
  (setq lyskom-edit-mode
        (if (null arg)
            (not lyskom-edit-mode)
          (> (prefix-numeric-value arg) 0)))

  (lyskom-set-menus 'lyskom-edit-mode lyskom-edit-mode-map)

  (when lyskom-edit-mode
    (lyskom-edit-sending-mode 0)
    (lyskom-edit-sent-mode 0)
    (auto-fill-mode 1)
    (auto-save-mode 1)
    (when (not (local-variable-p 'lyskom-edit-text-sent (current-buffer)))
      (make-local-variable 'lyskom-edit-text-sent)
      (setq lyskom-edit-text-sent nil))
    (make-local-variable 'paragraph-start)
    (make-local-variable 'paragraph-separate)
    (setq paragraph-start (concat "^" 
                                  (regexp-quote 
                                   (substitute-command-keys
                                    (lyskom-get-string 'header-separator)))
                                  "$\\|" paragraph-start))
    (setq paragraph-separate (concat "^" 
                                     (regexp-quote 
                                      (substitute-command-keys
                                       (lyskom-get-string 'header-separator)))
                                     "$\\|" paragraph-separate))
    (make-local-variable 'comment-start)
    (make-local-variable 'comment-end)
    (setq comment-start kom-cite-string
	  comment-end "")
    (run-hooks 'lyskom-edit-mode-hook)))

(defun lyskom-edit-sending-mode (arg)
  (interactive "P")
  (setq lyskom-edit-sending-mode 
        (if (null arg)
            (not lyskom-edit-sending-mode)
          (> (prefix-numeric-value arg) 0)))

  (when lyskom-edit-sending-mode
    (lyskom-edit-mode 0)
    (lyskom-edit-sent-mode 0)))

(defun lyskom-edit-sent-mode (arg)
  (interactive "P")
  (setq lyskom-edit-sent-mode 
        (if (null arg)
            (not lyskom-edit-sent-mode)
          (> (prefix-numeric-value arg) 0)))

  (when lyskom-edit-sent-mode
    (lyskom-edit-sending-mode 0)
    (lyskom-edit-mode 0)))


;;; ================================================================
;;;   Functions bound to keyboard seqences in lyskom-edit-mode
;;;

(defvar lyskom-edit-aux-list)
(defvar lyskom-edit-misc-list)
(defvar lyskom-edit-subject)
(defvar lyskom-edit-text)

(defun kom-edit-send-anonymous ()
  "Send the text anonymously to the server. Be aware that although the text 
will be truly anonymous, it is easy to slip up in such a way that the author
is evident anyway."
  (interactive)
  (lyskom-edit-send 'initiate-create-anonymous-text t))

(defun kom-edit-send ()
  "Send the text to the server. This command will attempt to send the text
to the server. If something goes wrong, a prompt will be shown allowing you
to edit the message and try to send it again."
  (interactive)
   (if (and (lyskom-default-value 'lyskom-is-anonymous)
            (lyskom-j-or-n-p 'do-send-anonymous))
       (lyskom-edit-send 'initiate-create-anonymous-text t)
     (lyskom-edit-send 'initiate-create-text nil)))

(defun lyskom-edit-send (send-function &optional is-anonymous)
  "Send the text to the server by calling SEND-FUNCTION.
If optional IS-ANONYMOUS is non-nil, assume that the text is being submitted
anonymously and take actions to avoid revealing the sender.

This runs `kom-send-text-hook' and (for backwards compatibility)
`lyskom-send-text-hook'."
  (condition-case err
      (if (or (not lyskom-edit-text-sent)
	      (j-or-n-p (lyskom-get-string 'already-sent)))
	  (progn 
	    (let ((buffer (current-buffer))
		  (handler lyskom-edit-handler)
		  (handler-data lyskom-edit-handler-data)
		  (handler-buffer lyskom-edit-handler-buffer)
		  (headers nil)
                  (misc-list nil)
                  (subject nil)
		  (subject-start-pos nil)
		  (body-start-pos nil)
                  (message nil)
                  (aux-list nil))

              ;;
              ;; Run user hooks
              ;; ####: ++++: FIXME: We should quit more graciously.

              (if (not (run-hook-with-args-until-failure 'kom-send-text-hook))
                  (signal 'lyskom-edit-text-abort nil))


	      (save-excursion
		(setq headers (lyskom-edit-parse-headers)
		      misc-list (apply 'lyskom-create-misc-list 
                                       (lyskom-text-headers->misc-info headers))
                      aux-list (lyskom-text-headers->aux-items headers)
		      subject (lyskom-text-headers->subject headers)))

              ;;
              ;; Check that there is a subject
              ;;

	      (if (or (null subject)
                      (string= subject ""))
		  (let ((old (point)))
		    (goto-char (point-min))
		    (re-search-forward (lyskom-get-string 'header-subject)
				       nil t)
		    (end-of-line)
		    (if (/= (point) old)
			(signal 'lyskom-no-subject '(enter-subject-idi)))))

              ;;
              ;; Check the recipients
              ;;

              (let ((extra-headers
                     (lyskom-edit-send-check-recipients misc-list
                                                        subject)))
                (if extra-headers
                    (setq misc-list
			  (apply 'lyskom-create-misc-list
				 (nconc (lyskom-text-headers->misc-info headers)
					extra-headers)))))

              ;;
              ;; Check that we don't add ourselves to an anon text
              ;;

              (when (and is-anonymous
                         (rassq lyskom-pers-no (cdr misc-list))
                         (lyskom-j-or-n-p 
                          (lyskom-get-string 'remove-self-sending-anonymous)))
                (rplacd misc-list (delq (rassq lyskom-pers-no (cdr misc-list))
                                        (cdr misc-list))))


              ;;
              ;; Transform the message text
              ;;

              (let ((lyskom-edit-aux-list aux-list)
                    (lyskom-edit-misc-list misc-list)
                    (lyskom-edit-subject subject)
                    (lyskom-edit-text (lyskom-edit-extract-text)))
                (run-hook-with-args-until-success
                 'lyskom-send-text-transform-hook)
                (setq aux-list lyskom-edit-aux-list)
                (setq misc-list lyskom-edit-misc-list)
                (setq subject lyskom-edit-subject)
                (setq message lyskom-edit-text))

	      (save-excursion
                (let* ((full-message
                        (cond ((and lyskom-allow-missing-subject
                                    (or (null subject)
                                        (string= subject ""))
                                    (not (string-match ".*\n" message)))
                               message)
                              (t (concat (or subject "") "\n" message))))
                       (content-type (lyskom-get-aux-item aux-list 1))
                       (charset (and content-type
                                     (lyskom-mime-content-type-get
                                      (lyskom-mime-decode-content-type
                                           (aux-item->data (car content-type)))
                                      'charset)))
		       (mime-charset (with-temp-buffer
				       (insert full-message)
                                       (when (fboundp 'latin-unity-remap-region)
                                         (lyskom-unity-text (point-min) (point-max)))
				       (lyskom-mime-charset-for-text (point-min) (point-max)))))

                  ;; If the charset isn't already set, encode the string

                  (if (and mime-charset (null charset))
                      (setq full-message
                            (lyskom-mime-encode-string full-message mime-charset))
		    (lyskom-error (lyskom-get-string 'too-many-languages-error)))

                  ;; Add the charset data to the content type

                  (cond ((> (length content-type) 1)
                         (lyskom-error (lyskom-get-string 'too-many-content-types)))
                        ((null content-type)
                         (setq aux-list
                               (cons (lyskom-create-aux-item 0 1 nil nil
                                                             (lyskom-create-aux-item-flags nil nil nil nil nil nil nil nil)
                                                             0
                                                             (format "text/x-kom-basic;charset=%S" mime-charset))
                                     aux-list)))
                        ((null charset)
                         (set-aux-item->data (car content-type)
                                             (format "%s;charset=%S"
                                                     (aux-item->data (car content-type))
                                                     mime-charset))))

                  ;; Send the text

                  (lyskom-edit-sending-mode 1)
                  (set-buffer lyskom-buffer)
                  ;; Don't change the prompt if we won't see our own text
                  (if (and kom-created-texts-are-read
                           (not is-anonymous))
                      (setq lyskom-dont-change-prompt t))
                  (setq lyskom-is-writing nil)
                  (lyskom-tell-internat 'kom-tell-send)
                  (run-hook-with-args 'kom-create-text-hook
                              full-message
                              misc-list
                              (if (not is-anonymous)
                                  (cons (lyskom-create-aux-item
                                         0 15 0 0
                                         (lyskom-create-aux-item-flags
                                          nil nil nil nil nil nil nil nil)
                                         0 (concat "lyskom.el "
                                                   lyskom-clientversion))
                                        aux-list)
                                aux-list)
                              buffer
                              is-anonymous)

                  (funcall send-function
                           'sending
                           'lyskom-create-text-handler
                           full-message
                           misc-list
                           (if (not is-anonymous)
                               (cons (lyskom-create-aux-item
                                      0 15 0 0
                                      (lyskom-create-aux-item-flags
                                       nil nil nil nil nil nil nil nil)
                                      0 (concat "lyskom.el "
                                                lyskom-clientversion))
                                     aux-list)
                             aux-list)
                           buffer
                           is-anonymous
			   handler
			   handler-data
                           handler-buffer))))
            (lyskom-undisplay-buffer)
	    (goto-char (point-max))))
    ;;
    ;; Catch no-subject and other things
    ;;

    (lyskom-edit-text-abort
     (and (cdr err) (apply 'lyskom-message (cdr err))))
    (lyskom-no-subject
     (lyskom-beep kom-ding-on-no-subject)
     (if (cdr-safe (cdr-safe err))
	 (goto-char (car-safe (cdr-safe (cdr-safe err)))))
     (lyskom-message "%s" (lyskom-get-string (car (cdr err)))))
    (lyskom-unknown-header
     (lyskom-message "%s" (lyskom-get-string (car (cdr err)))))))

(eval-when-compile (defvar ispell-dictionary nil))
(eval-when-compile (defvar ispell-message-text-end nil))
(eval-when-compile (defvar ispell-message-start-skip nil))
(eval-when-compile (defvar ispell-message-end-skip nil))


;;USER-HOOK: lyskom-ispell-text
(defun lyskom-ispell-text ()
  "Check spelling of the text body.
Put this in kom-send-text-hook"
  (kom-edit-ispell-message)
  t)


(eval-when-compile
  (defvar ispell-dictionary nil)
  (defvar ispell-message-text-end nil)
  (defvar ispell-message-start-skip nil)
  (defvar ispell-message-end-skip nil))

(defalias 'kom-ispell-message 'kom-edit-ispell-message)
(defun kom-edit-ispell-message ()
  "Check spelling of the text. Spelling is checked using ispell
and the dictionary indicated by `kom-ispell-dictionary'. If you
want to check the spelling of every message before sending it,
read the documentation for `lyskom-ispell-text' and 
`kom-send-text-hook'"
  (interactive)
  (require 'ispell)
  (let ((ispell-dictionary (or kom-ispell-dictionary ispell-dictionary))
        (kill-ispell (or (not (boundp 'ispell-dictionary))
                         (not (lyskom-string= kom-ispell-dictionary
                                       ispell-dictionary))))
        (result nil))
    (when kill-ispell (ispell-kill-ispell t))

    ;; Checking code

    (save-excursion
      (goto-char (point-min))
      (let* ((internal-messagep (lyskom-edit-find-separator t))
             (limit 
              (copy-marker
               (cond ((not ispell-message-text-end) (point-max))
                     ((char-or-string-p ispell-message-text-end)
                      (if (re-search-forward ispell-message-text-end nil t)
                          (match-beginning 0)
                        (point-max)))
                     (t (min (point-max) (funcall ispell-message-text-end))))))
             (cite-regexp 
              (regexp-quote
               (lyskom-default-value 'kom-cite-string)))
             (cite-regexp-start (concat "^[ \t]*$\\|" cite-regexp))
             (cite-regexp-end (concat "^\\(" cite-regexp "\\)"))
             (old-case-fold-search case-fold-search)
             (case-fold-search t)
             (ispell-checking-message t)
             (subject-string 
              (concat "^" (regexp-quote (lyskom-get-string 'subject)))))
        (lyskom-ignore ispell-checking-message)
        (goto-char (point-min))
        (while (if internal-messagep
                   (< (point) internal-messagep)
                 (not (eobp)))
          (if (lyskom-looking-at subject-string)
              (progn (goto-char (match-end 0))
                     (let ((case-fold-search old-case-fold-search))
                       (ispell-region (point)
                                      (progn
                                        (end-of-line)
                                        (point)))))
            (forward-line 1)))

        (while (< (point) limit)
          (while (and (lyskom-looking-at cite-regexp-start)
                      (< (point) limit)
                      (zerop (forward-line 1))))

          (if (< (point) limit)
              (let* ((start (point))
                     (end-c (and (re-search-forward cite-regexp-end limit 'end)
                                 (match-beginning 0)))
                     (end-fwd (and (goto-char start)
                                   (boundp 'ispell-message-start-skip)
                                   (stringp ispell-message-start-skip)
                                   (re-search-forward ispell-message-start-skip
                                                      limit 'end)))
                     (end (or (and end-c end-fwd (min end-c end-fwd))
                              end-c 
                              end-fwd
                              (marker-position limit))))
                (goto-char start)
                (setq result (ispell-region start end))
                  (if (and end-fwd (= end end-fwd))
                      (progn (goto-char end)
                             (re-search-forward ispell-message-end-skip 
                                                limit 'end))
                    (goto-char end)))))
          (set-marker limit nil)
          result))

    (when kill-ispell (ispell-kill-ispell t))
    result))

(defun lyskom-is-permitted-author (conf-stat)
  (and conf-stat
       (or (eq 0 (conf-stat->permitted-submitters conf-stat))
           (lyskom-is-supervisor (conf-stat->conf-no conf-stat)
                                 lyskom-pers-no))))


(defun lyskom-edit-send-check-recipients (misc-list subject) 
  "Check that the recipients of this text are OK. Ask the user to
confirm multiple recipients; check that the author of the commented
text is a member of some recipient of this text.

Cannot be called from a callback."
  (let* ((comm-to-list nil)
         (recipient-list nil)
         (text-stat nil)
         (collector (make-collector))
         (extra-headers nil)
         (buffer (current-buffer))
         (me (lyskom-with-lyskom-buffer lyskom-pers-no))
         (num-me 0)
         (num-real-recpt 0))
    (lyskom-ignore text-stat)       ; Have no idea if its ever used...

    ;;
    ;; List all texts this text is a comment to
    ;; List all recipients of the text
    ;;

    (lyskom-traverse misc (cdr misc-list)
      (cond ((eq (car misc) 'COMM-TO)
             (setq comm-to-list (cons (cdr misc)
                                      comm-to-list)))
            ((memq (car misc) lyskom-recpt-types-list)
             (when (eq (car misc) 'RECPT)
               (setq num-real-recpt (1+ num-real-recpt))
               (when (eq (cdr misc) me) (setq num-me (1+ num-me))))
             (setq recipient-list (cons (cdr misc) recipient-list)))))

    ;;
    ;; Check that there are recipients
    ;;

    (when (null recipient-list)
      (lyskom-error "%s" (lyskom-format 'no-recipients)))

    ;;
    ;; Check for duplicate recipients
    ;;

    (let ((tmp recipient-list))
      (while tmp
        (when (memq (car tmp) (cdr tmp))
          (lyskom-error "%s" (lyskom-format 'duplicate-recipients (car tmp))))
        (setq tmp (cdr tmp))))

    ;;
    ;; Check for new comments
    ;;

    (when (lyskom-with-lyskom-buffer
            (cond ((null kom-check-for-new-comments) nil)
                  ((functionp kom-check-for-new-comments)
                   (funcall kom-check-for-new-comments
                            buffer misc-list subject))
                  (t t)))
      (lyskom-message "%s" (lyskom-format 'checking-comments))
      (lyskom-with-lyskom-buffer
        (set-collector->value collector nil)

        (mapc (lambda (text-stat)
                (cache-del-text-stat text-stat)
                (initiate-get-text-stat 'sending 
                                        'collector-push
                                        text-stat
                                        collector))
                comm-to-list)
        (lyskom-wait-queue 'sending)

	(lyskom-traverse
            text-stat (collector->value collector)
          (when text-stat
            (when (catch 'unread
                    (lyskom-traverse
                        misc-item (text-stat->misc-info-list text-stat)
                      (when (and (eq (misc-info->type misc-item) 'COMM-IN)
                                 (not (lyskom-text-read-at-least-once-p 
                                       (blocking-do
                                        'get-text-stat
                                        (misc-info->comm-in misc-item)))))
                        (throw 'unread t))))
              (unless (lyskom-j-or-n-p
                       (lyskom-format 'have-unread-comment text-stat))
                (signal 'lyskom-edit-text-abort
                        (list "%s"
                              (lyskom-get-string 
                               'please-check-commented-texts))))))))
      (lyskom-message "%s" (lyskom-format 'checking-comments-done)))
    
    
    ;;
    ;; Confirm multiple recipients
    ;;
    

    (if (and (lyskom-default-value 'kom-confirm-multiple-recipients)
             (not (eq (lyskom-default-value 'kom-confirm-multiple-recipients)
                      'before))
             (> (- num-real-recpt num-me) 1))

        (save-excursion
          (goto-char (point-min))
          (if (not 
               (lyskom-j-or-n-p
                (lyskom-format 'comment-all-relevant-p)))
              (signal 'lyskom-edit-text-abort 
                      (list "%s" 
                            (lyskom-get-string 
                             'please-edit-recipients))))))

    ;;
    ;; Check that the authors of all commented texts get to see the new text
    ;;

    (let ((authors-to-ask-about (lyskom-compute-recipients-commented-authors 
				 comm-to-list 
				 (delq nil 
				       (mapcar (lambda (misc) 
						 (and (memq (car misc) lyskom-recpt-types-list)
						      (cdr misc)))
					       (cdr misc-list))))))
      (when authors-to-ask-about

        ;;
        ;; Now authors-to-ask-about contains all authors that we
        ;; want to ask about. So do that.
        ;;

        (lyskom-traverse author authors-to-ask-about
          (let ((send-comments-to
                 (car (lyskom-get-aux-item (conf-stat->aux-items author) 33))))
            (when send-comments-to
              (cond ((string-match "^\\([0-9]+\\)\\s-+\\([0-9]+\\)" (aux-item->data send-comments-to))
                     (setq send-comments-to 
                           (cons (string-to-number
                                  (match-string 1 (aux-item->data send-comments-to)))
                                 (string-to-number
                                  (match-string 2 (aux-item->data send-comments-to))))))
                    ((string-match "^\\([0-9]+\\)"
                                   (aux-item->data send-comments-to))
                     (setq send-comments-to
                           (cons (string-to-number
                                  (match-string 1 (aux-item->data send-comments-to)))
                                 0)))
                    (t (setq send-comments-to nil))))

            (when (lyskom-j-or-n-p 
                   (lyskom-format 'add-recipient-p 
                                  author
                                  (car send-comments-to)))
              (setq extra-headers
                    (nconc
                     (list
                      (cond ((and send-comments-to
                                  (eq (cdr send-comments-to) 0)) 'RECPT)
                            ((and send-comments-to
                                  (eq (cdr send-comments-to) 1)) 'CC-RECPT)
                            ((and send-comments-to
                                  (eq (cdr send-comments-to) 15)) 'BCC-RECPT)
                            (t (if (lyskom-j-or-n-p
                                    (lyskom-format 'really-add-as-recpt-q author))
                                   'RECPT
                                 'CC-RECPT)))
                            (conf-stat->conf-no author))
                     extra-headers)))))))
    extra-headers))


;;UNUSED: lyskom-send-enriched
(defun lyskom-send-enriched ()
  (let ((content-type (lyskom-get-aux-item lyskom-edit-aux-list 1)))
    (when (or (null content-type)
              (and (= (length content-type) 1)
                   (string-match "^text/enriched" (aux-item->data (car content-type)))))
      (condition-case err
          (let ((buf (lyskom-get-buffer-create 'lyskom-enriched 
                                               "lyskom-enriched" 
                                               t)))
            (unwind-protect
                (save-current-buffer
                  (set-buffer buf)
                  (insert lyskom-edit-text)
                  (goto-char (point-min))
                  (format-encode-buffer 'text/enriched)
                  (goto-char (point-min))
                  (search-forward "\n\n")
                  (when (and (not (lyskom-string= 
                                   (replace-in-string (buffer-substring (point) (point-max))
                                                      "<<" "<" t)
                                   lyskom-edit-text))
                             (lyskom-with-lyskom-buffer
                               (lyskom-j-or-n-p 
                                (lyskom-get-string 'send-formatted))))
                    (setq lyskom-edit-text (buffer-substring (point) (point-max)))
                    (unless content-type
                      (setq lyskom-edit-aux-list 
                            (cons (lyskom-create-aux-item 0 1 nil nil
                                                          (lyskom-create-aux-item-flags nil nil nil nil nil nil nil nil)
                                                          0
                                                          "text/enriched")
                                  lyskom-edit-aux-list)))
                    (setq lyskom-edit-aux-list
                          (cons (lyskom-create-aux-item 0 10002 nil nil
                                                        (lyskom-create-aux-item-flags nil nil nil nil nil nil nil nil)
                                                        0
                                                        (buffer-substring (point-min) (point)))
                                lyskom-edit-aux-list)))
                  'stop-transforming-texts)
              (kill-buffer buf)))
        (error (if (lyskom-j-or-n-p
                    (lyskom-format (lyskom-get-string 'transform-error)
                                   (error-message-string err)))
                   nil
                 (signal 'lyskom-edit-text-abort nil)))))))



(defun kom-edit-quit ()
  "Cancel editing the text being written and return to reading LysKOM."
  (interactive)
  (let ((edit-buffer (current-buffer)))
    (goto-char (point-max))
    (setq lyskom-is-writing nil)
    (lyskom-tell-internat 'kom-tell-regret)
    (lyskom-save-excursion (set-buffer edit-buffer)
                           (delete-auto-save-file-if-necessary))
    (lyskom-undisplay-buffer edit-buffer)
    (kill-buffer edit-buffer))
  (garbage-collect))			;Take care of the garbage.


(defun kom-edit-show-commented ()
  "Show the commented text in another window."
  (interactive)
  (lyskom-edit-get-commented 'lyskom-edit-show-commented))


(defun kom-edit-insert-commented (arg)
  "Insert the commented text, prepending each line with the
text in `kom-cite-string' (defaults to \"> \"). Note that citing
the commented text is not common practise in LysKOM (unlike
e-mail and news) since there is a strong link to the commented
text anyway. Use this command sparingly."
  (interactive "P")
  (lyskom-edit-get-commented
   'lyskom-edit-insert-commented
   (list (cond ((and arg (listp arg)) "")
	       (t nil)))))


(defun kom-edit-insert-buglist ()
  "Insert the commented buglist, Roxen Internet Software style. Excluded from manual."
  (interactive)
  (lyskom-edit-get-commented 'lyskom-edit-insert-buglist))


(defun kom-edit-insert-digit-text ()
  "Prompt for a text to insert. Excluded from manual."
  (interactive)
  (setq unread-command-events (cons last-command-event unread-command-events))
  (call-interactively 'kom-edit-insert-text nil))


(defun kom-edit-insert-text (no)
  "Prompt for a text to insert, prefixing each line with the contents
of `kom-cite-string' (defaults to \"> \"). Note that citing texts is not
commonplace in LysKOM (unlike e-mail and news) since it is easy to
refer to specific texts (see `kom-edit-insert-link' and
`kom-edit-add-cross-reference'). Use this command sparingly."
  (interactive (list
		(cond
		 ((null current-prefix-arg)
                  (lyskom-read-number 'which-text-include))
		 ((prefix-numeric-value current-prefix-arg)))))
  (let ((buffer (current-buffer))
        (window (selected-window)))
    (set-buffer lyskom-buffer)
    (lyskom-collect 'edit)
    (initiate-get-text 'edit nil no)
    (initiate-get-text-stat 'edit nil no)
    (lyskom-use 'edit 'lyskom-edit-insert-commented buffer window)
    (set-buffer buffer)
    (sit-for 0)))


(defun lyskom-edit-get-commented (thendo &optional arg-list)
  "Get the commented text and text stat and then do THENDO with it.
This function is called with an argument list TEXT TEXT-STAT BUFFER
WINDOW plus any optional arguments given in ARG-LIST."
  (let ((p (point)))
    (save-excursion
      (let* ((buffer (current-buffer))
             (window (selected-window))
             (headers (condition-case nil
                          (lyskom-text-headers->misc-info (lyskom-edit-parse-headers))
                        (lyskom-edit-error nil))) ; Ignore these errors
             (no nil))
        (while headers
          (if (memq (car headers) '(COMM-TO FOOTN-TO))
              (setq no (car (cdr headers))
                    headers nil)
            (setq headers (cdr (cdr headers)))))
        (cond
         (no
          (goto-char p)
          (set-buffer lyskom-buffer)
          (blocking-do-multiple ((text (get-text no))
                                 (text-stat (get-text-stat no)))
            (set-buffer buffer)
            (apply thendo text text-stat buffer window arg-list)))
         (t
          (lyskom-message "%s" (lyskom-get-string 'no-such-text-m))))))
    (sit-for 0)))


;;; ============================================================
;;; Info node stuff

(defvar Info-current-node)
(defvar Info-current-file)

;; NOTUSED: kom-yank-info-nodename
(defalias 'kom-yank-info-nodename 'kom-edit-yank-info-nodename)
(defun kom-edit-yank-info-nodename ()
  "When browsing info files, this command will place a reference to the
current info node in the kill ring, from where it can be pasted into
another buffer. This command is useful when you want to refer to an 
info node in a LysKOM text."
  (interactive)
  (kill-new (format "*Note %s: (%s)%s,"
		    Info-current-node
		    (file-name-nondirectory Info-current-file)
		    Info-current-node)))


;; NOTUSED: kom-insert-last-info-nodename
(defalias 'kom-insert-last-info-nodename 'kom-edit-insert-last-info-nodename)
(defun kom-edit-insert-last-info-nodename ()
  "Insert a reference to the most recently visited info node."
  (interactive)
  (condition-case nil
      (let ((link nil))
        (save-current-buffer
          (set-buffer (get-buffer "*info*"))
          (setq link (format "*Note %s: (%s)%s,"
                             Info-current-node
                             (file-name-nondirectory Info-current-file)
                             Info-current-node)))
        (insert link))
    (error (lyskom-message "%s" (lyskom-get-string 'cant-find-info-node)))))

;;; ================================================================
;;;	  Add recipient, copy-recipient - Addera mottagare 
;;;
;;;  Author: Anders Gertz
;;;  Changed by: Linus Tolke

(defun kom-edit-add-comment ()
  "Makes this text a comment to another text. Using this command it is
possible to make the text a comment to multiple texts. To remove a 
comment link, simply remove the corresponding line from the headers
in the edit buffer."
  (interactive)
  (let* ((edit-buffer (current-buffer))
         (insert-at (point-min-marker))
         (text-no (lyskom-read-number (lyskom-get-string 'text-to-comment-q)))
         (text-stat (blocking-do 'get-text-stat text-no)))
    (lyskom-ignore edit-buffer)
    (lyskom-save-excursion
     (if text-stat
         (lyskom-edit-get-commented-author 
          (blocking-do 'get-text-stat text-no)
          (lyskom-get-string 'comment)
          insert-at text-no)
       (lyskom-error "%s" (lyskom-get-string 'no-such-text-m)))
     (set-marker insert-at nil))))


(defun kom-edit-add-recipient ()
  "Adds a regular recipient to the text or converts an existing
recipient to a regular recipient. Using this command it is possible to
add any number of regular recipients. To remove a recipient, simply
delete the corresponding header line in the edit buffer."
  (interactive)
  (lyskom-edit-add-recipient/copy 'added-recipient nil 'RECPT))


(defun kom-edit-add-bcc ()
  "Adds a blind carbon copy recipient to the text, or converts an
existing recipient to blind carbon copy. Using this command it is
possible to add any number of regular recipients. To remove a
recipient, simply delete the corresponding header line in the edit
buffer."
  (interactive)
  (lyskom-edit-add-recipient/copy 'added-blank-carbon-copy nil 'BCC-RECPT))


(defun kom-edit-add-copy ()
  "Adds a carbon copy recipient to the text, or converts an existing
recipient to blind carbon copy. Using this command it is possible to
add any number of regular recipients. To remove a recipient, simply
delete the corresponding header line in the edit buffer."
  (interactive)
  (lyskom-edit-add-recipient/copy 'added-carbon-copy nil 'CC-RECPT))

(defun kom-edit-move-text ()
  "Adds a regular recipient to the text, or converts an existing
recipient to blind carbon copy, and converts all other recipients to
carbonn copy recipients. This command is intended for situations where
a commend is being sent to a different recipient than the commented
text was."
  (interactive)
  (lyskom-edit-add-recipient/copy 'edit-who-to-move-to-q
                                  'lyskom-edit-move-recipients))


(defun lyskom-edit-move-recipients (conf-stat insert-at edit-buffer)
  (save-current-buffer
    (set-buffer edit-buffer)
    (let* ((headers (lyskom-edit-parse-headers))
           (subject (lyskom-text-headers->subject headers))
           (miscs (mapcar (lambda (x) (cond ((and (eq (car x) 'RECPT)
						  (not (conf-type->letterbox
							(uconf-stat->conf-type
							 (blocking-do 'get-uconf-stat (cdr x))))))
					     (cons 'CC-RECPT (cdr x)))
					    (t x)))
			  (cdr (lyskom-edit-translate-headers (lyskom-text-headers->misc-info headers)))))
           (aux-list (lyskom-text-headers->aux-items headers))
           (elem nil))


      ;; If the new target is already a recipient, convert it to the right 
      ;; kind. Otherwise insert the new target after the last comm-to

      (setq elem (lyskom-edit-find-misc miscs lyskom-recpt-types-list 
                                        (conf-stat->conf-no conf-stat)))
      (if elem
          (setcar elem 'RECPT)
        (lyskom-insert-in-list
         (cons 'RECPT (conf-stat->conf-no conf-stat))
         miscs
         (car (cdr (memq (lyskom-edit-find-misc miscs '(FOOTN-TO COMM-TO) 
                                                nil t)
                         miscs)))))

      (lyskom-edit-replace-headers subject (cons 'MISC-LIST miscs) aux-list))))



(defun lyskom-edit-do-add-recipient/copy (recpt-type recpt-no edit-buffer)
  (save-current-buffer
    (set-buffer edit-buffer)
    (let* ((headers (lyskom-edit-parse-headers))
           (miscs (lyskom-edit-translate-headers (lyskom-text-headers->misc-info headers)))
           (elem (lyskom-edit-find-misc miscs lyskom-recpt-types-list
                                        recpt-no)))

      (cond (elem (setcar elem recpt-type))
            (t (setq miscs
                     (append miscs (list (cons recpt-type recpt-no))))))
      (lyskom-edit-replace-headers (lyskom-text-headers->subject headers)
                                   miscs
                                   (lyskom-text-headers->aux-items headers)))))


(defun lyskom-edit-add-recipient/copy (prompt &optional what-to-do recpt-type)
  "Adds a new recipient or a cc-recipient to the text which is being edited.
PROMPT is the prompt to use to ask the user for a recipient.
WHAT-TO-DO is a function to call to do the insertion.
RECPT-TYPE is the type of recipient to add."
  (let ((edit-buffer (current-buffer))
	(insert-at (point-min-marker))
	(conf-stat (lyskom-read-conf-stat prompt '(all) nil nil t)))
    (lyskom-save-excursion
     (save-current-buffer
       (set-buffer lyskom-buffer)
       ;; +++ The information about msg-of-day might be old. We should
       ;; make sure it is up-to-date.

       (let ((text-no (conf-stat->msg-of-day conf-stat))
             (win-config nil)
             (text nil)
             (text-stat nil)
             (collector (make-collector)))

         (unless (zerop text-no)
           (initiate-get-text 'edit 'collector-push text-no collector)
           (initiate-get-text-stat 'edit 'collector-push text-no collector)
           (lyskom-wait-queue 'edit)
           (setq text-stat (elt (collector->value collector) 0))
           (setq text (elt (collector->value collector) 1)))

         (when (or (null text)
                   (null text-stat)
                   (null (get-buffer-window edit-buffer))
                   (progn (setq win-config (current-window-configuration))
                          (with-output-to-temp-buffer "*Motd*"
                            (lyskom-princ
                             (lyskom-format 'conf-has-motd-no
                                            (text->text-no text)
                                            (text->decoded-text-mass text 
                                                                     text-stat))))
                          (j-or-n-p (lyskom-get-string 'still-want-to-add))))

           (when (and kom-confirm-add-recipients
                      (eq recpt-type 'RECPT)
                      (not (lyskom-j-or-n-p (lyskom-format
                                             'really-add-as-recpt-q
                                             conf-stat))))
             (setq recpt-type 'CC-RECPT))

           (if what-to-do
               (funcall what-to-do conf-stat insert-at edit-buffer)
             (lyskom-edit-do-add-recipient/copy recpt-type
                                                (conf-stat->conf-no conf-stat)
                                                edit-buffer)))
         (when win-config (set-window-configuration win-config))))
     (set-marker insert-at nil))))

(defun lyskom-edit-sub-recipient/copy (recpt-no edit-buffer)
  "Remove the recipient having RECPT-NO from EDIT-BUFFER"
  (save-current-buffer
    (set-buffer edit-buffer)
    (let* ((headers (lyskom-edit-parse-headers))
           (miscs (lyskom-edit-translate-headers (lyskom-text-headers->misc-info headers)))
           (elem (lyskom-edit-find-misc miscs lyskom-recpt-types-list
                                        recpt-no)))

      (when elem (setcar elem nil))

      (lyskom-edit-replace-headers (lyskom-text-headers->subject headers)
                                   miscs
                                   (lyskom-text-headers->aux-items headers)))))
    

(defun kom-edit-add-cross-reference ()
  "Add a cross reference to this text. This command prompts for the
type and target of the cross reference. Cross references are used
to systematically refer from a text to another text, conference or
person without altering the contents of the text."
  (interactive)
  (let ((item (lyskom-read-cross-reference-and-get-aux-item)))
    (when item
      (lyskom-edit-insert-aux-item item))))

(defun kom-edit-add-read-confirm-request ()
  "Request that others confirm reading this text. Conforming clients
will ask each reader of this text to confirm reading the text. Note
that not all clients understand this request, and that confirmation is
neither automatic nor mandatory. Use very sparingly as this interrupts
the normal flow of reading for many users."
  (interactive)
  (lyskom-edit-insert-aux-item
   (lyskom-create-aux-item 0 6 0 0
                           (lyskom-create-aux-item-flags
                            nil nil nil nil nil nil nil nil)
                           0 "")))

(defun kom-edit-add-no-comments ()
  "Request that nobody comments this text. Conforming clients will
either prevent users from commenting the text or ask for confirmation
before commenting the text. Note that not all clients understand this
request and that it is advisory only."
  (interactive)
  (lyskom-edit-insert-aux-item
   (lyskom-create-aux-item 0 4 0 0
                           (lyskom-create-aux-item-flags
                            nil nil nil nil nil nil nil nil)
                           0 "")))

(defun kom-edit-add-personal-comments ()
  "Request that all replies to this text are in the form of personal
replies. Conforming clients will treat a request to comment this text
as a request to answer privately. Note that not all clients understand
this request and that it is advisory only."
  (interactive)
  (lyskom-edit-insert-aux-item
   (lyskom-create-aux-item 0 5 0 0
                           (lyskom-create-aux-item-flags
                            nil nil nil nil nil nil nil nil)
                           0 "")))

(defun kom-edit-add-world-readable ()
  "Make this text readable to all users, even if they are not logged
in. World readable texts can be read by anyone, regardless of what
recipients the text has. World readable texts can even be read without
logging in to LysKOM."
  (interactive)
  (lyskom-edit-insert-aux-item
   (lyskom-create-aux-item 0 34 0 0
                           (lyskom-create-aux-item-flags
                            nil nil nil nil nil nil nil nil)
                           0 "")))

(defun kom-edit-insert-link ()
  "Insert an in-line link to another text, a conference or a person.
This command prompts for the target of the link and inserts text that
most clients will interpret as a link."
  (interactive)
  (let ((item (lyskom-read-link)))
    (when item
      (insert item))))



(defun lyskom-read-link ()
  "Query user about link type and value, and return the corresponding
link as a string."
  (let* ((type (lyskom-a-or-b-or-c-p 'link-type
                                     '(abc-conference
                                       abc-person
                                       abc-text)
                                     nil))
         (obj nil))
    (cond
     ((eq type 'abc-text)
      (let ((prompt 'which-text-to-link))
        (while (null obj)
          (setq obj (blocking-do 'get-text-stat
                                 (lyskom-read-number prompt)))
          (setq prompt 'which-text-to-link-err )))
      (let* ((text-no (text-stat->text-no obj))
             (text (blocking-do 'get-text text-no))
	     (txt (text->decoded-text-mass text obj))
	     (eos (string-match (regexp-quote "\n") txt))
	     (subject (substring txt 0 eos)))
	(format "<text %d: %s>" text-no subject)))
     
     ((eq type 'abc-conference)
      (while (null obj)
        (setq obj (lyskom-read-conf-stat 'which-conf-to-link '(conf) nil nil t)))
      (format "<möte %d: %s>" (conf-stat->conf-no obj)
			   (conf-stat->name obj)))

     ((eq type 'abc-person)
      (while (null obj)
        (setq obj (lyskom-read-conf-stat 'which-pers-to-link '(pers) nil nil t)))
      (format "<person %d: %s>" (conf-stat->conf-no obj)
			   (conf-stat->name obj))))))


(defun lyskom-edit-insert-aux-item (item)
  "Insert the aux item ITEM in the current buffer"
  (save-excursion
    (lyskom-edit-find-separator t)
    (forward-line (if kom-edit-hide-add-button -1 -2))
    (insert
     (concat (lyskom-format
              (format "%%#1@%%[%s%%] %%#2s" (lyskom-get-string 'aux-item-prefix))
	      ; FIXME: What happens with marker?
              (lyskom-default-button 'aux-edit-menu (cons (current-buffer)
                                                          (point-marker)))
              (lyskom-aux-item-call item 
                                    '(edit-insert print)
                                    item 
                                    lyskom-pers-no))
              "\n")))
  )


(defun lyskom-edit-toggle-secret-aux (buf arg text)
  (interactive)
  (save-current-buffer
    (set-buffer (car arg))
    (goto-char (cdr arg))
    (lyskom-edit-toggle-aux-item-flag buf arg text 'secret)))

(defun lyskom-edit-toggle-anonymous-aux (buf arg text)
  (interactive)
  (save-current-buffer
    (set-buffer (car arg))
    (goto-char (cdr arg))
    (lyskom-edit-toggle-aux-item-flag buf arg text 'anonymous)))

(defun lyskom-edit-toggle-inherit-aux (buf arg text)
  (interactive)
  (save-current-buffer
    (set-buffer (car arg))
    (goto-char (cdr arg))
    (lyskom-edit-toggle-aux-item-flag buf arg text 'inherit)))

(defun lyskom-edit-delete-aux (buf arg text)
  (interactive)
  (save-current-buffer
    (set-buffer (car arg))
    (goto-char (cdr arg))
    (beginning-of-line)
    (delete-region (point) (save-excursion (forward-line 1) (point)))))


(defun lyskom-edit-generate-aux-item-flags (flags)
  (let ((str (mapconcat 'identity
                          (delq nil
                                (list
                                 (and (aux-item-flags->secret flags)
                                      (lyskom-get-string 'secret-aux-flag))
                                 (and (aux-item-flags->anonymous flags)
                                      (lyskom-get-string 'anonymous-aux-flag))
                                 (and (aux-item-flags->inherit flags)
                                      (lyskom-get-string 'inherit-aux-flag))))
                          ", ")))
      (when (not (string= str ""))
        (format " [%s]" str))))

(defun lyskom-edit-insert-aux-item-flags (flags)
  (end-of-line)
  (insert (lyskom-edit-generate-aux-item-flags flags)))

(defun lyskom-edit-toggle-aux-item-flag (buf arg text flag)
  (beginning-of-line)
  (let ((flags (lyskom-edit-extract-aux-item-flags)))
    (when (re-search-forward "\\s-?\\[[^]]*\\]\\s-*$" 
                             (save-excursion (end-of-line) (point)) t)
      (delete-region (match-beginning 0) (match-end 0)))
    (funcall (intern (format "set-aux-item-flags->%s" flag))
             flags
             (not
              (funcall (intern (format "aux-item-flags->%s" flag)) flags)))
    (lyskom-edit-insert-aux-item-flags flags)))



  

;;; ================================================================
;;;   Help functions for the functions bound to keyboard sequences 
;;;                       in lyskom-edit-mode.


(defun lyskom-edit-find-misc (misc-list type data &optional last)
  "Return the first misc-info pair in MISC-LIST of type TYPE containing DATA.
If TYPE is a list, then any type in TYPE is considered to match. If DATA
is nil, then any DATA is considered to match.
If optional LAST is non-nil, then return the last match instead of the first."
  (when (eq (car misc-list) 'MISC-LIST) (setq misc-list (cdr misc-list)))
  (let ((result nil)
        (elem nil))
    (while (and misc-list (or last (null result)))
      (setq elem (car misc-list))
      (setq misc-list (cdr misc-list))
      (when (cond ((listp type) (and (memq (car elem) type)
                                     (or (null data) (eq data (cdr elem)))))
                  ((symbolp type) (and (eq type (car elem))
                                       (or (null data) (eq data (cdr elem)))))
                  (t (or (null data) (eq data (cdr elem)))))
        (setq result elem)))
    result))

(defun lyskom-edit-translate-headers (misc-list)
  "Translate result of lyskom-edit-parse-header to something we can send
to lyskom-edit-replace-headers"
  (let ((result nil))
    (while misc-list
      (setq result (cons (cons (car misc-list) (car (cdr misc-list)))
                         result))
      (setq misc-list (cdr (cdr misc-list))))
    (cons 'MISC-LIST (nreverse result))))

(defun lyskom-edit-replace-headers (subject misc-list aux-list)
  "Replace all headers with SUBJECT and MISC-LIST"
  (save-excursion
    (let ((start nil)
          (inhibit-read-only t)
          (end nil))
      (goto-char (point-min))
      (setq start (point-marker))
      (set-marker-insertion-type start t)
      (lyskom-edit-find-separator t)
      (end-of-line)
      (setq end (point-marker))
      (goto-char (point-min))
      (lyskom-edit-insert-miscs misc-list subject "" aux-list)
      (delete-region start end)
      (goto-char end)
      (delete-char 1)
      (set-marker start nil)
      (set-marker end nil))))

(defun lyskom-looking-at-header (header match-number)
  "Check if point is at the beginning of a header of type HEADER.
Return the corresponding number (conf no etc.) if MATCH-NUMBER is
non-nil. If MATCH-NUMBER is 'angled, only match a number inside <>."
  (if (lyskom-looking-at
       (concat (lyskom-get-string header)
	       (cond ((eq match-number 'angled)
		      "[^0-9\n\r]*<\\([0-9]+\\)>")
                     ((eq match-number 'empty)
                      "[^:]*:\\(\\s-*\\)$")
		     (match-number
		      "[^0-9]*\\([0-9]+\\)")
		     (nil
		      ""))))
      (if match-number
	  (lyskom-string-to-number (buffer-substring (match-beginning 2)
					   (match-end 2)))
	t)
    nil))

(defun lyskom-edit-parse-headers ()
  "Parse the headers of an article.

The value returned is a lyskom-text-headers structure."
  (save-excursion
    (goto-char (point-min))
    (let ((misc nil)
          (subject nil)
          (aux nil))
      (save-restriction
        ;; Narrow to headers
        (lyskom-edit-find-separator t)
        (narrow-to-region (point-min) (point))
        (goto-char (point-min))
        (while (< (point) (point-max))
          (let ((case-fold-search t)
                (n nil))
            (cond
             ((setq n (lyskom-looking-at-header 'recipient-prefix 'angled))
              (setq misc (nconc misc (list 'RECPT n))))
             ((setq n (lyskom-looking-at-header 'carbon-copy-prefix 'angled))
              (setq misc (nconc misc (list 'CC-RECPT n))))
             ((setq n (lyskom-looking-at-header 'blank-carbon-copy-prefix
                                                'angled))
              (setq misc (nconc misc (list 'BCC-RECPT n))))
             ((setq n (lyskom-looking-at-header 'comment-prefix t))
              (setq misc (nconc misc (list 'COMM-TO n))))
             ((setq n (lyskom-looking-at-header 'footnote-prefix t))
              (setq misc (nconc misc (list 'FOOTN-TO n))))
             ((lyskom-looking-at-header 'header-subject nil)
              (setq subject (lyskom-edit-extract-subject)))

             ((lyskom-looking-at (lyskom-get-string 'aux-item-prefix-regexp))
              (goto-char (match-end 0))
              (let ((item (lyskom-edit-parse-aux-item)))
                (if item
                    (setq aux (cons item aux))
                  (signal 'lyskom-unknown-header
                          (list 'unknown-header (point))))))

             ((lyskom-looking-at (lyskom-get-string 'comment-item-prefix))
              nil)

             ((or (lyskom-looking-at-header 'blank-carbon-copy-prefix 'empty)
                  (lyskom-looking-at-header 'carbon-copy-prefix 'empty)
                  (lyskom-looking-at-header 'recipient-prefix 'empty))
              nil)
             ((lyskom-looking-at-header 'add-recpt-button-text-regex nil) nil)

             (t (signal 'lyskom-unknown-header (list 'unknown-header (point))))))
          (forward-line 1)))
      (lyskom-create-lyskom-text-headers subject misc aux))))

(defun lyskom-edit-parse-aux-item ()
  (let ((definitions lyskom-aux-item-definitions)
        (data nil))
    (while (and (null data) definitions)
      (setq data (lyskom-aux-item-call (car definitions) 'parse))
      (unless data (setq definitions (cdr definitions))))

    (when data
      (lyskom-create-aux-item 0 (lyskom-aux-item-definition->number
                                 (car definitions))
                              0 0 (lyskom-edit-extract-aux-item-flags) 0
                              data))))

(defun lyskom-edit-extract-aux-item-flags ()
  (if (lyskom-looking-at ".*\\[\\([^]]*\\)\\]\\s-*$")
    (let ((flag-strings (match-string 1))
          (start 0)
          (flag nil)
          (flags (lyskom-create-aux-item-flags 
                  nil nil nil nil nil nil nil nil))
          (case-fold-search t))
      (while (string-match "\\s-*\\(\\w+\\)\\s-*" flag-strings start)
        (setq flag (match-string 1 flag-strings))
        (setq start (match-end 0))
        (cond ((string-match (lyskom-get-string 'secret-aux-flag) flag)
               (set-aux-item-flags->secret flags t))
              ((string-match (lyskom-get-string 'anonymous-aux-flag) flag)
               (set-aux-item-flags->anonymous flags t))
              ((string-match (lyskom-get-string 'inherit-aux-flag) flag)
               (set-aux-item-flags->inherit flags t))
              (t nil)))
      flags)
    (lyskom-create-aux-item-flags nil nil nil nil nil nil nil nil)))


(defun lyskom-edit-extract-subject ()
  "Find the subject.
Point must be located on the line where the subject is."
  (re-search-forward ": \\(.*\\)")
  (buffer-substring (match-beginning 1) (match-end 1)))


(defun lyskom-edit-extract-text ()
  "Get text as a string."
  (save-excursion
    (if (not (lyskom-edit-find-separator t))
	(signal 'lyskom-internal-error
		"Altered lyskom-header-separator line.")
      (end-of-line)
      (buffer-substring (1+ (point))
			(progn
			  (goto-char (1- (point-max)))
			  (while (lyskom-looking-at "\\s-") ; remove trailing
			    (backward-char 1)) ; whitespace
			  (forward-char 1)
			  (point))))))


(defun lyskom-create-text-handler (text-no edit-buffer is-anonymous
					   callback callback-data 
                                           callback-buffer)
  "Handle an attempt to write a text."
  (lyskom-tell-internat 'kom-tell-silence)
  (message "")
  (cond
   ((null text-no)
    (lyskom-insert-before-prompt
     (lyskom-format 'could-not-create-text lyskom-errno
		    (lyskom-get-error-text lyskom-errno)))
    (beep)
    (lyskom-message "%s" (lyskom-format 'could-not-create-text lyskom-errno
				   (lyskom-get-error-text lyskom-errno)))
    (set-buffer lyskom-buffer)
    (read-list-enter-first (lyskom-create-read-info 'RE-EDIT-TEXT
                                                    nil
                                                    256
                                                    nil
                                                    nil
                                                    nil
                                                    edit-buffer)
                           lyskom-reading-list)
    (lyskom-update-prompt)
    (set-buffer edit-buffer)
    (lyskom-edit-mode 1)
    (sit-for 0))
   (t 
    (if is-anonymous
        (lyskom-format-insert-before-prompt 'text-created-anonymous text-no)
      (lyskom-insert-before-prompt (lyskom-format 'text-created  text-no)))

    ;; Save the text

    (when (and kom-created-texts-are-saved (buffer-live-p edit-buffer))
      (initiate-get-conf-stat 'background 
                              'lyskom-edit-fcc-text
                              lyskom-pers-no
                              (save-current-buffer (set-buffer edit-buffer)
                                                   (buffer-string))
                              text-no
                              is-anonymous))

    ;; Immediately mark the text as read if kom-created-texts-are-read is set
    ;; and we are not sending the text anonymously.
    
    (cond
     ((and kom-created-texts-are-read (not is-anonymous))
      (lyskom-is-read text-no)
      (initiate-get-text-stat 'background 'lyskom-mark-as-read
			      text-no)
      (lyskom-run 'background 'set 'lyskom-dont-change-prompt nil)
      (lyskom-run 'background 'lyskom-set-mode-line))
     (t (setq lyskom-dont-change-prompt nil)))

    (save-current-buffer
      (set-buffer edit-buffer)		;Need local variables.
      (lyskom-edit-sent-mode 1))

    ;; Record the text number

    (unless is-anonymous
      (lyskom-setq-default lyskom-last-written text-no)
      (lyskom-setq-default lyskom-last-seen-written text-no))

    ;; Select the old configuration.

    (when (get-buffer-window edit-buffer)
      (set-window-configuration lyskom-edit-return-to-configuration)
      (set-buffer (window-buffer (selected-window)))
      (goto-char (point-max)))

    ;; Apply handler.

    (when (and callback (buffer-live-p callback-buffer))
      (save-current-buffer
        (set-buffer callback-buffer)
        (if callback (apply callback text-no callback-data))))

    ;; Kill the edit-buffer.

    (save-current-buffer
     (set-buffer edit-buffer)
     (delete-auto-save-file-if-necessary))
    (kill-buffer edit-buffer)
)))

(defun lyskom-edit-fcc-text (conf-stat text text-no is-anonymous)
  (condition-case arg
      (let ((start 1) (end 0) (inhibit-read-only t))
        (save-current-buffer
          (set-buffer (lyskom-get-buffer-create 'fcc "*kom*-fcc" t))
          (erase-buffer)
          (insert text)
          (goto-char (point-min))
          (when (lyskom-edit-find-separator t)
            (delete-region (match-beginning 0) (match-end 0))
            (unless kom-edit-hide-add-button
              (save-excursion
                (beginning-of-line)
                (forward-line -2)
                (delete-region (progn (beginning-of-line) (point))
                               (progn (forward-line 1) (point)))))
            (insert (make-string kom-text-header-dash-length ?-))
            (forward-line 1)
            (beginning-of-line)
            (setq start (point)))
          (goto-char (point-min))
          (when (re-search-forward "\\(\\s-\\|[\n\r]\\)+\\'" nil t)
            (delete-region (match-beginning 0) (match-end 0)))
          (goto-char (point-max))
          (setq end (point))
          (insert (lyskom-format "\n(%#1n) %#2s\n\n"
                                 text-no
                                 (make-string (- kom-text-footer-dash-length
                                                 (lyskom-string-width (int-to-string text-no)) 
                                                 3) ?-)))
          (goto-char (point-min))
          (lyskom-format-insert-at-point 'text-no-comment 
                                         text-no
                                         (let ((kom-print-relative-dates nil))
                                           (lyskom-format-time 'date-and-time))
                                         (count-lines start end)
                                         conf-stat
                                         is-anonymous)
          (append-to-file (point-min)
                          (point-max)
                          (expand-file-name kom-created-texts-are-saved))))
    (file-error (message "%S" arg)(lyskom-format-insert-before-prompt 'cant-fcc-text-file-error
                                                    text-no
                                                    kom-created-texts-are-saved
                                                    (elt arg 1)
                                                    (elt arg 2)
                                                    ))
    (error (lyskom-format-insert-before-prompt 'cant-fcc-text 
                                               text-no
                                               kom-created-texts-are-saved
                                               (error-message-string arg)))))
                          


(defun lyskom-edit-show-commented (text text-stat editing-buffer window)
  "Handles the TEXT and TEXT-STAT from the return of the call of the text.
The EDITING-BUFFER is the buffer the editing is done in. If this buffer is 
not displayed nothing is done. If displayed then this buffer is chosen then 
the with-output-to-temp-buffer command is issued to make them both apear."
  (and text
       (get-buffer-window editing-buffer)
       (progn
         (set-buffer editing-buffer)
         (select-window window)
         (let ((buf (lyskom-get-buffer-create 'view-commented "*Commented*"))
               (kom-deferred-printing nil))
           (save-selected-window
             (lyskom-display-buffer buf)
             (unless kom-review-uses-cache
               (cache-del-text-stat (text->text-no text)))
             (save-current-buffer (set-buffer buf)
                                  (erase-buffer)
                                  (lyskom-view-text (text->text-no text))
                                  (set-buffer-modified-p nil)
                                  (lyskom-view-mode)))))))


(defun lyskom-edit-insert-commented (text text-stat editing-buffer window &optional prefix)
  "Handles the TEXT and TEXT-STAT from the return of the call of the text.
When given a PREFIX string, that is prepended to each inserted line.
If PREFIX is nil, use the default prefix (kom-cite-string or \">\"."
  (if (and text text-stat)
      (let ((str (text->decoded-text-mass text text-stat))
	    (prefix (or prefix (lyskom-default-value 'kom-cite-string) ">")))
        (set-buffer editing-buffer)
        (and (not (bolp))
             (insert "\n"))
        (and (not (eolp))
             (open-line 1))
        (let* ((pb (point))
               (as (string-match "\n" str))
               (te (substring str (1+ as))))
          (insert te)
          (while (<= pb (point))
            (beginning-of-line)
	    (when prefix
	      (insert prefix))
            (forward-line -1))))
    (lyskom-message "%s" (lyskom-get-string 'no-get-text))))


;;; ========================================================================
;;;   Treat the commented text as a Roxen Internet Software-style buglist,
;;;   handling the removal of closed subjects, last-message changes et c

;;; Author: Johan Sundström

(defun lyskom-edit-insert-buglist (text text-stat editing-buffer window)
  "Handles the TEXT and TEXT-STAT from the return of the call of the text.
The commented text is inserted in the buffer in the Roxen Internet Software
buglist style, automating the removal of closed subjects and change-marks."
  (if (and text text-stat)
      (let ((str (text->decoded-text-mass text text-stat)))
        (set-buffer editing-buffer)
        (and (not (bolp))
             (insert "\n"))
        (and (not (eolp))
             (open-line 1))
        (let* ((pb (point))
               (as (string-match "\n" str))
               (te (substring str (1+ as))))
	  ;; insert the body of the commented text at point (pb)
          (insert te)
	  ;; traverse the inserted text, replacing a leading !
	  ;; with a space wherever some form of "[ ]" follows
	  ;; (and X:s, since some morons were too inept to
	  ;;  handle these highly advanced syntactic rules)
          (while (re-search-backward "^\\(\\s-*\\)[!X]\\([ \t(]*\\[\\)" nil t)
            (replace-match "\\1 \\2"))
	  (goto-char pb)
	  ;; new pass; leave all [ ], [/] and [\] subjects untouched,
	  ;; but remove the rest, except for category headers, who remain.
          (while
	      ;; Find a closed subject (that does not match any of the above)
	      (re-search-forward "^[ \t*([]*\\[[^\\/ ]\\]" nil t)
	    (beginning-of-line)
	    ;; Delete from here to the next open subject or the
	    ;; next category header, whichever comes first.
	    (let* ((delete-from (point))
		   (delete-to
		    ;; First part matches an open subject, second part finds
		    ;; new headline. A headline is here defined as something
		    ;; with no more than two leading ws characters followed by
		    ;; a non-whitespace, non-[(*! character. The advantage of
		    ;; this over matching against ^\s-*$ is that long subjects
		    ;; may be split into separate paragraphs and still work here.
		    (re-search-forward "^[ \t*(]*\\[[\\/ ]\\]\\|^\\s-\\{0,2\\}[^ \t(*![]" nil t)))
	      (if delete-to
		  ;; There was at least one more subject later on
		  (progn
		    (goto-char delete-to)
		    (beginning-of-line)
		    ;; Are we looking at a headline?
		    (if (lyskom-thing-at-point-looking-at "^\\s-\\{0,2\\}[^ \t(*![]")
			;; yes -- hence we need to move point somewhat
			(progn
			  (beginning-of-line)
			  ;; leave the newline before the headline intact
			  (forward-char -1))))
		;; This was the last subject; kill the rest of the buffer
		(goto-char (point-max)))
	      ;; Drain the closed subject.
	      (delete-region delete-from (point))))))
    (lyskom-message "%s" (lyskom-get-string 'no-get-text))))



;;; ================================================================
;;; Tab between buttons in the header

(defun kom-edit-next-button-or-self-insert (num)
  "Move to the next link or insert a TAB, depending on context. Excluded from manual."
  (interactive "p")
  (let ((header-end (lyskom-edit-find-separator))
        (start (point))
        (next-pos nil))
    (if (> (point) header-end)
        (unless (lyskom-traverse keymap (current-minor-mode-maps)
                  (let ((binding (lookup-key keymap (this-command-keys))))
                    (when (and (commandp binding)
                               (not (eq binding
                                        'kom-edit-next-button-or-self-insert)))
                      (call-interactively binding)
                      (lyskom-traverse-break t))))
          (let ((binding (lookup-key (current-local-map) 
                                     (this-command-keys) t)))
            (if (and (commandp binding)
                       (not (eq binding
                                'kom-edit-next-button-or-self-insert)))
                (call-interactively binding)
              (call-interactively 'self-insert-command))))
      (while (> num 0)
        (lyskom-next-area 1 'lyskom-button)
        (if (eq start (point))
            (setq next-pos nil)
          (setq next-pos (point)))
        (cond ((or (null next-pos)
                   (> next-pos header-end))
               (goto-char start)
               (unless (re-search-forward (regexp-quote (lyskom-get-string
                                                         'header-subject))
                                          header-end
                                          t)
                 (goto-char (point-max))
                 (setq num 0)))
              (t (goto-char next-pos)))
        (setq num (1- num) start (point))))))

(defun kom-edit-prev-button (num)
  "Move to the previous link. Excluded from manual."
  (interactive "p")
  (let ((header-end (lyskom-edit-find-separator)))
    (while (> num 0)
      (cond ((and (> (point) header-end)
                  (progn (goto-char (point-min))
                         (re-search-forward (regexp-quote (lyskom-get-string
                                                           'header-subject))
                                            header-end
                                            t)))
             (setq num (1- num)))
            (t (lyskom-prev-area num 'lyskom-button)
               (setq num 0))))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: filter.el
;;;;
;;;; Contains the support functions for text filtering.
;;;;

(eval-when-compile
  (require 'lyskom-command "command"))

;;;============================================================
;;;
;;; Filter pattern accessors
;;;
;;; Filters are lists with the following structure
;;;
;;;   (filter [ PATTERNS ATTRIBUTES FUNCTION])
;;;
;;; Where PATTERNS is a list of filter patterns and ATTRIBUTES is
;;; an association list. FUNCTION is a lisp function implementing
;;; the filter.
;;;
;;; The following attributes are reserved
;;;
;;; action -- What action to take when the pattern matches. Currently
;;;           one of dontshow, skip or skip-comments.
;;; expire -- When the filter expires. Not used.
;;;

(defun lyskom-make-filter (&optional p a)
  "Creates and returns a filter structure.
Optional P and A initialize pattern and attributes, respectively."
  (lyskom-create-filter p a (lyskom-create-compile-filter-function p)))

(defun lyskom-create-filter (pattern attribute-list function)
  (list 'filter (vector pattern attribute-list function)))

(defun lyskom-recompile-filter (filter)
  "Re-compile the filter FILTER"
  (set-filter->function filter
                        (lyskom-create-compile-filter-function
                         (filter->pattern filter))))

(defun lyskom-copy-filter (f)
  "Create a copy of the filter F"
  (lyskom-make-filter (copy-tree (filter->pattern f))
               (copy-tree (filter->attribute-list f))))

(defun lyskom-filter-p (f)
  "Returns T if f looks like a filter"
  (and (listp f)
       (eq 'filter (car f))))

(defun filter->pattern (f)
  "Extract the patterns part of a filter F"
  (elt (elt f 1) 0))

(defun filter->attribute-list (f)
  "Extract the attribute list of F."
  (elt (elt f 1) 1))

(defun filter->attribute (f a)
  "From filter F, extract the value of attribute A. 
Returns nil if no such attribute is present."
  (cdr (assq a (filter->attribute-list f))))

(defun set-filter->pattern (f p)
  "Set the patterns part of F to P."
  (aset (elt f 1) 0 p))

(defun set-filter->attribute-list (f l)
  "Set the attribute list of filter F to L"
  (aset (elt f 1) 1 l))

(defun set-filter->attribute (f a v)
  "Set the value in filter F of attribute A to V."
  (let ((x (assq a (filter->attribute-list f))))
    (if x
        (setcdr x v)
      (set-filter->attribute-list f
       (cons (cons a v) (filter->attribute-list f))))))

(defun filter->function (f)
  "Get the function for filter F"
  (elt (elt f 1) 2))

(defun set-filter->function (f fn)
  "Set the function for filter F to FN"
  (aset (elt f 1) 2 fn))


;;;============================================================
;;;
;;;


(defvar lyskom-filter-hack nil
  "Variable to busy-wait on to get the filter action. Set to
invalid-value until a filter action has been selected.")

(defun lyskom-filter-text-p (text-no)
  (if (null lyskom-filter-list)
      nil
    (progn
      (setq lyskom-filter-hack 'invalid-value)
      (initiate-get-text-stat 'filter 'lyskom-filter-text-p-2 text-no)
      ;;
      ;; Block until done
      ;;
      (while (eq lyskom-filter-hack 'invalid-value)
        (lyskom-accept-process-output))
      lyskom-filter-hack)))
    
(defun lyskom-filter-text-p-2 (text-stat)
  (if (null text-stat)
      (setq lyskom-filter-hack nil)
    (progn
      ;;
      ;; Collect information from the server
      ;;
      (lyskom-collect 'filter)
      
      ;;
      ;; Get the conf-stat of the author of the text
      ;; Get the text body
      ;;
      
      (initiate-get-conf-stat 'filter nil (text-stat->author text-stat))
      (initiate-get-text 'filter nil (text-stat->text-no text-stat))
      
      ;;
      ;; Get the conf-stat of the recipients
      ;;
      
      (lyskom-traverse
          misc
          (text-stat->misc-info-list text-stat)
        (let ((type (misc-info->type misc)))
          (if (memq type lyskom-recpt-types-list)
              (initiate-get-conf-stat 'filter 
                                      nil
                                      (misc-info->recipient-no misc)))))
      
      ;;
      ;; Use the results
      ;;
      
      (lyskom-use 'filter 'lyskom-filter-text-p-3 text-stat))))
    
(defun lyskom-filter-text-p-3 (author text &rest data)
  (if (null text)
      (setq lyskom-filter-hack nil)
    (let (subject text-stat)
      
      ;;
      ;; Extract the text-stat which is the last element of data
      ;; Next shorten the list in data to exclude the text-stat.
      ;;
      
      (setq text-stat (elt data (- (length data) 1)))
      (if (= (length data) 1)
	  (setq data nil)
	(rplacd (nthcdr (- (length data) 2) data) nil))
      
      ;;
      ;; Extract the subject
      ;;
      
      (let ((str (text->decoded-text-mass text text-stat)))
        (cond ((string-match "\n" str)
               (setq subject (substring str 0 (match-beginning 0))))
              (t (setq subject ""))))
      
      ;;
      ;; Do the checking
      ;;
      
      (setq lyskom-filter-hack
	    (lyskom-check-filter-list text-stat
				      author
				      data
				      subject
                                      (text->decoded-text-mass text text-stat)
				      lyskom-filter-list)))))

(defun lyskom-check-filter-list (text-stat
                                 author
                                 recipient-list
                                 subject
                                 text
                                 filter-list)
  (let (tmp)
    (while filter-list
      (condition-case nil
          (if (functionp (filter->function (car filter-list)))
              (setq tmp (funcall (filter->function (car filter-list))
                                 (car filter-list)
                                 author
                                 recipient-list
                                 subject
                                 text-stat
                                 text)))
        (error nil))
      (if tmp
          (setq filter-list nil)
        (setq filter-list (cdr filter-list))))
    tmp))




;;;========================================
;;;  The filter compiler.
;;;


(defmacro lyskom-filter-is-member (testfn arg list selector)
  `(let (found 
         (objlist ,list))
     (while (and objlist (not found))
       (and (,testfn ,arg (,selector (car objlist)))
            (setq found t))
       (setq objlist (cdr objlist)))
     found))


(defun lyskom-create-compile-filter-function (pattern)
  (if (null pattern)
      (byte-compile 
       '(lambda (filter author recipient-list subject text-stat text) nil))
    (byte-compile (lyskom-create-filter-function pattern))))


(defun lyskom-create-filter-function (pattern)
  `(lambda (filter author recipient-list subject text-stat text)
     ,(cons 'and (lyskom-create-filter-function-body pattern))))

(defun lyskom-create-filter-function-body (pattern)
  (let (inverse)
    (cond
     ;;
     ;; End of pattern
     ;; 
     
     ((null pattern) '((filter->attribute filter 'action)))

     ;;
     ;; Bad pattern
     ;;
     
     ((or (not (listp pattern))
          (not (listp (car pattern)))) 
      (lyskom-error 
       "%s"
       (lyskom-get-string 'filter-error-specification)))
     
     ;;
     ;; Assume valid pattern
     ;;
     
     (t
      (let ((key (car (car pattern)))
            (args (cdr (car pattern)))
            (form nil))
        (if (eq key 'not)
            (if (not (listp args))
                (lyskom-error "%s" (lyskom-get-string 'filter-error-bad-not))
              (setq key (car args) args (cdr args) inverse t)))

        (setq form
              (cond 
               ((eq key 'author)
                (lyskom-filter-check-args 'stringp args)
                `(and author (string-match ,(regexp-quote args)
                                           (conf-stat->name author))))

               ((eq key 'author-re)
                (lyskom-filter-check-args 'regexpp args)
                `(and author (string-match ,args
                                           (conf-stat->name author))))

               ((eq key 'author-no)
                (lyskom-filter-check-args 'integerp args)
                `(and author (= ,args (conf-stat->conf-no author))))

               ((eq key 'recipient)
                (lyskom-filter-check-args 'stringp args)
                `(lyskom-filter-is-member
                    string-match
                    ,(regexp-quote args)
                    recipient-list
                    conf-stat->name))
               ((eq key 'recipient-re)
                (lyskom-filter-check-args 'regexpp args)
                `(lyskom-filter-is-member
                    string-match
                    ,args
                    recipient-list
                    conf-stat->name))
               ((eq key 'recipient-no)
                (lyskom-filter-check-args 'integerp args)
                `(lyskom-filter-is-member
                    =
                    ,args
                    recipient-list
                    conf-stat->conf-no))

               ((eq key 'subject)
                (lyskom-filter-check-args 'stringp args)
                `(string-match ,(regexp-quote args)
                                 subject))
               ((eq key 'subject-re)
                (lyskom-filter-check-args 'regexpp args)
                `(string-match ,args subject))

               ((eq key 'text)
                (lyskom-filter-check-args 'stringp args)
                `(string-match ,(regexp-quote args) text))
               ((eq key 'text-re)
                (lyskom-filter-check-args 'regexpp args)
                `(string-match ,args text))
               (t (lyskom-error 
                   (lyskom-get-string 'filter-error-unknown-key)
                   key
                   ))))
        
        (if inverse (setq form (list 'not form)))
        (cons form (lyskom-create-filter-function-body (cdr pattern))))))))

      

(defun lyskom-filter-check-args (fn arg)
  (if (not (funcall fn arg))
      (lyskom-error (lyskom-get-string 'filter-error-key-arg)
                    fn arg)))




;;; ============================================================
;;; lyskom-filter-prompt
;;;
;;; Print a notice that a text has been filtered. 
;;;

(defun lyskom-filter-prompt (text-no prompt)
  (setq lyskom-filter-hack t)
  (let ((text-stat (blocking-do 'get-text-stat text-no))
	(subject nil))
    (if text-stat
	(blocking-do-multiple 
            ((text (get-text text-no))
             (conf-stat (get-conf-stat 
                         (text-stat->author text-stat))))
          (if text
              (let ((str (text->decoded-text-mass text text-stat)))
                (setq subject
                      (if (string-match "\n" str)
                          (substring str 0 (match-beginning 0))
                        str))
                (lyskom-format-insert prompt
                                      text-stat
                                      subject
                                      (or conf-stat
                                          (text-stat->author text-stat)))
                (lyskom-scroll))))))
  (setq lyskom-filter-hack nil))



;;;========================================
;;; User functions and support functions
;;;

(defun lyskom-add-filter (filter)
  "Add the filter FILTER to the LysKOM filtering mechanism."
  (if (filter->attribute filter 'expire)
      (setq kom-session-filter-list (cons filter
                                          kom-session-filter-list))
    (progn
      (setq kom-permanent-filter-list (cons filter kom-permanent-filter-list))
      (lyskom-save-options (current-buffer)
                           (lyskom-get-string 'filter-edit-saving)
                           (lyskom-get-string 'filter-edit-saving-done)
                           (lyskom-get-string 'filter-edit-saving-error))))
  (setq lyskom-filter-list
        (cons filter lyskom-filter-list)))


(defun lyskom-filter-read-action ()
  "Read a filter action from the minibuffer, returning its symbol"
  (let ((completion-ignore-case t))
    (car
     (rassoc
      (lyskom-completing-read (lyskom-get-string 'filter-action)
                              (lyskom-maybe-frob-completion-table
                               (lyskom-reverse-pairs 
                                lyskom-filter-actions))
                              nil
                              nil
                              (cdr (car lyskom-filter-actions))
                              t)
      lyskom-filter-actions))))


(defun lyskom-filter-read-permanent ()
  "Ask the user is a filter is permanent and return t in this case.
Otherwise return nil."
  (lyskom-j-or-n-p (lyskom-get-string 'filter-permanent)))



;;;========================================
;;; Filtrera ärende --- Filter subject
;;; 

(def-kom-command kom-filter-subject (&optional subject)
  "Interactively filter a subject. This creates a permanent or temporary
filter for a single subject in one conference or all conferences.

An alternative to this is `kom-super-jump'.

To change existing filters, use `kom-filter-edit'."
  (interactive)
  (when (not (eq 0 lyskom-current-conf))
    (let* (conf perm filter action)
      (setq subject (lyskom-read-from-minibuffer 
                     (lyskom-get-string 'filter-subject)
                     (or subject lyskom-current-subject)))
      (setq filter (cons (cons 'subject subject) filter))
      (setq conf (lyskom-read-conf-no 'filter-in-conf
                                      '(all) t nil t))
      (if (not (eq conf 0))
          (setq filter (cons (cons 'recipient-no conf) filter)))
      (setq action (lyskom-filter-read-action))
      (setq perm (lyskom-filter-read-permanent))
	      
      (lyskom-add-filter
       (lyskom-make-filter filter
                    (list (cons 'action action)
                          (cons 'expire (not perm))))))))
  

     
;;;========================================
;;; Filtrera författare --- Filter author
;;;


(def-kom-command kom-filter-author ()
  "Interactively filter an author. This creates a permanent or
temporary filter on a single author in one conference or all
conferences.

To change existing filters, use `kom-filter-edit'."
  (interactive)
  (let (author conf filter action permanent)
    (setq author (lyskom-read-conf-no 'filter-author '(pers) t nil t))
    (if (not (eq author 0))
        (setq filter (cons (cons 'author-no author) filter)))
    (setq conf (lyskom-read-conf-no 'filter-in-conf
                                    '(all) t nil t))
    (if (not (eq conf 0))
        (setq filter (cons (cons 'recipient-no conf) filter)))
    (setq action (lyskom-filter-read-action))
    (setq permanent (lyskom-filter-read-permanent))
    (lyskom-add-filter
     (lyskom-make-filter filter
                  (list (cons 'action action)
                        (cons 'expire (not permanent)))))))

(def-kom-command kom-filter-recipient ()
  "Interactively filter a recipient. This creates a permanent or
temporary filter on a particular recipient.

To change existing filters, use `kom-filter-edit'."
  (interactive)
  (let ((conf-no (lyskom-read-conf-no 'filter-recipient
                                      '(all) nil nil t))
        (action (lyskom-filter-read-action))
        (permanent (lyskom-filter-read-permanent)))
    (cond ((and conf-no (not (zerop conf-no)))
           (lyskom-add-filter
            (lyskom-make-filter (list (cons 'recipient-no conf-no))
                         (list (cons 'action action)
                               (cons 'expire (not permanent)))))
           ))))




;;;============================================================
;;;
;;; Superhoppa
;;;

(def-kom-command kom-super-jump (text-no)
  "Skip all texts and comments that share the subject and recipient of
the selected text. This creates a temporary filter on the subject of
the selected text in one of its recipients. The recipient is selected
automatically: if the current conference is a recipient, then filter
in that conference. Otherwise filter in the first recipient that the
user is a member of.

To change existing filters, use `kom-filter-edit'.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'super-jump-q)))
  (if (or (null lyskom-current-text)
          (zerop lyskom-current-text))
      (lyskom-insert-string 'have-to-read)
    (let ((text-stat (blocking-do 'get-text-stat lyskom-current-text))
          (all-recipients nil)
          (recipients-member nil)
          (cc-recipients-member nil)
          (bcc-recipients-member nil)
          (recipients-nonmember nil)
          (cc-recipients-nonmember nil)
          (bcc-recipients-nonmember nil)
          (filter-recipient nil))

      ;; Extract recipients into different lists
      (lyskom-traverse misc
                       (text-stat->misc-info-list text-stat)
        (when (memq (misc-info->type misc) lyskom-recpt-types-list)
          (let* ((conf-no (misc-info->recipient-no misc))
                 (symbol
                  (if (lyskom-get-membership conf-no)
                      (cond ((eq (misc-info->type misc) 'RECPT)
                             'recipients-member)
                            ((eq (misc-info->type misc) 'CC-RECPT)
                             'cc-recipients-member)
                            ((eq (misc-info->type misc) 'BCC-RECPT)
                             'bcc-recipients-member))
                    (cond ((eq (misc-info->type misc) 'RECPT)
                           'recipients-nonmember)
                          ((eq (misc-info->type misc) 'CC-RECPT)
                           'cc-recipients-nonmember)
                          ((eq (misc-info->type misc) 'BCC-RECPT)
                           'bcc-recipients-nonmember)))))
            (set symbol (cons conf-no (symbol-value symbol)))
            (setq all-recipients (cons conf-no all-recipients)))))

      ;; Get the recipient to filter in
      (setq filter-recipient 
            (or (and (memq lyskom-current-conf all-recipients) lyskom-current-conf)
                (car (nreverse recipients-member))
                (car (nreverse cc-recipients-member))
                (car (nreverse bcc-recipients-member))
                (car (nreverse recipients-nonmember))
                (car (nreverse cc-recipients-nonmember))
                (car (nreverse bcc-recipients-nonmember))))

      (if (null filter-recipient)
          (lyskom-insert-string 'no-recipient)

        (let ((text lyskom-current-subject))
          (when (string-match "^\\s-*$" lyskom-current-subject) (setq text ""))

          (lyskom-add-filter
           (lyskom-make-filter (list 
                         (cons 'subject-re 
                               (concat (if (string-equal text "") "^" "")
                                       "\\([rR][eE]: *\\|[Ff][Ww][Dd]: *\\)*"
                                       (replace-in-string
                                        (regexp-quote text)
                                        "[ \t]+" "[ \t]+")
                                       (if (string-equal text "") "$" "")))
                         (cons 'recipient-no filter-recipient))
                        (list (cons 'action 'skip-tree)
                              (cons 'expire t))))
          (lyskom-format-insert 'super-jump
                                (copy-sequence lyskom-current-subject)
                                filter-recipient))))))




;;;============================================================
;;;
;;; Filtrera text
;;;

(def-kom-command kom-filter-text (&optional text)
  "Interactively filter on text contents. This creates a permanent or
temporary filter on the contents of texts in all conferences or in a
single conference.

To change existing filters, use `kom-filter-edit'."
  (interactive)
  (when (not (eq 0 lyskom-current-conf))
    (let ((conf nil)
          (action nil)
          (perm nil)
          (filter nil))
      (setq text 
            (lyskom-read-from-minibuffer (lyskom-get-string 
                                          'filter-which-text)
                                         (or text "")))
      (setq filter (cons (cons 'text text) filter))
      (setq conf (lyskom-read-conf-no 'filter-in-conf
                                      '(all) t nil t))
      (if (not (eq conf 0))
          (setq filter (cons (cons 'recipient-no conf) filter)))
      (setq action (lyskom-filter-read-action))
      (setq perm (lyskom-filter-read-permanent))
      (lyskom-add-filter
       (lyskom-make-filter filter
                    (list (cons 'action action)
                          (cons 'expire (not perm))))))))


;;;============================================================
;;; Lista filter                         (kom-list-filters)
;;;
;;; Author: David Byers
;;; Calls internal functions in filter-edit mode. This may or
;;; may not be a good idea, but it works...

(def-kom-command kom-list-filters ()
  "Display all filters. This lists all filters in the LysKOM buffer.

To change existing filters, use `kom-filter-edit'."
  (interactive)
  (let ((filters lyskom-filter-list))
    (goto-char (point-max))
    (if (null filters)
        (lyskom-insert (lyskom-get-string 'no-filters))
      (progn
        (lyskom-insert (lyskom-get-string 'view-filters-header))
        (while filters
          (goto-char (point-max))
          (lyskom-format-filter-pattern (car filters))
          (setq filters (cdr filters)))
        (lyskom-insert (lyskom-get-string 'view-filters-footer))))))

;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;;============================================================
;;;;============================================================
;;;;
;;;; File: filter-edit.el
;;;;
;;;; This file contains the filter editor
;;;;


(defvar filter-edit-currently-edited-filter-entry-list nil
  "List of filters currently being edited in a filter editor")
(defvar filter-edit-change-flag nil)
(defvar filter-edit-filter-list nil)
(defvar filter-edit-list-start nil)
(defvar filter-edit-list-end nil)

(defun lyskom-copy-filter-list (l)
  "Copy the filter list L"
  (cond ((null l) nil)
        (t (cons (lyskom-copy-filter (car l))
                 (lyskom-copy-filter-list (cdr l))))))

(defun lyskom-reverse-pairs (l)
  "Reverse the pairs in the assoc list L"
  (mapcar (function
           (lambda (e) (cons (cdr e) (car e)))) l))

;;;========================================
;;; Data types
;;;
;;; filter-entry
;;; Entry in the filter-entry list
;;;
;;; A filter entry is a vector consisting of
;;;
;;; [START END LINES PATTERN]
;;;
;;; START is the starting position (mark) in the buffer
;;; END is the ending position (mark) in the buffer
;;; LINES is a list of starting positions (marks) of the lines
;;; PATTERN is the filter displayed (type filter)
;;;

(def-komtype filter-entry 
  ((start :read-only t)
   (end   :read-only t)
   lines
   (filter :read-only t)))


;;;============================================================
;;; Verification functions
;;;

(defun lyskom-verify-filter-list (filter)
  "Return non-nil if FILTER is a valid filter list"
  (cond ((null filter) t)
        ((not (listp filter)) nil)
        ((not (lyskom-filter-p (car filter))) nil)
        (t (and (lyskom-verify-filter-pattern 
                 (filter->pattern (car filter)))
                (lyskom-verify-filter-list (cdr filter))))))

(defun lyskom-verify-filter-pattern (filter)
  "Return non-nil if FILTER is a valid filter pattern"
  (cond ((null filter) nil)
        ((and (= (length filter) 1)
              (symbolp (car filter))) t)
        ((not (listp (car filter))) nil)
        ((eq 'not (car (car filter)))
         (lyskom-verify-filter-pattern
          (cons (cdr (car filter)) (cdr filter))))
        ((memq (car (car filter))
		 '(author-re recipient-re subject-re text-re))
         (and
          (lyskom-filter-verify-regexp (cdr (car filter)))
          (lyskom-verify-filter-pattern (cdr filter))))
         
        ((memq (car (car filter)) '(author subject recipient text))
         (and
          (stringp (cdr (car filter)))
          (lyskom-verify-filter-pattern (cdr filter))))
        ((memq (car (car filter)) '(author-no recipient-no))
         (and
          (integerp (cdr (car filter)))
          (lyskom-verify-filter-pattern (cdr filter))))
        (t nil)))

(defun lyskom-filter-verify-regexp (re)
  "Return t if RE is a valid regexp."
  (condition-case nil
      (progn
        (string-match re "teststring")
        t)
    (error nil)))

;;;========================================
;;; Locator functions
;;;

(defun lyskom-filter-edit-lineno (where entry)
  "Return the line number at WHERE in the entry ENTRY.
-1 means the header. 0 is the first line. Any number higher than the number of lines means outside the pattern."
  (let* ((lines (append (filter-entry->lines entry)
                        (cons (filter-entry->end entry) nil)))
         (line -1)
         (current-line (length lines)))
    (if (>= where (filter-entry->start entry))
        (while lines
          (if (< where (car lines))
              (progn
                (setq current-line line)
                (setq lines nil))
            (progn
              (setq lines (cdr lines))
              (setq line (1+ line))))))
    current-line))


(defun lyskom-filter-edit-locate (where)
  "Locate the entry at character position WHERE.
nil means WHERE is not in any entry."
  (let ((entry-no (lyskom-filter-edit-locate-no where)))
    (if (= -1 entry-no)
        nil
      (elt filter-edit-currently-edited-filter-entry-list entry-no))))


(defun lyskom-filter-edit-locate-no (where)
  "Find the filter-edit-currently-edited-filter-entry-list index of the entry
covering the character position WHERE. -1 means WHERE is not covered y any
entry."
  (let ((i 0)
        (entry-no -1)
        (l filter-edit-currently-edited-filter-entry-list))
    (while l
      (if (and (>= where (filter-entry->start (car l)))
               (<= where (filter-entry->end (car l))))
          (progn (setq entry-no i)
                 (setq l nil))
        (progn (setq i (1+ i))
               (setq l (cdr l)))))
    entry-no))


;;;========================================
;;; Formatting functions
;;;

(defun lyskom-format-filter-list (filters)
  "Format the filter list FILTERS and insert the result into the
current buffer. The buffer variable 
FILTER-EDIT-CURRENTLY-EDITED-FILTER-ENTRY-LIST is also updated."
  (let ((inhibit-read-only t))
    (setq filter-edit-currently-edited-filter-entry-list nil)
    (lyskom-format-filter-list-2 filters)
    (setq filter-edit-currently-edited-filter-entry-list
	  (nreverse filter-edit-currently-edited-filter-entry-list))))


(defun lyskom-format-filter-list-2 (filters)
  "Format the filter list FILTERS and insert the result into the
current buffer."
  (cond ((null filters) nil)
        (t (setq filter-edit-currently-edited-filter-entry-list
                 (cons (lyskom-format-filter-pattern (car filters))
                       filter-edit-currently-edited-filter-entry-list))
           (lyskom-format-filter-list-2 (cdr filters)))))

  

(defun lyskom-format-filter-pattern (pat)
  "Format and insert the filter pattern PAT into the current buffer.
Returns an filter-entry structure representing the entry."
  (let ((inhibit-read-only t)
        start end lines)
   
    ;;
    ;; Insert text representation
    ;;

    (setq start (point-marker))
    (lyskom-filter-format-entry-header pat t)
    (setq lines (lyskom-format-filter-pattern-2 (filter->pattern pat) nil))
    (setq end (point-marker))
    (insert "\n")
    (lyskom-create-filter-entry start end lines pat)))


(defun lyskom-format-filter-pattern-2 (pat lines &optional neg)
  "Format and insert the body of the filter pattern PAT into the 
current buffer. Accumulate line starts in LINES.
If NEG is non-nil, the first line will be negated."
  (cond ((or (null pat)) (nreverse lines))
        ((eq (car (car pat)) 'not)
         (lyskom-format-filter-pattern-2 (cons (cdr (car pat))
                                               (cdr pat))
                                         lines
                                         t))
        (t
         (setq lines (cons (point-marker) lines))
         (lyskom-format-filter-pattern-insert pat neg)
         (lyskom-format-filter-pattern-2 (cdr pat) lines))))



(defun lyskom-format-filter-pattern-insert (pat neg)
  "Format and insert the first pattern in PAT.
If NEG is non-nil, format the negation."
  (let (tmp)
    (insert
     (format "    %s %s %S"
	     (cdr (assoc (car (car pat)) lyskom-filter-what))
	     (if neg
		 " != "
	       " = ")
	     (cdr (car pat))))
    (cond ((memq (car (car pat)) '(recipient-no author-no))
	   (save-excursion
	     (if (boundp 'lyskom-buffer)
		 (set-buffer lyskom-buffer))
	     (setq tmp (blocking-do 'get-conf-stat (cdr (car pat)))))
	   (insert (format "   <%s>" (conf-stat->name tmp)))))
    (insert "\n")))
	


(defun lyskom-filter-format-entry-header (filter &optional newline action)
  "Format the header of a filter entry. FILTER is the filter to format. 
If NEWLINE is non-nil, insert a newline after the header."
  (let (permanent)
    (setq action (filter->attribute filter 'action)
	  permanent (null (filter->attribute filter 'expire)))
    (insert 
     (format "--- %s %s"
	     (cdr 
	      (assq action lyskom-filter-actions		    ))
	     (lyskom-get-string
	      (if permanent 'permanent 'temporary))))
    (if newline (insert "\n"))))


;;;========================================
;;; User commands
;;;

(defun lyskom-filter-edit-next-pattern (arg)
  "Move to the ARGth next pattern line in the current buffer."
  (interactive "p")
  (if (null arg) (setq arg 1))
  (if (< arg 0) 
      (lyskom-filter-edit-prev-entry (- arg))
    (while (/= arg 0)
      (let ((entry-no (lyskom-filter-edit-locate-no (point)))
            tmp)
        (if (= -1 entry-no)
            (if (car filter-edit-currently-edited-filter-entry-list)
                (goto-char 
		 (filter-entry->start 
		  (car filter-edit-currently-edited-filter-entry-list)))
              (lyskom-error (lyskom-get-string 'filter-edit-empty-list)))
          (let* ((entry 
		  (elt filter-edit-currently-edited-filter-entry-list
		       entry-no))
                 (lineno (lyskom-filter-edit-lineno (point) entry)))
            (cond 
             ((= lineno -1)
              (if (filter-entry->lines entry)
                  (setq tmp (car (filter-entry->lines entry)))
                (progn
                  (setq tmp 
                        (elt filter-edit-currently-edited-filter-entry-list
			     (1+ entry-no)))
                  (if tmp 
                      (setq tmp (filter-entry->start tmp))
                    (lyskom-error (lyskom-get-string 'filter-edit-end-of-list))))))

              ((= lineno (1- (length (filter-entry->lines entry))))
               (setq tmp (elt filter-edit-currently-edited-filter-entry-list
			      (1+ entry-no)))
               (if tmp 
                   (setq tmp (filter-entry->start tmp))
                 (lyskom-error (lyskom-get-string 'filter-edit-end-of-list))))
              (t (setq tmp (elt (filter-entry->lines entry) (1+ lineno)))))
        (goto-char tmp)
        (setq arg (1- arg))))))))


(defun lyskom-filter-edit-prev-pattern (arg)
  "Move to the ARGth previous pattern line in the current buffer."
  (interactive "p")
  (if (null arg) (setq arg 1))
  (if (< arg 0) 
      (lyskom-filter-edit-next-entry (- arg))
    (while (/= arg 0)
      (let ((entry-no (lyskom-filter-edit-locate-no (point)))
            tmp)
        (if (= -1 entry-no)
            (if (car filter-edit-currently-edited-filter-entry-list)
                (goto-char
		 (filter-entry->start
		  (car filter-edit-currently-edited-filter-entry-list)))
              (lyskom-error (lyskom-get-string 'filter-edit-empty-list)))
          (let* ((entry 
		  (elt filter-edit-currently-edited-filter-entry-list
		       entry-no))
                 (lineno (lyskom-filter-edit-lineno (point) entry)))
            (cond 
             ((= lineno -1)
              (if (= 0 entry-no)
                  (lyskom-error (lyskom-get-string 'filter-edit-start-of-list))
                (progn
                  (setq entry 
			(elt filter-edit-currently-edited-filter-entry-list
			     (1- entry-no)))
                  (setq tmp (car 
                             (nthcdr (1- (length (filter-entry->lines entry)))
                                     (filter-entry->lines entry))))
                  (if (null tmp)
                      (setq tmp (filter-entry->start entry))))))

              ((= lineno 0)
               (setq tmp (filter-entry->start entry)))

              (t (setq tmp (elt (filter-entry->lines entry) (1- lineno)))))
        (goto-char tmp)
        (setq arg (1- arg))))))))



(defun lyskom-filter-edit-next-entry (arg)
  "Move to the ARGth next entry in the current buffer."
  (interactive "p")
  (if (null arg) (setq arg 1))
  (if (< arg 0) 
      (lyskom-filter-edit-prev-entry (- arg))
    (while (/= 0 arg)
      (let ((entry-no (lyskom-filter-edit-locate-no (point))))
        (if (= -1 entry-no)
            (if (car filter-edit-currently-edited-filter-entry-list)
                (goto-char 
		 (filter-entry->start
		  (car filter-edit-currently-edited-filter-entry-list)))
              (lyskom-error (lyskom-get-string 'filter-edit-empty-list)))
          (let ((tmp (elt filter-edit-currently-edited-filter-entry-list (1+ entry-no))))
            (if tmp
                (progn
                  (goto-char (filter-entry->start tmp))
                  (setq arg (1- arg)))
              (lyskom-error (lyskom-get-string 'filter-edit-end-of-list)))))))))


(defun lyskom-filter-edit-prev-entry (arg &optional noerror)
  "Move to the ARGth previous entry in the current buffer."
  (interactive "p")
  (not
   (catch 'fail
     (if (null arg) (setq arg 1))
     (if (< arg 0) 
         (lyskom-filter-edit-next-entry (- arg))
       (while (/= 0 arg)
         (let ((entry-no (lyskom-filter-edit-locate-no (point))))
           (if (= -1 entry-no)
               (if (car filter-edit-currently-edited-filter-entry-list)
                   (goto-char (filter-entry->start (car filter-edit-currently-edited-filter-entry-list)))
                 (if noerror (throw 'fail t) 
                   (lyskom-error (lyskom-get-string 'filter-edit-empty-list)))))
           (if (= 0 entry-no)
               (if noerror (throw 'fail t) 
                 (lyskom-error (lyskom-get-string 'filter-edit-start-of-list)))
             (let ((tmp (elt filter-edit-currently-edited-filter-entry-list (1- entry-no))))
               (setq arg (1- arg))
               (goto-char (filter-entry->start tmp))))))))))


(defun lyskom-filter-edit-beginning-of-list ()
  "Move to the first entry in the list"
  (interactive)
  (if (null filter-edit-currently-edited-filter-entry-list)
      (lyskom-error (lyskom-get-string 'filter-edit-empty-list))
    (progn
      (push-mark)
      (goto-char (filter-entry->start (car filter-edit-currently-edited-filter-entry-list))))))


(defun lyskom-filter-edit-end-of-list ()
  "Move to the last entry in the list"
  (interactive)
  (if (null filter-edit-currently-edited-filter-entry-list)
      (lyskom-error (lyskom-get-string 'filter-edit-empty-list))
    (let ((entry (elt filter-edit-currently-edited-filter-entry-list (1- (length filter-edit-currently-edited-filter-entry-list)))))
      (push-mark)
      (goto-char (or (elt (filter-entry->lines entry)
                          (1- (length (filter-entry->lines entry))))
                     (filter-entry->start entry))))))



(defun lyskom-filter-edit-insert-entry ()
  "Add an entry to the end of the list"
  (interactive)
  (let ((inhibit-read-only t)
        (completion-ignore-case t)
        (rev-actions 
         (lyskom-reverse-pairs lyskom-filter-actions))
        action permanent filter start end entry)
    (if filter-edit-currently-edited-filter-entry-list
        (progn
          (goto-char (filter-entry->end (elt filter-edit-currently-edited-filter-entry-list
                                             (1- (length filter-edit-currently-edited-filter-entry-list)))))
          (insert "\n"))
      (goto-char filter-edit-list-end))

      (setq action (lyskom-completing-read (lyskom-get-string 'filter-edit-filter-how)
                                           (lyskom-maybe-frob-completion-table
                                            rev-actions)
                                           nil
                                           t))
      (setq permanent
            (lyskom-j-or-n-p (lyskom-get-string 'filter-permanent)))
      (setq filter (lyskom-make-filter nil
                                (list (cons 'action 
                                            (cdr (assoc action rev-actions)))
                                      (cons 'expire (not permanent)))))
      (setq start (point-marker))
      (lyskom-filter-format-entry-header filter t)
      (setq end (point-marker))
      (setq entry
            (lyskom-create-filter-entry start end nil filter))
      (if filter-edit-currently-edited-filter-entry-list
          (setcdr (nthcdr (1- (length filter-edit-currently-edited-filter-entry-list)) filter-edit-currently-edited-filter-entry-list)
                  (cons entry nil))
        (setq filter-edit-currently-edited-filter-entry-list (cons entry nil)))
      (setq filter-edit-change-flag t)
      (goto-char start)))


(defun lyskom-filter-edit-insert-pattern ()
  "Add a pattern line to the current list entry."
  (interactive)
  (let ((entry-no (lyskom-filter-edit-locate-no (point))))
    (if (= -1 entry-no) 
        (lyskom-error (lyskom-get-string 'filter-edit-outside-entry))))
  (let ((what nil)
        (pred nil)
        (arg nil)
        (argstring nil)
        (lineno nil)
        (entry nil)
        (filter nil)
        (pat nil)
        (inhibit-read-only t)
        (completion-ignore-case t)
        (rev-what (lyskom-reverse-pairs lyskom-filter-what)))
    (setq what (lyskom-completing-read
                (lyskom-get-string 'filter-edit-filter-what)
		(lyskom-maybe-frob-completion-table rev-what)
                nil t))
    (setq pred 
          (lyskom-completing-read (lyskom-format 'filter-edit-insert-pred what)
			   (lyskom-maybe-frob-completion-table
			    lyskom-filter-predicate-list
			    t)
                           nil t))
    (setq argstring (lyskom-read-from-minibuffer
                     (lyskom-format 'filter-edit-insert-arg what pred)))
    (setq what (cdr (assoc what rev-what)))

    (if (not
         (cond ((memq what '(author subject text recipient))
		(setq arg argstring))
               ((memq what '(author-no recipient-no))
		(setq arg (lyskom-string-to-number argstring)))
               ((memq what '(author-re subject-re text-re recipient-re))
                (setq arg argstring)
                (lyskom-filter-verify-regexp arg))))
        (lyskom-error (lyskom-get-string 'filter-edit-bad-argument)
                       argstring))

    ;;
    ;; Build pattern
    ;;

    (setq pat 
          (cons what arg))
    (if (cdr (assoc pred (lyskom-maybe-frob-completion-table
			  lyskom-filter-predicate-list t)))
        (setq pat (cons 'not pat)))

    ;;
    ;; Locate current entry
    ;; Locate current line
    ;;
    
    (setq entry (lyskom-filter-edit-locate (point)))
    (setq lineno (lyskom-filter-edit-lineno (point) entry))
    (setq filter (filter-entry->filter entry))

    ;;
    ;; Splice the new pattern into the old pattern at the
    ;; proper position. If lineno is -1 (the cursor is not in
    ;; the pattern area, append the pattern.
    ;;

    (if (= -1 lineno)
        (setq lineno (length (filter->pattern filter))))
    
    (cond ((= lineno 0) 
           (set-filter->pattern
            filter
            (cons pat (filter->pattern filter))))
          (t (if (filter->pattern filter)
                 (setcdr (nthcdr (1- lineno)
                             (filter->pattern filter))
                     (cons pat (nthcdr lineno
                                       (filter->pattern filter))))
               (set-filter->pattern filter pat))))
    
    ;;
    ;; Update display
    ;;
    
    (if (= lineno (length (filter-entry->lines entry)))
        (goto-char (filter-entry->end entry))
      (goto-char (elt (filter-entry->lines entry) lineno)))
    
    ;;
    ;; OK, right now, POINT is on top os a marker that represents the
    ;; NEXT line. This marker must be puched forward to avoid duplicating
    ;; it.
    ;;
    
    (forward-line -1)
    (end-of-line)
    (insert "\n")
    (lyskom-format-filter-pattern-insert
     (cons
      (if (eq (car pat) 'not)
          (cdr pat) 
        pat) nil)
     (eq (car pat) 'not))
    (forward-char -1) 
    (delete-char 1)
    (beginning-of-line)
    
    ;;
    ;; Update lines list in the entry
    ;;

    (cond ((= lineno 0)
           (set-filter-entry->lines
            entry
            (cons (point-marker) (filter-entry->lines entry))))
          (t (setcdr (nthcdr (1- lineno)
                                 (filter-entry->lines entry))
                     (cons (point-marker)
                           (nthcdr lineno 
                                   (filter-entry->lines entry))))))
    (setq filter-edit-change-flag t)))



(defun lyskom-filter-edit-delete-pattern (arg)
  "Delete ARG pattern lines, starting with the one at point.
Only lines in the current entry will be deleted."
  (interactive "p")
  (let* ((inhibit-read-only t)
         (entry (lyskom-filter-edit-locate (point)))
         (filter (filter-entry->filter entry)))
    (if (null entry)
        (lyskom-error (lyskom-get-string 'filter-edit-outside-list))
      (progn
        (while (/= 0 arg)
          (let ((lineno (lyskom-filter-edit-lineno (point) entry)))
            (if (null arg) (setq arg 1))
            (if (and (> lineno -1)
                     (< lineno (length (filter-entry->lines entry))))
                (progn 
                  (setq arg (1- arg))
                  (setq lineno (lyskom-filter-edit-lineno (point) entry))
                  (delete-region
                   (save-excursion (beginning-of-line) (point))
                   (1+ (save-excursion (end-of-line) (point))))
                  (if (= lineno (1- (length (filter-entry->lines entry))))
                      (forward-line -1))
                  (if (= lineno 0)
                      (progn
                        (set-filter-entry->lines 
                         entry 
                         (cdr (filter-entry->lines entry)))
                        (set-filter->pattern 
                         filter
                         (cdr (filter->pattern filter)))
                        (setq filter-edit-change-flag t))
                    (progn
                      (setcdr (nthcdr (1- lineno) 
                                      (filter-entry->lines entry))
                              (nthcdr (1+ lineno)
                                      (filter-entry->lines entry)))
                      (setcdr (nthcdr (1- lineno)
                                      (filter->pattern
                                       (filter-entry->filter entry)))
                              (nthcdr (1+ lineno)
                                      (filter->pattern
                                       (filter-entry->filter entry))))
                      (setq filter-edit-change-flag t))))
              (lyskom-error (lyskom-get-string 'filter-edit-end-of-pattern)))))))))


(defun lyskom-filter-edit-delete-entry (arg &optional which noerror)
  "Delete ARG entries, starting with the one covering point.
If optional WHICH is non-nil, start with entry number WHICH.
If NOERROR is non-nil, return nil instead of signaling an error."
  (interactive "p")
  (let ((inhibit-read-only t))
  (not
   (catch 'fail
     (if (null arg) (setq arg 1))
     (while (/= 0 arg)
       (let* ((entry-no (or which
                            (lyskom-filter-edit-locate-no (point))))
              (entry nil))
         (if (= -1 entry-no) 
             (if noerror
                 (throw 'fail t)
               (lyskom-error (lyskom-get-string 'filter-edit-end-of-list))))
         (setq entry (elt filter-edit-currently-edited-filter-entry-list
			  entry-no))
         (delete-region (filter-entry->start entry)
                        (1+ (filter-entry->end entry)))
         (cond ((= 0 entry-no)
                (setq filter-edit-currently-edited-filter-entry-list
		      (cdr filter-edit-currently-edited-filter-entry-list))
                (setq filter-edit-change-flag t))
               (t (setcdr
		   (nthcdr (1- entry-no)
			   filter-edit-currently-edited-filter-entry-list)
		   (nthcdr (1+ entry-no)
			   filter-edit-currently-edited-filter-entry-list))
                  (setq filter-edit-change-flag t)))
         (if (= entry-no
		(length filter-edit-currently-edited-filter-entry-list))
             (progn (if (lyskom-filter-edit-prev-entry 1 t)
                        (setq arg 0)
                      (throw 'fail t)))
           (setq arg (1- arg)))))))))
  
(defun lyskom-filter-edit-quit ()
  "Quit filter edit mode and ask to save changes (if any)"
  (interactive)
  (let ((save nil))
    (if filter-edit-change-flag
        (setq save (lyskom-j-or-n-p 
                    (lyskom-get-string 'filter-edit-save-p))))
    (if save
        (lyskom-filter-edit-save))
    (set-window-configuration lyskom-edit-return-to-configuration)))

(defun lyskom-filter-edit-expunge ()
  "Delete all entries that are completely empty"
  (interactive)
  (let ((e filter-edit-currently-edited-filter-entry-list)
        (index 0))
    (while e
      (if (null (filter-entry->lines (car e)))
          (lyskom-filter-edit-delete-entry 1 index t)
        (setq index (1+ index)))
      (setq e (cdr e)))))

(defun lyskom-filter-empty-patterns-p ()
  "Return non-nil if the entry list contains empty entries."
  (let ((e filter-edit-currently-edited-filter-entry-list)
        (result nil))
    (while e
      (if (null (filter-entry->lines (car e)))
          (progn (setq e nil) (setq result t))
        (setq e (cdr e))))
    result))


(defun lyskom-filter-edit-save ()
  "Save changes in filter edit mode"
  (interactive)
  (if (and 
       (lyskom-filter-empty-patterns-p)
       (lyskom-j-or-n-p
        (lyskom-get-string 'filter-edit-remove-empty)))
      (lyskom-filter-edit-expunge))

  (let ((e filter-edit-currently-edited-filter-entry-list)
        (xpermanent-list nil)
        (xtemporary-list nil))
    (while e
      (set-filter->function 
       (filter-entry->filter (car e))
       (lyskom-create-compile-filter-function
	(filter->pattern (filter-entry->filter (car e)))))
      (set (if (filter->attribute (filter-entry->filter (car e))
                                  'expire)
               'xtemporary-list
             'xpermanent-list)
           (cons (filter-entry->filter (car e))
                 (if (filter->attribute (filter-entry->filter (car e))
                                        'expire)
                     xtemporary-list
                   xpermanent-list)))
      (setq e (cdr e)))
    (lyskom-with-lyskom-buffer
      (setq lyskom-filter-list 
            (append
             (setq kom-permanent-filter-list (nreverse xpermanent-list))
             (setq kom-session-filter-list (nreverse xtemporary-list))))
      (setq filter-edit-change-flag nil)
      (lyskom-message "%s" (lyskom-get-string 'filter-edit-saving))
      (lyskom-save-options lyskom-buffer
                           (lyskom-get-string 'filter-edit-saving)
                           (lyskom-get-string 'filter-edit-saving-done)
                           (lyskom-get-string 'filter-edit-saving-error)))))


(defun lyskom-filter-edit-revert ()
  "Discard changes and restart editing"
  (interactive)
  (if (or (not filter-edit-change-flag)
          (and filter-edit-change-flag
               (lyskom-j-or-n-p (lyskom-get-string 'filter-edit-restart-p))))
      (let ((inhibit-read-only t))
        (setq filter-edit-currently-edited-filter-entry-list nil)
        (delete-region filter-edit-list-start filter-edit-list-end)
        (goto-char filter-edit-list-start)
        (insert "\n")
        (lyskom-format-filter-list (lyskom-copy-filter-list filter-edit-filter-list))
	(set-marker filter-edit-list-end (point))
        (setq filter-edit-change-flag nil)
        (lyskom-filter-edit-beginning-of-list))))


(defun lyskom-filter-edit-toggle-permanent ()
  "Toggle the permanent flag of the current entry"
  (interactive)
  (let ((entry-no (lyskom-filter-edit-locate-no (point)))
        (inhibit-read-only t))
    (if (= -1 entry-no)
        (lyskom-error (lyskom-get-string 'filter-edit-outside-entry)))
    (let ((entry (elt filter-edit-currently-edited-filter-entry-list
		      entry-no)))
      (set-filter->attribute (filter-entry->filter entry)
                             'expire
                             (not (filter->attribute 
                                   (filter-entry->filter entry)
                                   'expire)))
      (setq filter-edit-change-flag t)
      (save-excursion
        (goto-char (filter-entry->start entry))
        (delete-region (point) (save-excursion (end-of-line) (point)))
        (lyskom-filter-format-entry-header (filter-entry->filter entry))))))


(defun lyskom-filter-edit-toggle-action ()
  "Toggle the filter action of the current entry."
  (interactive)
  (let ((entry-no (lyskom-filter-edit-locate-no (point)))
        (inhibit-read-only t))
    (if (= -1 entry-no)
        (lyskom-error (lyskom-get-string 'filter-edit-outside-entry)))
    (let* ((entry (elt filter-edit-currently-edited-filter-entry-list
		       entry-no))
           (action-list lyskom-filter-actions)
           (action (car
                    (cdr (memq 
                          (assq
                            (filter->attribute (filter-entry->filter entry)
                                               'action)
                           action-list)
                          action-list))))
           (new-action (or action (car action-list))))
      (setq filter-edit-change-flag t)
      (set-filter->attribute (filter-entry->filter entry)
                             'action
                             (car new-action))
      (save-excursion
        (goto-char (filter-entry->start entry))
        (delete-region (point) (save-excursion (end-of-line) (point)))
        (lyskom-filter-format-entry-header (filter-entry->filter entry))))))


(defun lyskom-filter-edit-brief-help ()
  "Display a help message in the minibuffer."
  (interactive)
  (lyskom-message "%s" (lyskom-get-string 'filter-edit-help)))


(defun lyskom-filter-edit-mode ()
  "\\<lyskom-filter-edit-map>Mode for editing LysKOM filters.
Entry to this mode runs lyskom-filter-edit-hook

Commands:
\\[lyskom-filter-edit-prev-pattern] Moves up in the pattern list.
\\[lyskom-filter-edit-next-pattern] Moves down in the pattern list.
\\[lyskom-filter-edit-prev-entry]   Moves up one filter in the list.
\\[lyskom-filter-edit-next-entry]   Moves down one filter in the list.
\\[lyskom-filter-edit-beginning-of-list]    Moves to the beginning of the list.
\\[lyskom-filter-edit-end-of-list]    Moves to the end of the list.
\\[lyskom-filter-edit-delete-pattern]   Deletes the pattern line at point.
\\[lyskom-filter-edit-delete-entry] Deletes the filter containing point.
\\[lyskom-filter-edit-insert-pattern]   Inserts a pattern line at point.
\\[lyskom-filter-edit-insert-entry] Inserts a new filter into the list.
\\[lyskom-filter-edit-expunge]  Deletes all empty filters from the list.
\\[lyskom-filter-edit-revert]   Discards all changes to the list.
\\[lyskom-filter-edit-toggle-action]    Toggles the filter action of the filter containing point.
\\[lyskom-filter-edit-toggle-permanent] Toggles between permanent and temporary.
\\[lyskom-filter-edit-save] Saves the filters.
\\[lyskom-filter-edit-quit] Quit and return to LysKOM.

All key bindings:
\\{lyskom-filter-edit-map}
"
  (interactive)
  (kill-all-local-variables)
  (make-local-variable 'filter-edit-currently-edited-filter-entry-list)
  (make-local-variable 'filter-edit-change-flag)
  (make-local-variable 'filter-edit-filter-list)
  (make-local-variable 'filter-edit-list-start)
  (make-local-variable 'filter-edit-list-end)
  (make-local-variable 'lyskom-buffer)
  (make-local-variable 'lyskom-edit-return-to-configuration)
  (setq buffer-read-only t)
  (setq filter-edit-change-flag nil)
  (buffer-disable-undo (current-buffer))
  (lyskom-use-local-map lyskom-filter-edit-map)
  (setq mode-name "LysKOM Filter Edit")
  (setq major-mode 'lyskom-filter-edit-mode)
  (setq local-abbrev-table 'lyskom-filter-edit-abbrev-table)
  (run-hooks 'lyskom-filter-edit-hook))


;;;========================================
;;; LysKOM command to start editing
;;;

(defun kom-filter-edit ()
  "Interactively edit text filters. This command starts a special mode
in which it is possble to create and alter filters. Use this to remove
undesired filters and to create complex filters."
  (interactive)
  (let ((buf (current-buffer))
        (filters lyskom-filter-list)
        (server-name lyskom-server-name)
        (curwin (current-window-configuration)))
    (lyskom-display-buffer
     (lyskom-get-buffer-create 'edit-filters
                               (concat (buffer-name buf)
                                       "-"
                                       (lyskom-get-string 
                                        'filter-edit-buffer-name))
                               t))
    (let ((inhibit-read-only t))
      (erase-buffer))
    (lyskom-filter-edit-mode)
    (setq lyskom-buffer buf)
    (let ((inhibit-read-only t))
      (setq filter-edit-filter-list filters)
      (insert (format (lyskom-get-string 'filter-edit-header)
                      server-name))
      (setq filter-edit-list-start (point-marker))
      (insert "\n")
      (lyskom-format-filter-list (lyskom-copy-filter-list filter-edit-filter-list))
      (setq filter-edit-list-end (point-max-marker))
      (insert (make-string (1- (window-width)) ?=)
	      (format (lyskom-get-string 'filter-edit-footer))))
    (setq lyskom-edit-return-to-configuration curwin)
    (lyskom-filter-edit-beginning-of-list)))


;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: lyskom-buttons.el
;;;; Author: David Byers
;;;;
;;;;


(lyskom-external-function glyph-property)
(lyskom-external-function widget-at)
(lyskom-external-function widget-get)
(lyskom-external-function w3-widget-button-click)
(lyskom-external-function w3-popup-menu)
(lyskom-external-function Info-goto-node)
(lyskom-external-function term-char-mode)
(lyskom-external-function w3m)


(defun lyskom-add-button-action (type text func)
  "Add a new action to the popup menu for a class of objects.
Arguments are TYPE, the type of object to adjust, TEXT the menu text
for the action and FUNC, the function to call when the action is
selected. By default TYPE may be any one of text, conf, pers or url 
although users can add other types.

FUNC must be a function with three arguments, BUFFER, ARGUMENT and
TEXT.  BUFFER is the LysKOM buffer that the command should use, TEXT
is the text of the selected button and ARGUMENT is the data argument
associated with the object. For button type text it is a text
number. For types conf and pers it is the conference number for the
object. For URLs it is the text of the URL (a string) or NIL. For
other (user-defined) types, it is a string.

For more information on button types and arguments, see the
documentation for the variable lyskom-text-buttons."
  (nconc (nth 3 (assq type lyskom-button-actions))
	 (list (cons text func))))


(defun kom-previous-link (num)
  "Move the cursor to the previous active area in the LysKOM buffer."
  (interactive "p")
  (lyskom-prev-area num 'lyskom-button))

(defun kom-next-link (num)
  "Move the cursor to the next active area in the LysKOM buffer."
  (interactive "p")
  (lyskom-next-area num 'lyskom-button))


(defun kom-button-press-or-self-insert-command ()
  "Simulate a mouse button press at point, if there is a button."
  (interactive)
  (if (get-text-property (point) 'lyskom-button)
      (call-interactively 'kom-button-press)
    (call-interactively 'self-insert-command)))

(defun kom-button-press ()
  "Simulate a mouse button press at point."
  (interactive)
  (let* ((type  (get-text-property (point) 'lyskom-button-type))
         (data  (assq type lyskom-button-actions))
         (hint (get-text-property (point) 'lyskom-button-hint))
         (act  (or (and kom-use-button-hints hint)
                   (and data (elt data 2)))))
    (cond ((null data) (lyskom-button-press (point)))
          ((null act) (lyskom-button-menu (point) 'key))
          (t (lyskom-button-press (point))))))

(defun kom-menu-button-press-or-self-insert-command ()
  "Simulate a menu mouse button press at point, if there is a button."
  (interactive)
  (if (get-text-property (point) 'lyskom-button)
      (call-interactively 'kom-menu-button-press)
    (call-interactively 'self-insert-command)))


(defun kom-menu-button-press ()
  "Simulate a menu mouse button press at point."
  (interactive)
  (lyskom-button-menu (point) 'key))

(defun kom-button-click (event &optional do-default)
  "Execute the default action of the active area under the mouse.
If optional argument do-default is non-nil, call the default binding of
this-command-keys."
  (interactive "@e")
  (let* ((pos (lyskom-event-point event))
         (glyph (lyskom-event-glyph event))
         (widget (and pos 
                      (or (and glyph (glyph-property glyph 'widget))
                          (widget-at pos))))
         (parent (and widget (widget-get widget ':parent)))
         (href (or (and widget (widget-get widget ':href))
                   (and parent (widget-get parent ':href))
                   (and widget (widget-get widget 'href))
                   (and parent (widget-get parent 'href))))
         (w3m-url (and pos (get-text-property pos 'w3m-href-anchor)))
         (type  (and pos (get-text-property pos 'lyskom-button-type)))
         (data  (and type (assq type lyskom-button-actions)))
         (hint (and pos (get-text-property pos 'lyskom-button-hint)))
         (act  (or (and kom-use-button-hints hint)
                   (and data (elt data 2)))))
    (cond (href (require 'w3)
                (w3-widget-button-click event))
          (w3m-url (require 'w3m)
               (w3m w3m-url))
          ((and do-default
                (or (null pos)
                    (null (get-text-property pos 'lyskom-button-type))))
           (let ((fn (lookup-key global-map (this-command-keys))))
             (when (commandp fn)
               (call-interactively fn))))
          ((null act) (kom-popup-menu event))
          (t (lyskom-button-press pos)))))

(defun kom-button-click-or-yank (event)
  "Execute the default action of the active area under the mouse.
If there is no active area, then do something else."
  (interactive "@e")
  (kom-button-click event t))

(defun kom-popup-menu (event)
  "Pop up a menu of actions to be taken at the active area under the mouse."
  (interactive "@e")
  (let* ((pos (lyskom-event-point event))
         (glyph (lyskom-event-glyph event))
         (widget (and pos 
                      (or (and glyph (glyph-property glyph 'widget))
                          (widget-at pos))))
         (parent (and widget (widget-get widget ':parent)))
         (href (or (and widget (widget-get widget ':href))
                   (and parent (widget-get parent ':href))
                   (and widget (widget-get widget 'href))
                   (and parent (widget-get parent 'href)))))
    (cond (href (require 'w3)
                (w3-popup-menu event))
          ((and pos (get-text-property pos 'lyskom-button-type))
           (lyskom-button-menu pos event))
          (t (lyskom-background-menu pos event)))))
  
(defun kom-mouse-null (event)
  "Do nothing."
  (interactive "@e")
  ;; This is here to pervent unwanted events when clicking mouse-3
  (identity 1))

(defun lyskom-make-button-menu (title entries buf arg text filter)
  "Create a menu keymap from a list of button actions."
  ;; Use the command as the event for simplicity.  Note that the menu
  ;; function alters the menu, so we copy the entries to prevent it
  ;; from fiddling with lyskom-button-actions.
  (let* ((lyskom-language lyskom-global-language)
         (title (lyskom-menu-encode title 'item)))
    (when (> (length title) 44) (setq title (concat (substring title 0 40)
                                                    " ...")))
    (cond ((string-match "XEmacs" (emacs-version))
           (cons title
                 (delq nil
                       (mapcar (lambda (entry)
                                 (and (funcall filter (cdr entry) arg)
                                      (vector (lyskom-menu-encode
                                               (lyskom-get-string (car entry))
					       'item)
                                              (list (cdr entry)
                                                    buf
                                                    (if (listp arg)
                                                        (list 'quote arg)
                                                      arg)
                                                    text)
                                              ':active t)))
                               entries))))
          (t (list title
                   (cons title
                         (delq nil
                               (mapcar (lambda (entry)
                                         (when (funcall filter (cdr entry) arg)
                                           (cons 
                                            (lyskom-menu-encode (lyskom-get-string (car entry))
                                                                'item)
                                            `(,(cdr entry) ,buf ,arg ,text))))
                                       entries))))))))


(defun lyskom-button-menu (pos event)
  "Internal function used by kom-popup-menu"
  (let* ((type  (get-text-property pos 'lyskom-button-type))
         (arg   (get-text-property pos 'lyskom-button-arg))
         (text  (get-text-property pos 'lyskom-button-text))
         (buf   (get-text-property pos 'lyskom-buffer))
         (data  (assq type lyskom-button-actions))
         (title (cond 
                 ((get-text-property pos 'lyskom-button-menu-title)
                  (apply 'lyskom-format 
                         (get-text-property pos 'lyskom-button-menu-title)))
                 ((elt data 1)
                  (lyskom-format (lyskom-get-string (elt data 1)) text))
                 (t (lyskom-format (lyskom-get-string 'generic-popup-title) text))))
         (actl  (or (and data (elt data 3)) nil))
         (filter (or (and data (elt data 5)) (lambda (&rest args) t))))
    (cond ((null data) (goto-char pos))
          ((null actl) (goto-char pos))
          ((null buf) (goto-char pos))
          ((null (get-buffer buf))
           (lyskom-message "%s" (lyskom-get-string 'no-such-buffer)))
          (t         
           (if (symbolp title) (setq title (lyskom-get-string title)))
           (set-buffer buf)

	   ;; There is a simple bug in x-popup-menu which causes menus
	   ;; from simple keymaps to be title-less. A list consisting
	   ;; of a single keymap works better. A patch is submittet to
	   ;; the GNU folks. /davidk
	   (if (eq event 'key)
	       (lyskom-keyboard-menu title actl buf arg text filter)
	     (let* ((menu (lyskom-make-button-menu title actl
						   buf arg text filter)))
	       (lyskom-do-popup-menu menu event)))))))


(defun lyskom-keyboard-menu (title entries buf arg text filter)
  "Do a keyboard menu selection."
  (let* ((prompt nil)
         (maxlen 0)
         (entries 
          (delq nil 
                (mapcar (lambda (el)
                          (and (funcall filter (cdr el) arg)
                               (cons (if (stringp (car el)) 
                                         (car el)
                                       (lyskom-get-string (car el)))
                                     (cdr el))))
                        entries)))
         (title (if (stringp title) title (lyskom-get-string title)))
         (completion-ignore-case t))
    (lyskom-traverse e entries
      (if (> (lyskom-string-width (car e)) maxlen)
          (setq maxlen (lyskom-string-width (car e)))))
    (setq prompt (substring title 0
			    (min (lyskom-string-width title)
				 (- (window-width (minibuffer-window))
				    maxlen 3))))

    (let ((choice (lyskom-read-from-menu prompt
                                         (lyskom-maybe-frob-completion-table
                                          entries t))))
      (when choice
        (funcall (cdr choice)
                 buf arg text)))))

       

(defun lyskom-button-press (pos)
  "Execute the default action of the active area at POS if any."
  (when pos
    (let* ((type (get-text-property pos 'lyskom-button-type))
           (arg  (get-text-property pos 'lyskom-button-arg))
           (text (get-text-property pos 'lyskom-button-text))
           (buf  (get-text-property pos 'lyskom-buffer))
           (hint (get-text-property pos 'lyskom-button-hint))
           (data (assq type lyskom-button-actions))
           (act  (or (and kom-use-button-hints hint)
                     (and data (elt data 2)))))
                 
      (cond ((null act) (goto-char pos))
            ((null buf) (goto-char pos))
            ((and buf (null (get-buffer buf))) 
             (lyskom-message "%s" (lyskom-get-string 'no-such-buffer)))
            (t (and buf (set-buffer buf))
               (funcall act
                        buf
                        arg
                        text))))))
                      

(defun lyskom-fix-pseudo-url (url)
  (save-match-data
    (when (string-match "^google:/*" url)
      (setq url (concat "http://www.google.com/search?q="
                        (substring url (match-end 0)))))
    (if (not (string-match lyskom-url-protocol-regexp url))
        (cond ((string-match "^www\\." url) 
               (concat "http://" url))
              ((string-match "^ftp\\." url) 
               (concat "ftp://" url))
              ((string-match "^gopher\\." url) 
               (concat "gopher://" url))
              ((string-match "^wais\\." url)
               (concat "wais://" url))
              (t (concat "http://" url)))
      url)))



(defun lyskom-button-add-links (text links)
  "Add links to TEXT according to LINKS.
LINKS is a list of lyskom-text-link objects."
  (lyskom-traverse spec links
    (let ((case-fold-search (lyskom-text-link->ignore-case spec)) 
          (start 0))
      (while (string-match (lyskom-text-link->pattern spec) text start)
         ;; Need to do this here since replace-in-string fscks up match-data
        (setq start (match-end 0))
        (add-text-properties
         (or (match-beginning (lyskom-text-link->highlight spec))
             (match-beginning 0))
         (or (match-end (lyskom-text-link->highlight spec))
             (match-end 0))
         (lyskom-generate-button 'url
                                 nil
                                 (replace-in-string 
                                  (match-string 0 text)
                                  (lyskom-text-link->pattern spec)
                                  (lyskom-text-link->replacement spec))
                                 kom-url-face)
         text)))))



(defun lyskom-button-get-arg (el text)
  "Get the button argument for button type EL from TEXT according to
the current match-data."
  (let ((no (or (elt el 3) 0)))
    (substring text (match-beginning no) (match-end no))))

(defun lyskom-button-get-text (el text)
  "Get the button text for button type EL from TEXT according to
the current match-data."
  (let ((no (or (elt el 2) 0)))
    (substring text (match-beginning no) (match-end no))))

(defun lyskom-button-get-face (el)
  "Get the button face for button type EL from TEXT according to
the current match-data."
  (let ((face (elt el 4)))
    (or (and (boundp face) (symbol-value face))
        (and (facep face) face))))

(defsubst lyskom-button-get-pred (el)
  "Return the match predicate for bitton type EL."
  (elt el 5))

(defun lyskom-button-transform-text (text &optional text-stat)
  "Add text properties to the string TEXT according to the definition of
lyskom-text-buttons. Returns the modified string."
  (let ((blist lyskom-text-buttons)
        (rcpts (reverse (and text-stat (lyskom-text-recipients text-stat))))
        (start 0)
        (el nil))

    ;; Do other text buttons
    (while blist
      (setq el (car blist))
      (setq start 0)
      (while (string-match (elt el 0) text start)
        (if (or (null (lyskom-button-get-pred el))
                  (funcall (lyskom-button-get-pred el)
                           (lyskom-button-get-text el text)))
          (progn
            (add-text-properties 
             (match-beginning (or (elt el 2) 0))
             (match-end (or (elt el 2) 0))
             (cond ((and (eq (elt el 1) 'text)
                         (not lyskom-transforming-external-text))
                    (lyskom-generate-button 'text
                                            (lyskom-button-get-arg el text)
                                            (lyskom-button-get-text el text)
                                            (lyskom-button-get-face el)))
                   ((eq (elt el 1) 'conf)
                    (lyskom-generate-button 'conf
                                            (lyskom-button-get-arg el text)
                                            (lyskom-button-get-text el text)
                                            (lyskom-button-get-face el)))
                   ((eq (elt el 1) 'pers)
                    (lyskom-generate-button 'pers
                                            (lyskom-button-get-arg el text)
                                            (lyskom-button-get-text el text)
                                            (lyskom-button-get-face el)))
                   ((eq (elt el 1) 'url)
                    (lyskom-generate-button 'url
                                            nil
                                            (lyskom-button-get-text el text)
                                            (lyskom-button-get-face el)))

                   ((eq (elt el 1) 'pseudo-url)
                    (let ((url (lyskom-fix-pseudo-url
                                (lyskom-button-get-text el text))))
                      (lyskom-generate-button 'url
                                              nil
                                              url
                                              (lyskom-button-get-face el))))

                   ((eq (elt el 1) 'info-node)
                    (lyskom-generate-button 'info-node
                                            (lyskom-button-get-arg el text)
                                            (lyskom-button-get-text el text)
                                            (lyskom-button-get-face el)))


                   ((eq (elt el 1) 'email)
                    (lyskom-generate-button 'email
                                            nil
                                            (lyskom-button-get-text el text)
                                            (lyskom-button-get-face el)))
                   (t nil))
             text)
            (setq start (match-end 0)))
          (setq start (1+ (match-beginning 0)))))
      (setq blist (cdr blist)))

    ;; Do links from kom-text-links last -- they have precedence
    ;; Do the first recipient last so links for that rcpt have prio
    (lyskom-traverse rcpt rcpts
      (lyskom-button-add-links text (cdr (assq rcpt kom-text-links))))

    ;; Do global links from kom-text-links very last
    (lyskom-button-add-links text (cdr (assq t kom-text-links)))
    )
  text)


(defun lyskom-get-button-hint (hints)
  "Get the hint to be used right now (if any) from HINTS"
  (let ((result nil)
        (hint nil))
    (while (and hints (null result))
      (setq hint (car hints))
      (setq hints (cdr hints))
      (cond ((null (car hint)) 
             (if (and (eq lyskom-current-function
                          (elt hint 1))
                      (or (null (elt hint 2))
                          (eq lyskom-current-function-phase
                              (elt hint 2))))
                 (setq result (elt hint 3))))
            ((listp (car hint))
             (if (and lyskom-executing-command
                      (memq lyskom-current-command (car hint)))
                 (setq result (cdr hint))))
            ((symbolp (car hint))
             (if (and lyskom-executing-command
                      lyskom-current-command
                      (eq lyskom-current-command (car hint)))
                 (setq result (cdr hint))))))
    result))


	
(defun lyskom-generate-button (type arg &optional text face menu-title
				    subtle)
  "Generate the properties for a button of type TYPE with argument
ARG. Optional argument TEXT is the button text to be saved as a
property and FACE is the default text face for the button. Optional
argument MENU-TITLE defines the title for the popup menu. See
lyskom-default-button for more information. Optional argument SUBTLE
means don't set the lyskom-button property if non-nil. that means
kom-next- and -previous-link won't notice the button"
  (setq lyskom-dummy-variable-to-fool-the-byte-compiler
        (car menu-title))		; produce error if menu-title not cons
  (let* ((numarg (cond ((numberp arg) arg)
                       ((stringp arg) (string-to-number arg))
                       ((lyskom-conf-stat-p arg) (conf-stat->conf-no arg))
                       ((lyskom-uconf-stat-p arg) (uconf-stat->conf-no arg))
                       ((lyskom-pers-stat-p arg) (pers-stat->pers-no arg))
                       (t nil)))
         (data (assq type lyskom-button-actions))
         (hints (and data (elt data 4)))
         (the-hint (lyskom-get-button-hint hints))
         (props 
          (cond ((and (memq type '(conf pers)) numarg)
                 (list 'face 
                       (or face
                           (or 
			    (let ((val (lyskom-indirect-assq 
					numarg kom-highlight-conferences)))
			      (when val
				(cond ((facep val) val)
				      ((and (symbolp val) (boundp val))
				       (symbol-value val)))))
                            kom-active-face))
                       'mouse-face kom-highlight-face
                       'lyskom-button-text text
                       'lyskom-button-type type
                       'lyskom-button-arg numarg
                       'lyskom-button-menu-title menu-title
                       'lyskom-buffer lyskom-buffer))
                ((and (eq type 'text) numarg)
                 (list 'face (or face kom-text-no-face)
                       'mouse-face kom-highlight-face
                       'lyskom-button-text text
                       'lyskom-button-type type
                       'lyskom-button-arg numarg
                       'lyskom-button-menu-title menu-title
                       'lyskom-buffer lyskom-buffer))
                ((eq type 'url)
                 (list 'face (or face kom-active-face)
                       'mouse-face kom-highlight-face
                       'lyskom-button-text text
                       'lyskom-button-type type
                       'lyskom-button-arg arg
                       'lyskom-button-menu-title menu-title
                       'lyskom-buffer lyskom-buffer))
                (t
		 (list 'face (or face kom-active-face)
		       'mouse-face kom-highlight-face
		       'lyskom-button-text text
		       'lyskom-button-type type
		       'lyskom-button-arg arg
                       'lyskom-button-menu-title menu-title
		       'lyskom-buffer lyskom-buffer)))))

    (append (list 'rear-nonsticky t)
	    (if (not subtle) (list 'lyskom-button t))
	    (if the-hint
		(cons 'lyskom-button-hint
		      (cons the-hint props))
	      props))))




(defun lyskom-default-button (type arg &optional menu-title)
  "Generate a button of type TYPE from data in ARG. ARG can be almost any
type of data and is converted to the proper argument type for buttons of
type TYPE before being sent to lyskom-generate-button. Optional argument
MENU-TITLE is a list consisting of a format string or symbol and arguments
for the format string. The arguments are not when the menu is popped
up."
  (and kom-text-properties
       (let (xarg text face subtle)
	 (cond ((eq type 'conf)
		(cond ((lyskom-conf-stat-p arg)
		       (if (conf-type->letterbox (conf-stat->conf-type arg))
			   (setq type 'pers))
		       (setq xarg arg
			     text (conf-stat->name arg)))
                      ((lyskom-uconf-stat-p arg)
		       (if (conf-type->letterbox (uconf-stat->conf-type arg))
			   (setq type 'pers))
		       (setq xarg (uconf-stat->conf-no arg)
			     text (uconf-stat->name arg)))
                      ((lyskom-conf-z-info-p arg)
                       (if (conf-type->letterbox (conf-z-info->conf-type arg))
                           (setq type 'pers))
                       (setq xarg (conf-z-info->conf-no arg)
                             text (conf-z-info->name arg)))
		      ((numberp arg)
                       (if (setq xarg (cache-get-uconf-stat arg))
                           (progn
                             (if (conf-type->letterbox
                                  (uconf-stat->conf-type xarg))
                                 (setq type 'pers))
                             (setq text (uconf-stat->name xarg))
                             (setq xarg arg))
                         (setq text "" xarg arg)))
		      (t (setq text "" xarg 0))))

	       ((eq type 'pers)
		(cond ((lyskom-conf-stat-p arg)
		       (setq xarg arg
			     text (conf-stat->name arg)))
                      ((lyskom-uconf-stat-p arg)
		       (setq xarg arg
			     text (uconf-stat->name arg)))
                      ((lyskom-conf-z-info-p arg)
                       (setq xarg arg
                             text (conf-z-info->name arg)))
		      ((lyskom-pers-stat-p arg)
		       (setq xarg (pers-stat->pers-no arg)
			     text (or (conf-stat->name
                                       (cache-get-conf-stat
                                        (pers-stat->pers-no arg)))
                                      "")))
		      ((numberp arg) 
                       (if (setq xarg (cache-get-uconf-stat arg))
                           (progn
                             (if (conf-type->letterbox
                                  (uconf-stat->conf-type xarg))
                                 (setq type 'pers))
                             (setq text (uconf-stat->name xarg))
                             (setq xarg arg))
                         (setq text "" xarg arg)))

		      (t (setq text "" xarg 0))))

	       ((eq type 'text)
		(cond ((stringp arg) (setq xarg (string-to-number arg)
					   text arg))
		      ((numberp arg) (setq xarg arg
					   text (number-to-string arg)))
		      ((lyskom-text-stat-p arg)
		       (setq xarg (text-stat->text-no arg)
			     text (number-to-string (text-stat->text-no arg))))
		      (t (setq xarg 0 text ""))))

	       ((eq type 'url)
                (setq face kom-url-face)
		(cond ((stringp arg) (setq xarg nil text arg))
		      (t (setq xarg nil text ""))))

               ((eq type 'email)
                (setq face kom-url-face)
                (cond ((stringp arg) (setq xarg nil text arg))
                      (t (setq xarg nil text ""))))

	       ((eq type 'timestamp)
		(setq face 'default
		      subtle t)
                (cond ((null arg) (setq xarg (lyskom-current-client-time)
                                        ;text (format-time-string "%Y-%m-%d %H:%M")
                                        ))
		      (t (setq xarg arg
                               ;text (format-time-string "%Y-%m-%d %H:%M" arg)
                               ))))
	       (t (setq xarg arg
			text "")))
	 (lyskom-generate-button type xarg text face menu-title subtle))))
                  

           


;;;========================================
;;; Button actions
;;;


(defun lyskom-button-view-text (buf arg text)
  "In the LysKOM buffer BUF, view the text ARG. Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-view arg))))

(defun lyskom-button-unread-text (buf arg text)
  "In the LysKOM buffer BUF, unread the text ARG. 
Last argument TEXT is ignored. This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-mark-unread arg))))

(defun lyskom-button-copy-text-no (but arg text)
  "In the LysKOM buffer BUF, ignore ARG and copy TEXT to the kill ring.
This is a LysKOM button action."
  (kill-new text))


(defun lyskom-button-review-noconversion (buf arg text)
  "In the LysKOM buffer BUF, view the text ARG without conversion. 
Last argument TEXT is ignored. This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-review-noconversion arg))))

(defun lyskom-button-review-converted (buf arg text)
  "In the LysKOM buffer BUF, view the text ARG converted. 
Last argument TEXT is ignored. This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-review-converted arg))))

(defun lyskom-button-review-rot13 (buf arg text)
  "In the LysKOM buffer BUF, view the text ARG rot13:ed. 
Last argument TEXT is ignored. This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-review-rot13 arg))))

(defun lyskom-button-find-root-review (buf arg text)
  "In the LysKOM buffer BUF, view the text ARG. Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
	(t (pop-to-buffer buf)
           (goto-char (point-max))
	   (kom-find-root-review arg))))

(defun lyskom-button-find-root (buf arg text)
  "In the LysKOM buffer BUF, view the text ARG. Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
	(t (pop-to-buffer buf)
           (goto-char (point-max))
	   (kom-find-root arg))))

(defun lyskom-button-comment-text (buf arg text)
  "In the LysKOM buffer BUF, comment the text ARG. Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-write-comment arg))))

(defun lyskom-button-private-comment-text (buf arg text)
  "In the LysKOM buffer BUF, write a private comment the text ARG. 
Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-private-answer arg))))

(defun lyskom-button-mark-text (buf arg text)
  "In the LysKOM buffer BUF, mark the text ARG. Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (unwind-protect
               (progn
                 (pop-to-buffer buf)
                 (lyskom-start-of-command 'kom-mark-text)
                 (lyskom-mark-text arg))
             (lyskom-end-of-command)))))

(defun lyskom-button-unmark-text (buf arg text)
  "In the LysKOM buffer BUF, unmark the text ARG. Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (unwind-protect
               (progn
                 (pop-to-buffer buf)
                 (lyskom-start-of-command 'kom-unmark-text)
                 (lyskom-unmark-text arg))
             (lyskom-end-of-command)))))

(defun lyskom-button-save-text (buf arg text)
  "In the LysKOM buffer BUF, save the text ARG. Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (unwind-protect
               (progn (pop-to-buffer buf)
                      (kom-save-text nil (list arg)))))))

(defun lyskom-button-save-text-body (buf arg text)
  "In the LysKOM buffer BUF, save the text ARG. Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (unwind-protect
               (progn (pop-to-buffer buf)
                      (kom-save-text-body arg))))))

(defun lyskom-button-review-comments (buf arg text)
  "In the LysKOM buffer BUF, review comments to the the text ARG.
Last argument TEXT is ignored. This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-review-comments arg))))

(defun lyskom-button-review-tree (buf arg text)
  "In the LysKOM buffer BUF, recursively review comments to the the text ARG.
Last argument TEXT is ignored. This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-review-tree arg))))

(defun lyskom-button-write-footnote (buf arg text)
  "In the LysKOM buffer BUF, write a footnote to the the text ARG.
Last argument TEXT is ignored. This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-write-footnote arg))))

(defun lyskom-button-fast-reply (buf arg text)
  "In the LysKOM buffer BUF, make a remark to the the text ARG.
Last argument TEXT is ignored. This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-fast-reply arg))))


(defun lyskom-button-view-conf-presentation (buf arg text)
  "In the LysKOM buffer BUF, view the presentation of ARG. 
Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-review-presentation arg))))

(defun lyskom-button-view-conf-status (buf arg text)
  "In the LysKOM buffer BUF, view the status of conference ARG. Last argument
TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-status-conf arg))))

(defun lyskom-button-goto-conf (buf arg text)
  "In the LysKOM buffer BUF, go to the conference ARG. Last argument TEXT is
ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-go-to-conf arg))))

(defun lyskom-button-add-self (buf arg text)
  "In the LysKOM buffer buf, add self to conference ARG."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (let ((conf (blocking-do 'get-conf-stat arg)))
             (if conf
                 (kom-add-self conf)
               (lyskom-insert-before-prompt 'somebody-deleted-that-conf))))))

(defun lyskom-button-sub-self (buf arg text)
  "In the LysKOM buffer buf, sub self from conference ARG."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-sub-self arg))))

(defun lyskom-button-view-pers-presentation (buf arg text)
  "In the LysKOM buffer BUF, view the presentation of person ARG. 
Last argument TEXT is ignored.
This is a LysKOM button action."
  (lyskom-button-view-conf-presentation buf arg text))

(defun lyskom-button-view-pers-status (buf arg text)
  "In the LysKOM buffer BUF, view the status of person ARG. 
Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-status-person arg))))

(defun lyskom-button-befriend (buf arg text)
  "In the LysKOM buffer BUF, befriend person ARG.
Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-befriend arg))))

(defun lyskom-button-moronify (buf arg text)
  "In the LysKOM buffer BUF, befriend person ARG.
Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (goto-char (point-max))
           (kom-moronify arg))))

(defun lyskom-button-mail (buf arg text)
  "In the LysKOM buffer BUF, send mail to the conference ARG.
Last argument TEXT is ignored.
This is a LysKOM button action."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-send-letter arg))))

(defun lyskom-button-send-message (buf arg text)
  "In the LysKOM buffer BUF, send a personal message to person ARG."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-send-message arg nil))))

(defun lyskom-button-view-session-status (buf arg text)
  "In the LysKOM buffer BUF, show session status for person ARG."
  (cond ((not (integerp arg)) nil)
        (t (pop-to-buffer buf)
           (kom-status-session (lyskom-session-from-conf arg)))))

(defun lyskom-button-copy-email (but arg text)
  "In the LysKOM buffer BUF, ignore ARG and copy TEXT to the kill ring.
This is a LysKOM button action."
  (kill-new text))

(lyskom-external-function compose-mail)
(defun lyskom-button-open-email (but arg text)
  "In the LysKOM buffer BUF, ignore ARG and open TEXT as an e-mail address.
This is a LysKOM button action."
  (if (fboundp 'compose-mail)
      (compose-mail text)
    (mail nil text)))


(defun lyskom-button-copy-url (but arg text)
  "In the LysKOM buffer BUF, ignore ARG and copy TEXT to the kill ring.
This is a LysKOM button action."
  (kill-new (replace-in-string text "\\s-+" "")))


(defun lyskom-transform-url (url)
  (let ((rules kom-url-transformation-rules))
    (while rules
      (setq url (replace-in-string url (car (car rules)) (cdr (car rules)))
            rules (cdr rules))))
  url)

(defun lyskom-button-open-url (buf arg text)
  "In the LysKOM buffer BUF, ignore ARG and open TEXT as an URL.
This is a LysKOM button action."
  (let* ((url (lyskom-fix-pseudo-url (replace-in-string text "\\s-+" "")))
         protocol
         url-manager)
    (setq url (lyskom-transform-url url))
    (string-match lyskom-url-protocol-regexp url)
    (setq protocol (match-string 1 url))
    (setq url-manager (lyskom-get-url-manager protocol))
    (if (null url-manager)
        (lyskom-error "Can't find URL viewer"))
    (funcall (elt url-manager 3) url url-manager)))

 
 
;;;
;;;	Info node button
;;;

(defun lyskom-button-goto-info-node (buf arg text)
  "In the LysKOM buffer BUF, open ARG as an Info node, and ignore TEXT.
This is a LysKOM button action."
  (when (not (fboundp 'Info-goto-node))
    (autoload 'Info-goto-node "info"
      "Go to info node named NAME.  Give just NODENAME or (FILENAME)NODENAME."
      t))
  (setq arg (replace-in-string arg "\n" " " t))
  (setq arg (replace-in-string arg " +" " " t))
  (Info-goto-node arg))

;;;
;;;	Timestamp button
;;;

(defun lyskom-button-copy-timestamp (buf arg text)
  "In the LysKOM buffer BUF, ignore TEXT and copy ARG to the kill ring
after formating it as time. This is a LysKOM button action."
  (let ((kom-print-relative-dates nil))
    (kill-new (lyskom-format-time 'date-and-time arg))))


;;;
;;;	LysKOM URL Management
;;;

(defun lyskom-get-url-manager (protocol)
  "Get the URL manager for PROTOCOL (a string). Returns a function."
  (let ((managers kom-url-managers)
	(preferences kom-url-viewer-preferences)
	(result nil))
    (while (and preferences (not result))
      (setq managers kom-url-managers)
      (while (and managers (not result))
	(if (and (string-match (car (car managers))
			       (car preferences))
		 (string-match (car (cdr (car managers)))
			       protocol))
	    (setq result (car managers)))
	(setq managers (cdr managers)))
      (setq preferences (cdr preferences)))
    result))



(defun lyskom-url-manager-starting (manager)
  "Tell the user that the URL manager MANAGER is starting."
  (lyskom-message "%s" (lyskom-format (lyskom-get-string 'starting-program)
				 (elt manager 2))))


(eval-when-compile (defvar browse-url-browser-function nil))

(defun lyskom-view-url-browse-url (url manager)
  (require 'browse-url)
  (funcall browse-url-browser-function url))

(lyskom-with-external-functions (w3-fetch)
  (defun lyskom-view-url-w3 (url manager)
    "View the URL using W3. Second argument MANAGER is ignored."
    (w3-fetch url)))

(defun lyskom-view-url-dired (url manager)
  "View the URL URL using dired. Second argument MANAGER is ignored."
  (if (not (and (string-match
		 "\\(file\\|ftp\\)://\\([^/:]*\\)\\(:[0-9]*\\)?\\(/\\|$\\)"
		 url)
		(match-beginning 0)
		(match-beginning 1)
		(match-beginning 2)))
      (lyskom-error "Bad URL"))
  (let ((host (substring url (match-beginning 2) (match-end 2)))
	(path (substring url (match-end 0)))
	(user "anonymous"))
    (if  (string-match ";type=.$" path)
        (setq path (substring path 0 (match-beginning 0))))
    (if (and (string-match "\\([^@]*\\)@" host)
	     (match-beginning 1))
	(progn
	  (setq user (substring host (match-beginning 1) (match-end 1)))
	  (setq host (substring host (match-end 0)))))
    (cond ((string= host "localhost") (find-file path))
          (t (find-file (concat "/" user "@" host ":/" path))))
    ;;    (message "%s  %s  %s" user host path)
    ))



(defun lyskom-view-url-telnet (url manager)
  "View the URL URL using telnet. Second argument MANAGER is ignored."
  (if (not (and (string-match 
                 "telnet://\\([^@]*@\\)?\\([^/:]*\\)\\(:[0-9]*\\)?"
		 url)
		(match-beginning 0)
		(match-beginning 2)))
      (lyskom-error "Bad URL"))
  (let ((host (substring url (match-beginning 2) (match-end 2)))
	(port (if (match-beginning 3)
		  (substring url (1+ (match-beginning 3)) (match-end 3))
		""))
	(user (if (match-beginning 1)
                  (substring url (match-beginning 1) (1- (match-end 1)))
                nil))
        ;; (password nil)
	)
    (if (and user
             (string-match "^\\([^:]*\\):\\(.*\\)" user))
        (progn
	  ;; (setq password (substring user
	  ;; 			    (match-beginning 2)
	  ;; 			    (match-end 2)))
	  (setq user (substring user 
				(match-beginning 1)
				(match-end 1)))))
    
    (telnet (concat host " " port))
    ;;    (message "u:%s  p:%s  h:%s  #:%s" 
    ;;             (or user "<u>")
    ;;             (or password "<p>")
    ;;             (or host "<h>")
    ;;             (or port "<#>"))

    ))


(defun lyskom-view-url-mailmode (url manager)
  "View the URL URL using mail in Emacs. The second argument MANAGER is ignored."
  (if (not (and (string-match "mailto:\\([^@]+@.*\\)$" url)
		(match-beginning 1)))
      (lyskom-error "Bad URL"))
  (mail nil (substring url (match-beginning 1) (match-end 1))))


(defun lyskom-view-url-windows (url manager)
  "View the URL URL in Microsoft Windows. MANGER is the URL manager.
Fall back on Netscape if not running in Microsoft Windows.

In theory, opening an URL on Windows should be a matter of doing
\(w32-shell-execute \"open\" URL\), but this sometimes fails, so
we protect ourselves by also trying to start some common defaults
using start-process."
  (cond
   ((memq window-system '(win32 mswindows w32))
    (cond
     ((and (boundp 'kom-windows-browser-command)
           (and (not (null kom-windows-browser-command))
                (not (eq (length kom-windows-browser-command) 0))))
      ;; Explicit given file name of browser, so complain to user if
      ;; the value is bad.
      (if (not (file-executable-p kom-windows-browser-command))
          (error (concat "Not an executable file: %s;"
                         " kom-windows-browser-command has a bad value")
                 kom-windows-browser-command))

      (condition-case nil
          (progn
            (lyskom-url-manager-starting manager)
            (start-process "Browser"
                           nil
                           kom-windows-browser-command
                           url))
        (error (error (concat "Failed starting browser using"
                              " kom-windows-browser-command"
                              " (%s)")
                      kom-windows-browser-command))))
     ((and (not (memq 'w32-shell-execute lyskom-compatibility-definitions))
           (condition-case nil
               (progn (w32-shell-execute "open" url) t)
             (error nil)))
      (lyskom-url-manager-starting manager))
     ;;(lyskom-message "Webb via [%s \"%s\" \"%s\"] ..."
     ;;                "w32-shell-execute" "open" url))
     (t (let ((programs
               '("c:\\Program\\Internet Explorer\\iexplore.exe"
                 "c:\\Programs\\Internet Explorer\\iexplore.exe"
                 "c:\\Program Files\\Internet Explorer\\iexplore.exe"
                 "start"
                 "explorer"
                 "C:\\Program Files\\Netscape\\Communicator\\Program\\netscape.exe"
                 "C:\\Program Files\\Netscape\\Navigator\\Program\\netscape.exe"
                 )))
          (while programs
            (condition-case nil
                (progn
                  (lyskom-url-manager-starting manager)
                  (start-process (car programs)
                                 nil
                                 (car programs)
                                 url)
                  (setq programs nil))
              (error (setq programs (cdr programs)))))))))
   (t (lyskom-view-url-netscape url manager))))


(defun lyskom-view-url-netscape (url manager)
  "View the URL URL using Netscape Navigator. The second argument
MANAGER is the URL manager that started Netscape.

This function attempts to load the URL in a running Netscape, but failing
that, starts a new one."
  (setq url (replace-in-string url "," "%2C"))
  (setq url (replace-in-string url "(" "%28"))
  (setq url (replace-in-string url ")" "%29"))
  (let* ((url-string (if (memq window-system '(win32 mswindows w32))
                         (list url)
                       (list "-remote"
                             (format
                              (cond ((eq kom-netscape-variant 'new-window)
                                     "openUrl(%s, new-window)")
                                    ((eq kom-netscape-variant 'new-tab)
                                     "openUrl(%s, new-tab)")
                                    (t "openUrl(%s)"))
                              url))))
         
         (proc (apply 'start-process "netscape"
                      nil
                      (if (listp kom-netscape-command)
                          (car kom-netscape-command)
                        kom-netscape-command)
                      (if (listp kom-netscape-command)
                          (append (cdr kom-netscape-command)
                                  url-string)
                        url-string)))
         (status 'run)
         (exit nil))
    (lyskom-url-manager-starting manager)
    (while (eq status 'run)
      (lyskom-accept-process-output)
      (setq status (process-status proc)))
    (setq exit (process-exit-status proc))
    (cond ((and (eq status 'exit) 
                (eq exit 1))
           (apply 'start-process "netscape"
                          nil
                          (if (listp kom-netscape-command)
                              (car kom-netscape-command)
                            kom-netscape-command)
                          (if (listp kom-netscape-command)
                              (append (cdr kom-netscape-command)
                                      (list url))
                            (list url))))
          (t nil))))


(defun lyskom-view-url-mosaic (url manager)
  "View the URL URL using NCSA Mosaic. The attempts to open the URL in an
existing Mosaic process. Failing that, it starts a new Mosaic."
  (let ((pid -1)
        tempbuffer
        (filename "/tmp/Mosaic."))
    (if (file-exists-p (expand-file-name "~/.mosaicpid"))
        (save-current-buffer
          (set-buffer 
           (setq tempbuffer (get-buffer-create " *kom*-mosaicpid")))
          (insert-file-contents (expand-file-name "~/.mosaicpid"))
          (setq pid (read tempbuffer))
          (delete-region (point-min) (point-max))
          (insert "newwin\n")
          (insert url)
          (insert "\n")
          (setq filename (concat filename (number-to-string pid)))
          (write-region (point-min) (point-max) filename nil 'x)
          (kill-buffer tempbuffer)
          (if (= -1 (signal-process pid 30))
              (apply 'start-process "mosaic"
                     (current-buffer)
                     (if (listp kom-mosaic-command)
                         (car kom-mosaic-command)
                       kom-mosaic-command )
                     (if (listp kom-mosaic-command)
                         (append (cdr kom-mosaic-command)
                                 (list url))
                       (list url)))
	    (lyskom-url-manager-starting manager)))
      (save-excursion
        (apply 'start-process "mosaic"
               (current-buffer)
               (if (listp kom-mosaic-command)
                   (car kom-mosaic-command)
                 kom-mosaic-command )
               (if (listp kom-mosaic-command)
                   (append (cdr kom-mosaic-command)
                           (list url))
                 (list url)))
	(lyskom-url-manager-starting manager)))))

(defun lyskom-view-url-galeon (url manager)
  "View the URL URL using Galeon. The second argument MANAGER is the URL
manager that started Galeon.

This function attempts to load the URL in a running Galeon, but failing
that, starts a new one."
  (setq url (replace-in-string url "," "%2C"))
  (setq url (replace-in-string url "(" "%28"))
  (setq url (replace-in-string url ")" "%29"))
  (let* ((url-string (if (memq window-system '(win32 mswindows w32))
                         (list url)
                       (list "-n"
                             (format "%s" url)))))
    (lyskom-url-manager-starting manager)
    (apply 'start-process "galeon"
           nil
           (if (listp kom-galeon-command)
               (car kom-galeon-command)
             kom-galeon-command)
           (if (listp kom-galeon-command)
               (append (cdr kom-galeon-command)
                       url-string)
             url-string))))

;; Added by Peter Liljenberg
(defun lyskom-view-url-lynx (url manager)
  "View the URL URL using Lynx.
Lynx will be run either in an xterm or in Emacs terminal mode,
depending on the value of `kom-lynx-terminal'."
  (cond
   ((eq kom-lynx-terminal 'xterm)
    (apply 'start-process
	   "lynx"
	   nil
	   (car kom-lynx-xterm-command)
	   (append (cdr kom-lynx-xterm-command) (list url)))
    (lyskom-url-manager-starting manager))

   ((eq kom-lynx-terminal 'terminal)
    (let* ((lbuf (get-buffer "*Lynx*"))
	   (lproc (and lbuf (get-buffer-process lbuf))))
      (if lproc
	  ;; Tell existing Lynx to fetch URL
	  (process-send-string lproc (concat "g" url "\n"))

	;; Create a new Lynx
	(switch-to-buffer
	 (apply 'make-term "Lynx"
		(if (listp kom-lynx-terminal-command)
		    (car kom-lynx-terminal-command)
		  kom-lynx-terminal-command)
		nil
		(if (listp kom-lynx-terminal-command)
		    (append (cdr kom-lynx-terminal-command) (list url))
		  (list url))))
	(delete-other-windows)
	(term-char-mode)
	(set-process-sentinel
	 (get-buffer-process (current-buffer))
	 (function (lambda (proc str)
		     (kill-buffer (process-buffer proc))))))
      (lyskom-url-manager-starting manager)))

   (t (lyskom-error "Bad Lynx terminal: %s" kom-lynx-terminal))
   ))


;;;
;;;     aux-item buttons
;;;

(defun lyskom-button-delete-aux (buf arg text)
  (let ((aux nil))
    (cond ((lyskom-aux-item-p arg))
          ((listp arg)
           (let ((items (cond ((eq 'text (car arg))
                               (text-stat->aux-items 
                                (blocking-do 'get-text-stat (elt arg 1))))
                              ((eq 'conf (car arg))
                               (conf-stat->aux-items 
                                (blocking-do 'get-conf-stat (elt arg 1))))
                              ((eq 'server (car arg))
                               (server-info->aux-item-list
                                (blocking-do 'get-server-info)))
                              (t nil))))
             (while items
               (when (eq (aux-item->aux-no (car items)) (elt arg 2))
                 (setq aux (car items))
                 (setq items nil))
               (setq items (cdr items))))))

    (when aux
      (lyskom-start-of-command nil)
      (unwind-protect
          (progn
            (unless (cond ((eq 'text (car arg))
                           (cache-del-text-stat (elt arg 1))
                           (blocking-do 'modify-text-info
                                        (elt arg 1)
                                        (list (aux-item->aux-no aux))
                                        nil))
                          ((eq 'conf (car arg))
                           (cache-del-conf-stat (elt arg 1))
                           (blocking-do 'modify-conf-info
                                        (elt arg 1)
                                        (list (aux-item->aux-no aux))
                                        nil))
                          ((eq 'server (car arg))
                           (prog1 (blocking-do 'modify-server-info
                                               (list (aux-item->aux-no aux))
                                               nil)
                             (lyskom-set-default 'lyskom-server-info (blocking-do 'get-server-info)))))
              (lyskom-report-command-answer nil)))
        (lyskom-end-of-command)))))
          

(defun lyskom-button-info-aux (buf arg text)
  (pop-to-buffer buf)
  (goto-char (point-max))
  (let ((aux nil))
    (cond ((lyskom-aux-item-p arg))
          ((listp arg) 
           (let ((items 
                  (cond ((eq 'text (car arg))
                         (text-stat->aux-items 
                          (blocking-do 'get-text-stat (elt arg 1))))
                        ((eq 'conf (car arg))
                         (conf-stat->aux-items 
                          (blocking-do 'get-conf-stat (elt arg 1))))
                        ((eq 'server (car arg))
                         (server-info->aux-item-list
                          (blocking-do 'get-server-info)))
                        (t nil))))
             (while items
               (when (eq (aux-item->aux-no (car items)) (elt arg 2))
                 (setq aux (car items))
                 (setq items nil))
               (setq items (cdr items))))))
              
    (if aux
        (let ((header (cond ((eq 'text (car arg)) 
                             (lyskom-format 'aux-item-for-text-no (elt arg 1)))
                            ((eq 'conf (car arg))
                             (lyskom-format 'aux-item-for-conference-no
                                            (blocking-do 'get-conf-stat
                                                         (elt arg 1))))
                            ((eq 'server (car arg))
                             (lyskom-format 'aux-item-for-server))
                            (t "????"))))
          (lyskom-start-of-command nil)
          (unwind-protect
              (lyskom-insert (or (lyskom-aux-item-call aux
                                                       'info 
                                                       aux
                                                       header)
                                 (lyskom-aux-item-info aux header)))
            (lyskom-end-of-command)))
      (lyskom-format-insert-before-prompt 'cant-get-aux-item))))
           
(defun lyskom-button-apply (buf arg text)
  (apply (car arg) (cdr arg)))


;;; ================================================================
;;; Functions to put in kom-highlight-conferences

(defun lyskom-highlight-function-get-conf-stat (arg)
  "Try to translate ARG to a conf-stat without making server calls.

ARG may be a conf-stat, pers-stat, uconf-stat, conf-z-info, integer or string."
  (if (lyskom-conf-stat-p arg)
      arg
    (let* ((conf-no (cond ((lyskom-pers-stat-p arg) (pers-stat->pers-no arg))
                          ((lyskom-uconf-stat-p arg) (uconf-stat->conf-no arg))
                          ((numberp arg) arg)
                          ((lyskom-conf-z-info-p arg) (conf-z-info->conf-no arg))
                          ((stringp arg) (lyskom-string-to-number arg)))))
      (when conf-no
        (cond ((cache-get-conf-stat conf-no))
              (conf-no (initiate-get-conf-stat 'background nil conf-no)
                       nil))))))


(defun lyskom-highlight-i-am-supervisor (arg)
  "Returns non-nil if lyskom-pers-no is the supervisor of ARG.

ARG interpreted by `lyskom-highlight-function-get-conf-stat'.

This function never makes server calls, so if the information required
to answer accurately is not cached, this function will return an incorrect
result (nil instead of t)."
  (let ((conf-stat (lyskom-highlight-function-get-conf-stat arg)))
    (lyskom-i-am-supervisor conf-stat nil)))

(defun lyskom-highlight-has-no-presentation (arg)
  "Returns non-nil if lyskom-pers-no is the supervisor of CONF-STAT.

ARG is interpreted by `lyskom-highlight-function-get-conf-stat'.

This function never makes server calls, so if the information required
to answer accurately is not cached, this function will return an incorrect
result (nil instead of t)."
  (let ((conf-stat (lyskom-highlight-function-get-conf-stat arg)))
    (cond ((null conf-stat) nil)
          ((conf-type->secret (conf-stat->conf-type conf-stat)) nil)
          (t (eq 0 (conf-stat->presentation conf-stat))))))
;;;;; -*-coding: raw-text;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;; ================================================================
;;; ================================================================
;;;
;;; File: view-text.el
;;; 
;;; This file contains functions that have to do with putting
;;; a text into the lyskom buffer.
;;;


(defvar lyskom-view-text-text)
(defvar lyskom-view-text-text-stat)


(defun lyskom-view-text (text-no &optional mark-as-read
				 follow-comments conf-stat priority
				 build-review-tree filter-active
				 flat-review)
  "Display text number TEXT-NO.
Args: TEXT-NO &optional MARK-AS-READ FOLLOW-COMMENTS CONF-STAT 
PRIORITY BUILD-REVIEW-TREE FLAT-REVIEW.
If MARK-AS-READ (or `kom-review-marks-texts-as-read') is non-nil the text will
be marked as read.
If FOLLOW-COMMENTS is non-nil all comments and footnotes to this text will be
read before the next text. CONF-STAT must be the conference status of the
current conference, and PRIORITY the priority, if FOLLOW-COMMENTS is non-nil.
If BUILD-REVIEW-TREE is non-nil then it fixes a new entry in the 
lyskom-reading-list to read the comments to this.
If FLAT-REVIEW is non-nil this is a review caused by lyskom-view-commented or
kom-view-text viewing REVIEW or REVIEW-MARK text.

Note that this function must not be called asynchronously."

  (let ((inhibit-read-only t)
	(filter (and filter-active
                     (lyskom-filter-text-p text-no)))
        (start nil)
        (todo nil)
        (lyskom-last-text-format-flags nil))
    (cond ((eq filter 'obliterate)
	   (setq todo 'next-text)
	   (lyskom-mark-as-read (blocking-do 'get-text-stat text-no))
	   'next-text)
	  ((eq filter 'skip-text) (lyskom-filter-prompt text-no 'filter-text)
	   (setq todo 'next-text)
	   (lyskom-mark-as-read (blocking-do 'get-text-stat text-no))
	   'next-text)
	  ((eq filter 'skip-tree)
	   (lyskom-filter-prompt text-no 'filter-tree)

	   ;;(initiate-get-text-stat 'main 'lyskom-jump text-no t)
	   ;; Let's try something else:
	   (lyskom-jump (blocking-do 'get-text-stat text-no) t)
	   (lyskom-wait-queue 'main)

	   (setq todo 'next-text)
	   'next-text)
	  (t
	   (if (not (or (null filter) 
                        (eq filter 'read)
			(eq filter 'dontshow)))
	       (lyskom-message "%s" (lyskom-get-string 'invalid-filter-list)))
	   (if (eq filter 'dontshow)
	       (setq todo 'next-text))
	   (blocking-do-multiple ((text-stat (get-text-stat text-no))
				  (text (get-text text-no)))
             (setq lyskom-view-text-text text
                   lyskom-view-text-text-stat text-stat)
	     (if (and text-stat text)
		 (progn
                   (run-hooks 'kom-view-text-hook)
		   ;; Use a marker, because the buffer may lose data
		   ;; at the top if kom-max-buffer-size is set.
		   (setq start (point-max-marker))





                   (let ((mx-date (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 21)))
                         (mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 17)))
                         (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 16)))
                         (mx-to (lyskom-get-aux-item (text-stat->aux-items text-stat) 19))
                         (mx-cc (lyskom-get-aux-item (text-stat->aux-items text-stat) 20))
			 (mx-message-id (lyskom-get-aux-item (text-stat->aux-items text-stat) 22))
                         (mx-filename (lyskom-get-aux-item (text-stat->aux-items text-stat) 10104))
                         (mx-sender (lyskom-get-aux-item (text-stat->aux-items text-stat) 10103))
			 (mx-headers (lyskom-get-aux-item (text-stat->aux-items text-stat) 24))
                         (mx-reply-to (lyskom-get-aux-item (text-stat->aux-items text-stat) 18)))
                         
                   (lyskom-format-insert
                    'view-text-first-line
                    text-stat
                    (list 'lyskom-text-start (text-stat->text-no text-stat))
                    (if mx-date
                        (lyskom-format-mx-date mx-date)
                      (lyskom-format-time 'date-and-time
                                          (text-stat->creation-time text-stat)))
                    (text-stat->no-of-lines text-stat)
                    (or (lyskom-format-mx-author mx-from mx-author)
                        (lyskom-format "%#1P" 
                                       (text-stat->author text-stat)))
                    (when (eq filter 'dontshow)
                      (lyskom-get-string 'filtered))
                    (when kom-highlight-first-line
                      `(face ,kom-first-line-face)))

		   (set-marker start nil)

                   ;; Insert sender

                   (when (and mx-sender kom-show-imported-envelope-sender)
                     (lyskom-format-insert 'envelope-sender 
                                           (aux-item->data (car mx-sender))))
                   (when mx-filename
                     (lyskom-format-insert 'attachment-filename
                                           (aux-item->data (car mx-filename))))
                   (when (and mx-message-id kom-show-imported-message-id)
                     (lyskom-format-insert 'message-id
                                           (aux-item->data (car mx-message-id))))

                   ;; Insert imported at

                   (cond ((and (or mx-from mx-author mx-sender mx-headers)
                               (text-stat->author text-stat)
                               kom-show-imported-importer)
                          (lyskom-format-insert 'text-imported-at-by
                                                (lyskom-format-time
                                                 'date-and-time
                                                 (text-stat->creation-time text-stat))
                                                (text-stat->author text-stat)))
                         (mx-from
                          (lyskom-format-insert 'text-imported-at
                                                (lyskom-format-time
                                                 'date-and-time
                                                 (text-stat->creation-time text-stat))))
                         (mx-date (lyskom-format-insert 'text-created-at
                                                        (lyskom-format-time
                                                         'date-and-time
                                                         (text-stat->creation-time text-stat)))))
                   (when kom-show-imported-external-recipients
                     (mapc (lambda (el)
                             (lyskom-format-insert "%#1s: %#2s\n"
                                                   (lyskom-get-string 'mx-Recipient)
                                                   (aux-item->data el)))
                           mx-to)
                     (mapc (lambda (el)
                             (lyskom-format-insert "%#1s: %#2s\n"
                                                   (lyskom-get-string 'mx-Extra-recipient)
                                                   (aux-item->data el)))
                           mx-cc)
                     (mapc (lambda (el)
                             (lyskom-format-insert "%#1s: %#2s\n"
                                                   (lyskom-get-string 'mx-Extern-reply-to)
                                                   (aux-item->data el)))
                           mx-reply-to)
                     )



                   ;; Insert all recipients and other header lines.

		   (if (eq filter 'dontshow)
		       (lyskom-mark-as-read
			(blocking-do 'get-text-stat text-no))
		     (lyskom-traverse misc
			 (text-stat->misc-info-list text-stat)
		       (let ((type (misc-info->type misc)))
			 (cond
			  ((memq type '(RECPT BCC-RECPT CC-RECPT))
			   (lyskom-print-header-recpt 
			    (misc-info->recipient-no misc)
			    misc))
			  ((eq type 'COMM-IN)
			   (if kom-reading-puts-comments-in-pointers-last
			       nil
                             (lyskom-print-header-comm 
                              (misc-info->comm-in misc)
                              misc
                              text-stat)))
			  ((eq type 'FOOTN-IN)
			   (if kom-reading-puts-comments-in-pointers-last
			       nil
			     (lyskom-print-header-comm
			      (misc-info->footn-in misc)
			      misc
                              text-stat)))
			  ((eq type 'COMM-TO)
			   (lyskom-print-header-comm (misc-info->comm-to misc)
						     misc
                                                     text-stat))
			  ((eq type 'FOOTN-TO)
			   (lyskom-print-header-comm (misc-info->footn-to misc)
						     misc
                                                     text-stat))
			  )))

                     ;; Insert aux-items that go in the header.

                     (lyskom-traverse-aux aux (text-stat->aux-items text-stat)
                       (let ((tpw (lyskom-aux-item-definition-field aux 'text-print-when)))
                       (when (or (and (lyskom-viewing-noconversion) (not (eq tpw 'footer)))
                                 (eq tpw 'header)
                                 (and (eq tpw 'comment) (not kom-reading-puts-comments-in-pointers-last)))
                         (if (lyskom-aux-item-has-call aux 'text-print)
                             (let ((text (lyskom-aux-item-call aux 'text-print aux text-stat)))
                               (when text (lyskom-insert text) (lyskom-insert "\n")))
                           (lyskom-format-insert 'text-header-aux-item
                                                 (lyskom-get-string
                                                  (or (car (lyskom-aux-item-definition-field aux 'text-name))
                                                      'unknown-aux-item))
                                                 (aux-item->tag aux)
                                                 (aux-item->data aux)
                                                 (lyskom-aux-item-terminating-button aux text-stat))
                           (lyskom-insert "\n")))))


                     ;; If the text is marked, insert line saying so.

		     (let ((num-marks (text-stat->no-of-marks text-stat))
			   (is-marked-by-me (cache-text-is-marked
					     (text-stat->text-no text-stat))))
		       (when (> num-marks 0)
			 (if is-marked-by-me
			     (if (= num-marks 1)
				 (lyskom-format-insert 'marked-by-you
						       (mark->symbolic-mark-type
							is-marked-by-me))
			       (lyskom-format-insert 'marked-by-you-and-others
						     (1- num-marks)
						     (mark->symbolic-mark-type
						      is-marked-by-me)))
			   (lyskom-format-insert 'marked-by-several num-marks))))

                     ;; Insert the text body.

		     (lyskom-print-text text-stat text
					(or mark-as-read kom-review-marks-texts-as-read)
					text-no flat-review)
		     (if kom-review-marks-texts-as-read (lyskom-is-read text-no)))

                   ;; Insert aux-items that go in the footer.

                   (let ((text nil))
                       (lyskom-traverse-aux aux
                                            (text-stat->aux-items text-stat)
                         (when (and
                             (or (eq (lyskom-aux-item-definition-field aux
                                                                 'text-print-when)
                                     'footer)
                              (and (eq (lyskom-aux-item-definition-field aux
                                                                  'text-print-when)
                                       'comment)
                                   kom-reading-puts-comments-in-pointers-last
                               ))
                             (setq text 
                                   (lyskom-aux-item-call 
                                    aux
                                    'text-print
                                    aux
                                    text-stat)))
                           (lyskom-insert text)
                           (lyskom-insert "\n"))))

                   ;; Insert fast replies.

                   (let ((author-fast-replies nil)
                         (other-fast-replies nil))
                     (lyskom-traverse-aux aux (text-stat->aux-items text-stat)
                      (when (eq (aux-item->tag aux) 2)
                        (if (or (lyskom-viewing-noconversion)
                                (not (eq (text-stat->author text-stat)
                                         (aux-item->creator aux))))
                            (setq other-fast-replies
                                  (cons aux other-fast-replies))
                          (setq author-fast-replies
                                (cons aux author-fast-replies)))))
                     (when author-fast-replies
                       (lyskom-insert
                        (concat
                         (lyskom-format 'author-fast-replies)
                         "\n"
                         (mapconcat (lambda (item)
                                      (concat
                                       (lyskom-format 'author-fast-reply-aux
                                                      (aux-item->data item)
                                                      (aux-item->creator item))
                                       (lyskom-aux-item-terminating-button
                                        item
                                        text-stat)
                                       "\n"))
                                    (nreverse author-fast-replies)
                                    ""))))
                     (when other-fast-replies
                       (lyskom-insert
                        (concat
                         (lyskom-format 'other-fast-replies)
                         "\n"
                         (mapconcat (lambda (item)
                                      (concat
                                       (lyskom-format 'other-fast-reply-aux
                                                      (aux-item->data item)
                                                      (aux-item->creator item))
                                       (lyskom-aux-item-terminating-button
                                        item
                                        text-stat)
                                       "\n"))
                                    (nreverse other-fast-replies)
                                    "")))))

                   ;; Insert footnote/comment references.

		   (if kom-reading-puts-comments-in-pointers-last
		       (lyskom-view-text-handle-saved-comments text-stat))

		   ; Show footnotes if we should do that directly
 
		   (if kom-show-footnotes-immediately
		       (lyskom-traverse misc
			   (text-stat->misc-info-list text-stat)
			 (if (eq (misc-info->type misc) 'FOOTN-IN)
			     ;; Show the footnote
			     (progn
			       (lyskom-view-text (misc-info->footn-in misc)
						 mark-as-read t conf-stat priority)
			       ; Mark as read (internally) so follow-comments won't add 
			       ; it to the read list.
			       (lyskom-is-read (misc-info->footn-in misc))))))

                   ;; Prefetch commented texts.

		   (if (or follow-comments
			   ;; Checking build-review-tree should not be
			   ;; necessary, really /davidk
			   build-review-tree)
		       ;; This shows footnotes also.
		       (progn
			 ;; this is a bit silly, as it prefetches all
			 ;; the texts many times.
                         (unless (and build-review-tree
                                      kom-review-uses-cache)
                           (lyskom-prefetch-texttree text-no))
			 (lyskom-follow-comments text-stat
						 conf-stat
						 (or mark-as-read kom-review-marks-texts-as-read)
						 priority build-review-tree)))
                   (if (lyskom-text-p (cache-get-text text-no))
                       (cache-del-text text-no))
		   ))
	       (lyskom-format-insert 'no-such-text-no text-no))
             (let ((aux-items (text-stat->aux-items text-stat)))
               (while aux-items
                 (lyskom-aux-item-call (car aux-items)
                                       'read-action
                                       text-stat)
                 (setq aux-items (cdr aux-items))))
             )))
    todo))


(defun lyskom-text-is-mail-p (text-stat)
  "Ad-hoc test to determine whether a text is a mail by peeking at aux-items."
  (lyskom-match-aux-items
   (text-stat->aux-items text-stat)
   (lambda (aux-item)
     (let ((tag (aux-item->tag aux-item)))
       (or (and (> tag 15)	; 16:mx-author,	17:mx-from,	18:mx-reply-to,
		(< tag 22))	; 19:mx-to,	20:mx-cc,	21:mx-date,
	   (= 24 tag))))))	; 24:mx-headers

(defun lyskom-filter-signature-hook ()
  "Filter out the signature of imported mail messages. Most useful
when put in your `kom-view-text-hook'."
  (unless (lyskom-viewing-noconversion)
    (when (lyskom-text-is-mail-p lyskom-view-text-text-stat)
      (let* ((body (text->text-mass lyskom-view-text-text))
	     (sign (string-match "^-- $" body)))
	(when sign
	  (set-text->text-mass lyskom-view-text-text (substring body 0 sign))
	  (lyskom-signal-reformatted-text 'reformat-signature))))))


;(add-hook 'kom-view-text-hook 'lyskom-filter-signature-hook)

; review-rot13

(defun lyskom-rot13-string (s)
  (mapconcat 
   (lambda (c)
     (cond
      ((and (>= c ?a) (<= c ?z)) (string (+ ?a (% (+ 13 (- c ?a)) 26))))
      ((and (>= c ?A) (<= c ?Z)) (string (+ ?A (% (+ 13 (- c ?A)) 26))))
      (t (string c))))
   s ""))

(defun lyskom-filter-rot13 ()
  "Encrypts everything with rot-13. Least useless when put in your `kom-view-text-hook'."
  (set-text->text-mass lyskom-view-text-text 
		       (lyskom-rot13-string 
			(text->text-mass lyskom-view-text-text)))
  (lyskom-signal-reformatted-text 'reformat-rot13))


(defun lyskom-view-text-convert-UTF-8-to-ISO-8859-1 ()
  "Display rÃ¤ksmÃ¶rgÃ¥s as räksmörgås if the text has charset=utf-8,
unless we are reviewing without conversion. More precisely, the latin
1 characters are converted from their utf-8 normal form. Yet another
useful function to put in your `kom-view-text-hook'."
  (let* ((cti (lyskom-get-aux-item
	       (text-stat->aux-items lyskom-view-text-text-stat) 1))
         (content-type (if cti (aux-item->data (car cti)) "")))
    (when (and (string-match "charset\\s-*=\\s-*utf-?8" content-type)
	       (not (lyskom-viewing-noconversion)))
      (let* ((was (aref (cdr lyskom-view-text-text) 1)) ; original text version
	     (txt nil)					; converted-text-to-be
	     (get "Ã.") ; "[ÂÃ][\200-\277]", if emacsen would grok 8-bit ranges
	     (old 0)
	     (got (string-match get was))
	     ; (set nil)
	     (chr nil))
	(when got
	  (lyskom-signal-reformatted-text 'reformat-utf-8)
	  (while got
	    (setq ; set (aref was got)
		  chr (aref was (1+ got))
		  txt (nconc txt (list (substring was old got)
				       (char-to-string ;(if (eq set ?Â) chr 
					(+ chr 64))))
		  old (+ 2 got)
		  got (string-match get was old)))
	  (setq txt (apply 'concat (nconc txt (list (substring was old)))))
	  (setq txt (replace-in-string txt "Â" "")) ; sigh
	  (aset (cdr lyskom-view-text-text) 1 txt))))))

;(add-hook 'kom-view-text-hook 'lyskom-view-text-convert-UTF-8-to-ISO-8859-1)


(defun lyskom-view-text-convert-ISO-646-SE-to-ISO-8859-1 ()
  "Display r{ksm|rg}s as räksmörgås unless the text is an imported mail
or we are reviewing without conversion. In other words, the characters
][\\}{| are converted, however ^~@` are not. Yet another useful function
to put in your `kom-view-text-hook'."
  ;; First the hard part - should we patch the text
  ;; in the text object?
  (unless (or (lyskom-viewing-noconversion)
	      (lyskom-text-is-mail-p lyskom-view-text-text-stat))
    ;; Yes, modify the text from the text-object (stored in mod)
    (let* ((mod (aref (cdr lyskom-view-text-text) 1))
	   (brk (string-match "\n" mod))
	   (sbj (if brk (substring mod 0 brk) mod))
	   (txt (if brk (substring mod (1+ brk)) ""))
	   (tbl '((?\] . ?\305)          ; Å
		  (?\[ . ?\304)          ; Ä
		  (?\\ . ?\326)          ; Ö
		  (?\} . ?\345)          ; å
		  (?\{ . ?\344)          ; ä
		  (?\| . ?\366))))       ; ö
      (when (and (not (string-match "[\200-\377]" txt))
		 (or (string-match "[A-Za-z][][\\}{|]" txt)
		     (string-match "[][\\}{|][A-Za-z]" txt)
		     (string-match "[A-Za-z] +[][\\}{|]" txt)))
	(lyskom-signal-reformatted-text 'reformat-deswascii)
	(while (string-match "\\([][\\}{|]\\)" txt)
	  (let ((old-char (aref (match-string 1 txt) 0)))
	    (aset txt (match-beginning 1) (cdr (assoc old-char tbl)))))
	(aset (cdr lyskom-view-text-text) 1
	      (if brk (concat sbj "\n" txt) sbj))))))

;(add-hook 'kom-view-text-hook 'lyskom-view-text-convert-ISO-646-SE-to-ISO-8859-1)




(defun lyskom-follow-comments (text-stat conf-stat
					 mark-as-read priority review-tree)
  "Arrange so that all comments and footnotes to this text will be read.
This will essentially fix the reading list and issue calls for the cache to be
filled.

Arguments are: TEXT-STAT CONF-STAT MARK-AS-READ PRIORITY REVIEW-TREE
TEXT-STAT is the current text.
CONF-STAT the current conference
If MARK-AS-READ is non-nil the texts will be marked as read.
PRIORITY the priority of the reading.
If REVIEW-TREE is non-nil then build an entry of type 'REVIEW-TREE in the 
lyskom-reading-list."
  ;; . Fix the reading-list
  ;; . Issue cache-filling initiate-calls for everything left comments.
  
  (let ((flist nil)
        (clist nil)
        (mx-attachments-in (lyskom-get-text-attachments text-stat)))
    (lyskom-traverse misc (text-stat->misc-info-list text-stat)
      (cond
       ((eq (misc-info->type misc) 'FOOTN-IN)
	(setq flist (cons (misc-info->footn-in misc) flist)))
       ((eq (misc-info->type misc) 'COMM-IN)
        (setq clist (cons (misc-info->comm-in misc) clist)))))
    
    (let (comments footnotes)
      ;; Find the comments that we should read and enter them into the
      ;; read-list.
      (lyskom-traverse no clist
        (cond ((or review-tree
                   kom-follow-attachments
                   (not (memq no mx-attachments-in)))
               (let ((text-stat (blocking-do 'get-text-stat no)))
                 (if (and text-stat
                          (or review-tree
                              (and (or kom-follow-comments-outside-membership
				       (lyskom-member-of-at-least-one-p (lyskom-text-recipients text-stat)))
                                   (not (lyskom-text-read-p text-stat t)))))
                     (setq comments (cons no comments)))))
              ((memq no mx-attachments-in)
               (lyskom-skip-attachments no mark-as-read))))
      (if comments
          (if review-tree
              (lyskom-review-enter-read-info
               (lyskom-create-read-info 'REVIEW-TREE
                                        conf-stat priority
                                        (lyskom-create-text-list comments)
                                        (text-stat->text-no text-stat)) t)
            (read-list-enter-read-info
             (lyskom-create-read-info 'COMM-IN
                                      conf-stat priority
                                      (lyskom-create-text-list comments)
                                      (text-stat->text-no text-stat))
             lyskom-reading-list t)))

      ;; Find the footnotes that we should read and enter them into
      ;; the read-list. A slight trick is to increase the priority so
      ;; that they will be read first.
      (lyskom-traverse no flist
	(let ((text-stat (blocking-do 'get-text-stat no)))
	  (if (or review-tree
		  (and text-stat
		       (or kom-follow-comments-outside-membership
			   (lyskom-member-of-at-least-one-p (lyskom-text-recipients text-stat)))
		       (not (lyskom-text-read-p text-stat t))))
	      (setq footnotes (cons no footnotes)))))
      (if footnotes
	  (read-list-enter-read-info
	   (lyskom-create-read-info (if review-tree 'REVIEW-TREE 'FOOTN-IN)
				    conf-stat (1+ priority)
				    (lyskom-create-text-list footnotes)
				    (text-stat->text-no text-stat))
	   lyskom-reading-list t)))))

(defun lyskom-get-text-attachments (text-stat)
  "Return a list of all attachments to TEXT-STAT."
  (mapcar
   (lambda (el)
     (string-to-number (aux-item->data el)))
   (lyskom-get-aux-item (text-stat->aux-items text-stat) 10101)))

(defun lyskom-get-text-belongs-to (text-stat &optional footnotes)
  "Return a list of all texts TEXT-STAT is an attachment to.
If optional FOOTNOTES is non-nil, include text TEXT-STAT is a footnote to."
  (append
   (mapcar
    (lambda (el) (string-to-number (aux-item->data el)))
    (lyskom-get-aux-item (text-stat->aux-items text-stat) 10100))
   (when footnotes
     (delq nil
	   (mapcar (lambda (misc)
		     (when (eq (misc-info->type misc) 'FOOTN-TO)
		       (misc-info->footn-to misc)))
		   (text-stat->misc-info-list text-stat))))))


(defun lyskom-get-root-text-belongs-to (text-stat &optional footnotes)
  "Return the top-level parent of TEXT-STAT, following attachment
and footnote (if optional FOOTNOTES is non-nil"
  (lyskom-find-dag-roots  'lyskom-get-text-belongs-to
			  (lambda (x) 
                            (or (cache-get-text-stat x)
                                (blocking-do 'get-text-stat x)))
			  (text-stat->text-no text-stat)
			  nil
			  footnotes))


(defun lyskom-skip-attachments (text-no mark-as-read)
  "Skip the hierarchy of attachments starting at text-no"
  (let ((queue (list text-no))
        (done nil)
        (tmp (make-collector))
        (result nil))
    ;; Note: this does block the client, but explicitly checks that
    ;;       it is not being called from under the parser (where it
    ;;       really doesn't make a whole lot of sense anyway), so
    ;;       we ought to be OK anyway.
    (unless lyskom-is-parsing
      (while queue
        (while queue
          (when (not (memq (car queue) done))
            (initiate-get-text-stat 'background
                                    'collector-push
                                    (car queue) 
                                    tmp)
            (setq queue (cdr queue))
            (setq done (cons (car queue) done))))
        (lyskom-wait-queue 'background)

        (setq result (nconc result (collector->value tmp)))
        (lyskom-traverse text-stat (collector->value tmp)
          (setq queue (nconc queue (lyskom-get-text-attachments text-stat)))))

      (lyskom-traverse text-stat result
        (read-list-delete-text (text-stat->text-no text-stat) lyskom-reading-list)
        (read-list-delete-text (text-stat->text-no text-stat) lyskom-to-do-list)
        (when mark-as-read
          (lyskom-mark-as-read text-stat)
          (lyskom-is-read-handler text-stat))))))



(defun lyskom-text-read-p (text-stat &optional want-passive)
  "Return t if TEXT-STAT has been marked as read in all
recipients to it that the user is a member in."
  (let* ((misc-info-list (text-stat->misc-info-list text-stat))
	 (i (length misc-info-list))
	 (res 'not-member))
    (while (and res (not (zerop i)))
      (setq i (1- i))
      (let* ((misc-info (elt misc-info-list i))
	     (type (misc-info->type misc-info)))
	(cond
	 ((memq type '(RECPT BCC-RECPT CC-RECPT))
	  ;; Is this function ever called asynchronously? If not, we
	  ;; can use lyskom-get-membership istead.
	  (let ((membership (lyskom-try-get-membership
			     (misc-info->recipient-no misc-info) want-passive))
		(loc-no (misc-info->local-no misc-info)))

	    ;; Make a note that this text really is in a group we are
	    ;; a member of.
	    (if (and res membership) (setq res t))
	    (if (and membership
		     (> loc-no (membership->last-text-read membership))
		     (not (lyskom-vmemq loc-no
					(membership->read-texts membership))))
		(setq res nil)))))))
    (if (eq res 'not-member)
        nil
      res)))


(defun lyskom-text-read-at-least-once-p (text-stat &optional bg)
  "Return t if TEXT-STAT has been marked as read in any of the recipients
the user is a member of. Uses blocking-do. Returns t if TEXT-STAT is nil.

If BG is non-nil, this function is run in the background and must not use
blocking-do."
  (if text-stat
      (let* ((misc-info-list (text-stat->misc-info-list text-stat))
             (misc-item nil)
             (type nil)
             (membership nil)
             (is-member nil)
             (result nil))
        (while misc-info-list
          (setq misc-item (car misc-info-list))
          (setq type (misc-info->type misc-item))
          (setq misc-info-list (cdr misc-info-list))
          (cond ((memq type lyskom-recpt-types-list)
                 (setq membership (if bg
                                      (lyskom-try-get-membership
                                       (misc-info->recipient-no misc-item) t)
                                    (lyskom-get-membership
                                     (misc-info->recipient-no misc-item) nil)))

                 (when membership
                   (setq is-member t)
                   (when (or (<= (misc-info->local-no misc-item)
                                 (membership->last-text-read membership))
                             (lyskom-vmemq (misc-info->local-no misc-item)
                                           (membership->read-texts
                                            membership)))
                     (setq result t)
                     (setq misc-info-list nil))))))
        (cond (result result)
              ((not is-member) (not kom-follow-comments-outside-membership))
              (t nil)))
    t))


(defun lyskom-subtract-one-day (x)
  (let ((high-x (1- (car x)))
        (low-x (car (cdr x))))
    (if (> 20864 low-x)
        (setq high-x (1- high-x)
              low-x (+ low-x 65536)))
    (setq low-x (- low-x 20864))
    (list high-x low-x nil)))

(defun lyskom-calculate-day-diff (time)
  (let* ((now (current-time))
         (yesterday (lyskom-subtract-one-day now))
         (decnow (decode-time now))
         (decthen (decode-time yesterday)))
    (cond ((and (= (time->mday time) (elt decnow 3))
                (= (time->mon time)  (elt decnow 4))
                (= (time->year time) (elt decnow 5)))
           'today)
          ((and (= (time->mday time) (elt decthen 3))
                (= (time->mon time)  (elt decthen 4))
                (= (time->year time) (elt decthen 5)))
           'yesterday)
          (t nil))))


(defun lyskom-format-text-footer (text author author-name format format-flags)
  "Format the footer of a text."
  (let* ((result "")
         (start 0)
         (format-letter nil)
         (field-width nil)
         (kom-deferred-printing nil)
         (author-name-len nil)
         (have-author (and format (string-match "%=?[0-9]*P" format))))

    (setq author-name-len (lyskom-string-width (or author-name (conf-stat->name author))))

    ;; No user-specified format, so output dashes and stuff

    (if (null format)
        (let* ((format-flag-string
                (if (and format-flags (listp format-flags))
                    (let ((first-flag t))
                      (concat
                       "("
                       (mapconcat 
                        (lambda (str)
                          (let ((face nil))
                            ;; If it's a cons, the cdr is property list
                            (when (consp str) (setq face (cdr str) str (car str)))
			    (if (stringp str)
				str
			      (setq str (lyskom-get-string str))
			      (when first-flag 
				(setq str (capitalize str))))
			    (setq first-flag nil)
                            (when face (add-text-properties 0 (length str) face str))
                            str))
                        format-flags ", ")
                       ")"))
                  ""))
               (end-dash-chars
                (- kom-text-footer-dash-length
                   (lyskom-string-width (int-to-string (text-stat->text-no text))) 
                   3                             ; Parens and space
                   (if kom-show-author-at-end
                       (+ author-name-len 2) 
                     0)
                   (lyskom-string-width format-flag-string)))
               (end-dash (if (> end-dash-chars 0)
                             (make-string end-dash-chars ?-)
                           "")))
          (lyskom-format
           (cond ((and kom-dashed-lines kom-show-author-at-end)
                  "(%#1n) /%#2P/%#3s%#4s")
                 (kom-dashed-lines
                  "(%#1n) %#3s%#4s")
                 (kom-show-author-at-end
                  "(%#1n) /%#2P/")
                 (t
                  "(%#1n)"))
           text
           (or author-name author)
           end-dash
           format-flag-string))

      ;; User-specified format. Do all kinds of weird stuff

      (while (string-match "%\\(=?-?[0-9]+\\)?\\([-nPpf% ]\\)" format start)
        (setq result (concat result (substring format start 
                                               (match-beginning 0))))
        (setq format-letter (aref format (match-beginning 2)))
        (setq field-width (match-string 1 format))
        (when (null field-width) (setq field-width ""))
        (setq start (match-end 0))
        (setq result
              (concat 
               result
               (cond
                ((eq format-letter ?p)
                 (lyskom-format (format "%%%s#1p" field-width) 
                                (or author (text-stat->author text))))

                ((eq format-letter ?P)
                 (lyskom-format (format "%%%s#1P" field-width) 
                                (or author author-name)))

                ((eq format-letter ?n)
                 (lyskom-format (format "%%%s#1n" field-width) 
                                (text-stat->text-no text)))

                ((eq format-letter ?f)
                 (if format-flags
                     (lyskom-format (format "%%%s#1s" field-width) 
                                    (lyskom-get-string format-flags))
                   ""))

                ((eq format-letter ?-)
                 (let ((width 
                        (cond ((null field-width) 42)
                              ((string= "" field-width) 42)
                              ((eq ?= (aref field-width 0))
                               (lyskom-string-to-number (substring field-width 1)))
                              (t (lyskom-string-to-number field-width)))))
                 
                   (lyskom-format 
                    "%#1s"
                    (if have-author
                        (if (< author-name-len width)
                            (make-string (- width author-name-len) ?-)
                          "")
                      (make-string width ?-)))))

                ((eq format-letter ?%)
                 "%")
                (t (concat "%" field-width (make-string 1 format-letter))))))
        )
      (setq result (concat result (substring format start)))
      (if (string-match "[ \t]+\\'" result)
          (substring result 0 (match-beginning 0))
        result))))



(defun lyskom-deferred-insert-footer (conf-stat defer-info)
  "Insert the name of a conference at a previously reserved place."
  (let* ((text-stat (elt (defer-info->data defer-info) 0))
         (format-flags (elt (defer-info->data defer-info) 1))
         (mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 17)))
         (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 16)))
         (name (cond (conf-stat nil)
                      ((= (defer-info->call-par defer-info) 0)
                       (lyskom-get-string 'person-is-anonymous))
                      (t (lyskom-format 'person-does-not-exist
                                        (text-stat->author text-stat))))))

    (setq name (or (lyskom-format-mx-author mx-from mx-author) name))

    (lyskom-replace-deferred defer-info 
                             (lyskom-format-text-footer text-stat
                                                        conf-stat
                                                        name
                                                        kom-text-footer-format
                                                        format-flags))))


(defun lyskom-print-text (text-stat text mark-as-read text-no flat-review)
  "Print a text. The header must already be printed.
Print an error message if TEXT-STAT or TEXT is nil.
Mark the text as read if (and only if) MARK-AS-READ is non-nil.
Args: TEXT-STAT TEXT MARK-AS-READ TEXT-NO FLAT-REVIEW."
  (let* ((lyskom-current-function 'lyskom-print-text)
         (cti (lyskom-get-aux-item (text-stat->aux-items text-stat) 1))
         (content-type (and cti (aux-item->data (car cti)))))
    (cond
     ((or (null text)
          (null text-stat))
      (lyskom-format-insert 'no-such-text-no text-no)
      (setq lyskom-previous-text lyskom-current-text)
      (setq lyskom-current-text text-no))
     (t
      (when (eq (text-stat->author text-stat) lyskom-pers-no)
        (setq lyskom-last-seen-written (text-stat->text-no text-stat)))
      (let* ((str (text->decoded-text-mass text text-stat))
	     (truncated nil)
             ;; s1 s2 t1 t2
	     body)

        (cond
         ((and (string-match "\n" str)
               (not (equal "x-kom/user-area" content-type)))
          (setq lyskom-current-subject (substring str 0 (match-beginning 0)))
          (setq body (substring str (match-end 0)))
          (lyskom-insert-string 'head-Subject)
          (let ((lyskom-current-function-phase 'subject))
            (lyskom-format-insert "%#1r\n" 
                                  (cons text-stat 
                                        (copy-sequence
                                         lyskom-current-subject))))
          (lyskom-format-insert 
           "%#2$%#1s\n"
           (if kom-dashed-lines
               (make-string kom-text-header-dash-length ?-)
             "")
           (when kom-highlight-dashed-lines
             `(face ,(or kom-dashed-lines-face
                         lyskom-default-dashed-lines-face))))

          ;; (setq t1 (point-max))

	  ;; Truncate body if flat-review and long text
	  (when (and flat-review
                     (numberp kom-truncate-show-lines)
                     (numberp kom-truncate-threshold))
            (let* ((truncated-body
                    (lyskom-truncate-to-lines body
                                              kom-truncate-threshold
                                              (min kom-truncate-show-lines
                                                   kom-truncate-threshold))))
              (unless (eq truncated-body body)
                (setq body truncated-body
                      truncated t))))

          (let ((lyskom-current-function-phase 'body))
            (lyskom-format-insert "%#2$%#1t\n" 
                                  (cons text-stat body)
                                  (and kom-highlight-text-body
                                       `(face ,(or kom-text-body-face
                                                   lyskom-default-text-body-face))))
            )

	  ;; Indicate that the text was truncated
	  (if truncated
              (lyskom-signal-reformatted-text
               `(reformat-truncated . (face ,kom-warning-face))))


          ;; (setq t2 (point-max))
	  )
         (t                             ;No \n found. Don't print header.
          (lyskom-format-insert 
           "%#2$%#1s\n"
           (if kom-dashed-lines
               (make-string kom-text-header-dash-length ?-)
             "")
           (when kom-highlight-dashed-lines
             `(face ,(or kom-dashed-lines-face
                         lyskom-default-dashed-lines-face))))
          (lyskom-format-insert "%#2$%#1t\n"
                                (cons text-stat str)
                                (and kom-highlight-text-body
                                       `(face ,(or kom-text-body-face
                                                   lyskom-default-text-body-face))))
          (setq lyskom-current-subject "")))
        (sit-for 0)
        (let* ((lyskom-current-function-phase 'footer)
               (mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 17)))
               (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 16)))
               (author-name (lyskom-format-mx-author mx-from mx-author)))

          (if (and kom-deferred-printing
                   (or kom-text-footer-format
                       kom-show-author-at-end))
              (progn
                (lyskom-format-insert "%#2$%#1s\n"
                                      lyskom-defer-indicator
                                      (when kom-highlight-dashed-lines
                                        `(face ,(or kom-dashed-lines-face
                                                    lyskom-default-dashed-lines-face))))
                (lyskom-defer-insertion
                 (lyskom-create-defer-info
                  'get-conf-stat
                  (text-stat->author text-stat)
                  'lyskom-deferred-insert-footer
                  (set-marker (make-marker)
                              (- (point-max) 
                                 (length lyskom-defer-indicator)
                                 1))
                  (length lyskom-defer-indicator)
                  "%#1s"
                  (list text-stat lyskom-last-text-format-flags))))
            (let* ((conf-stat (and (or kom-text-footer-format
                                       kom-show-author-at-end)
                                   (not (eq 0 (text-stat->author text-stat)))
                                   (blocking-do 'get-conf-stat 
                                                (text-stat->author text-stat)))))
              (lyskom-format-insert
               "%#2$%#1s\n"
               (lyskom-format-text-footer 
                text-stat
                conf-stat
                (cond (author-name author-name)
                      ((eq (text-stat->author text-stat) 0)
                       (lyskom-get-string 'person-is-anonymous))
                      (conf-stat (conf-stat->name conf-stat))
                      (t (lyskom-format 'person-does-not-exist 
                                        (text-stat->author text-stat))))
                kom-text-footer-format
                lyskom-last-text-format-flags)
               (when kom-highlight-dashed-lines
                 `(face ,(or kom-dashed-lines-face
                             lyskom-default-dashed-lines-face)))))))
        (if mark-as-read
            (lyskom-mark-as-read text-stat))
        (setq lyskom-previous-text lyskom-current-text)
        (setq lyskom-current-text (text-stat->text-no text-stat)))))))


(defun lyskom-mark-as-read (text-stat)
  "Mark a text as read in all conferences that are recipients.
Tell the server that it is read.

Args: TEXT-STAT

This function does not remove the text from the internal structures of
the client. That is done by lyskom-is-read."
  (let ((misc-info-list (text-stat->misc-info-list text-stat)))
    (lyskom-traverse
     misc-info misc-info-list
     (if (and (memq (misc-info->type misc-info) lyskom-recpt-types-list)
	      ;; The whole membership list might not be fetched
	      ;; yet. So we better mark it as read in all conferences.
	      ;; (lyskom-member-p (misc-info->recipient-no misc-info))
	      )
	 (initiate-mark-as-read 'background 
                                nil
				(misc-info->recipient-no misc-info)
				(list (misc-info->local-no misc-info))
                                text-stat
                                (misc-info->recipient-no misc-info)
                                (list (misc-info->local-no misc-info)))))))

(defun lyskom-print-header-recpt (conf-no misc)
  "Print a line of info about a recipient (or cc-recipient) of a text."
  (lyskom-format-insert "%#1s: %#2M <%#3d>\n"
			(cond ((eq (misc-info->type misc) 'RECPT)
			       (lyskom-get-string 'Recipient))
                              ((eq (misc-info->type misc) 'BCC-RECPT)
                               (lyskom-get-string 'Hidden-recipient))
                              ((eq (misc-info->type misc) 'CC-RECPT)
                               (lyskom-get-string 'Extra-recipient))
                              (t (lyskom-get-string 'Strange-recipient)))
			conf-no
			(misc-info->local-no misc))
  (if (misc-info->sent-at misc)
      (lyskom-format-insert 'send-at
			    (lyskom-format-time
                             'date-and-time
			     (misc-info->sent-at misc))))
  (if (misc-info->sender misc)
      (lyskom-format-insert 'sent-by (misc-info->sender misc)))
  (if (misc-info->rec-time misc)
      (lyskom-format-insert
       'received-at
       (lyskom-format-time 'date-and-time (misc-info->rec-time misc)))))




(defun lyskom-view-text-handle-saved-comments (text-stat)
  "Ask server for what is needed to print the references to commenting texts 
and prints the references. Used at the bottom of the text.
Args: TEXT-STAT of the text being read."
  (lyskom-traverse misc
      (text-stat->misc-info-list text-stat)
    (let ((type (misc-info->type misc)))
      (cond
       ((eq type 'COMM-IN)
        (lyskom-print-header-comm (misc-info->comm-in misc) misc text-stat))
       ((eq type 'FOOTN-IN)
	(lyskom-print-header-comm (misc-info->footn-in misc) misc text-stat))))))



(defun lyskom-print-header-comm (text misc read-text-stat)
  "Get author of TEXT-NO and print a header line."
  (let ((text-stat (if kom-deferred-printing
		       (cache-get-text-stat text)
		     (blocking-do 'get-text-stat text))))

    ;; Print information about the link
    (cond (text-stat
           (lyskom-insert-header-comm text-stat misc read-text-stat))
          ((not kom-deferred-printing) 
           (lyskom-insert-header-comm text-stat misc read-text-stat))
          (t
           (let ((defer-info (lyskom-create-defer-info
                              'get-text-stat
                              text
                              'lyskom-insert-deferred-header-comm
                              (point-max-marker)
                              (length lyskom-defer-indicator)
                              nil       ; Filled in later
                              (list misc read-text-stat))))
             (lyskom-format-insert "%#1s\n" lyskom-defer-indicator)
             (lyskom-defer-insertion defer-info))))

    ;; Print information about who added the link
    (if (misc-info->sent-at misc)
	(lyskom-format-insert 'send-at (lyskom-format-time
                                        'date-and-time
					(misc-info->sent-at misc))))
    (if (misc-info->sender misc)
	(lyskom-format-insert 'sent-by (misc-info->sender misc)))))


(defun lyskom-insert-deferred-header-comm (text-stat defer-info)
  (let* ((author (if text-stat (text-stat->author text-stat) nil))
	 (misc (elt (defer-info->data defer-info) 0))
	 (read-text-stat (elt (defer-info->data defer-info) 1))
	 (type (misc-info->type misc))
         (mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat)
                                            17)))
         (read-mx-from (car (lyskom-get-aux-item (text-stat->aux-items read-text-stat)
                                                 17)))
         (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat)
                                              16)))
         (mx-attachments-in (lyskom-get-text-attachments read-text-stat))
         (mx-belongs-to (mapcar
                         (lambda (el)
                           (string-to-number (aux-item->data el)))
                         (lyskom-get-aux-item (text-stat->aux-items 
                                               read-text-stat) 10100)))
         (content-type "")
         (read-content-type "")
	 fmt data)

    (setq author (or (lyskom-format-mx-author mx-from mx-author) author))
    ;; Extract the content type

    (if (and mx-from
             (setq content-type
                   (car (lyskom-get-aux-item (text-stat->aux-items 
                                              text-stat) 1))))
        (progn (string-match "^\\(\\S-+\\)" (aux-item->data content-type))
               (setq content-type (format "(%s) "
                                          (aux-item->data content-type))))
      (setq content-type ""))
    
    (if (and read-mx-from
             (setq read-content-type
                   (car (lyskom-get-aux-item (text-stat->aux-items 
                                              read-text-stat) 1))))
        (progn (string-match "^\\(\\S-+\\)" (aux-item->data read-content-type))
               (setq read-content-type (format "(%s) "
                                          (aux-item->data read-content-type))))
      (setq read-content-type ""))
    
    (cond
     ((eq type 'COMM-TO)
      (setq fmt (cond ((memq (misc-info->comm-to misc) mx-belongs-to)
                       'attachment-to-text)
                      (author 'comment-to-text-by)
                      (t 'comment-to-text))
            content-type read-content-type
	    data (misc-info->comm-to misc)))
     ((eq type 'FOOTN-TO)
      (setq fmt (cond ((memq (misc-info->footn-to misc) mx-belongs-to)
                       'attachment-to-text)
                      (author 'footnote-to-text-by)
                      (t 'footnote-to-text))
            content-type read-content-type
	    data (misc-info->footn-to misc)))
     ((eq type 'COMM-IN)
      (setq fmt (cond ((memq (misc-info->comm-in misc) mx-attachments-in) 
                       'attachment-in-text)
                      (author 'comment-in-text-by)
                      (t 'comment-in-text))
	    data (misc-info->comm-in misc)))
     ((eq type 'FOOTN-IN)
      (setq fmt (cond ((memq (misc-info->footn-in misc) mx-attachments-in)
                       'attachment-in-text)
                      (author 'footnote-in-text-by)
                      (t 'footnote-in-text))
	    data (misc-info->footn-in misc)))) 
    (set-defer-info->format defer-info fmt)
    ; Note: author is ignored if fmt is not *-by
    (lyskom-replace-deferred defer-info data author content-type)))



(defun lyskom-insert-header-comm (text-stat misc read-text-stat)
  "Get author of TEXT-NO and print a header line."
  ;;+++ error kommer att se annorlunda ut.
  (let ((author (if text-stat
		    (text-stat->author text-stat)
		  nil))
        (mx-from (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 17)))
        (read-mx-from (car (lyskom-get-aux-item (text-stat->aux-items read-text-stat) 17)))
        (mx-author (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 16)))
        (mx-attachments-in (lyskom-get-text-attachments read-text-stat))
        (mx-belongs-to (mapcar
                        (lambda (el)
                          (string-to-number (aux-item->data el)))
                        (lyskom-get-aux-item (text-stat->aux-items read-text-stat) 10100)))
        (content-type "")
        (read-content-type "")
        (is-attachment nil)
        (type (misc-info->type misc)))

    (setq author (or (lyskom-format-mx-author mx-from mx-author) author))

    ;; Extract the content type

    (if (and mx-from
             (setq content-type
                   (car (lyskom-get-aux-item (text-stat->aux-items 
                                              text-stat) 1))))
        (progn (string-match "^\\(\\S-+\\)" (aux-item->data content-type))
               (setq content-type (format "(%s) "
                                          (aux-item->data content-type))))
      (setq content-type ""))

    (if (and read-mx-from
             (setq read-content-type
                   (car (lyskom-get-aux-item (text-stat->aux-items 
                                              read-text-stat) 1))))
        (progn (string-match "^\\(\\S-+\\)" (aux-item->data read-content-type))
               (setq read-content-type (format "(%s) "
                                          (aux-item->data read-content-type))))
      (setq read-content-type ""))

    
    (cond
     ((eq type 'COMM-TO)
      (lyskom-format-insert (if (and (memq (misc-info->comm-to misc) mx-belongs-to)
                                     (setq is-attachment t))
                                'attachment-to-text
                              'comment-to-text)
                            (misc-info->comm-to misc)
                            nil
                            read-content-type))
     ((eq type 'FOOTN-TO)
      (lyskom-format-insert (if (and (memq (misc-info->footn-to misc) mx-belongs-to)
                                     (setq is-attachment t))
                                'attachment-to-text
                              'footnote-to-text)
                            (misc-info->footn-to misc)
                            nil
                            read-content-type))
     ((eq type 'COMM-IN)
      (lyskom-format-insert (if (and (memq (misc-info->comm-in misc) mx-attachments-in)
                                     (setq is-attachment t))
                                'attachment-in-text
                              'comment-in-text)
                            (misc-info->comm-in misc)
                            nil
                            content-type))
     ((eq type 'FOOTN-IN)
      (lyskom-format-insert (if (and (memq (misc-info->footn-in misc) mx-attachments-in)
                                     (setq is-attachment t))
                                'attachment-in-text
                              'footnote-in-text)
                            (misc-info->footn-in misc)
                            nil
                            content-type)))
    (if (and author (not is-attachment))
	(lyskom-format-insert 'written-by author)
      (lyskom-insert "\n"))))

(defun lyskom-format-mx-author (mx-from mx-author)
  (let ((author nil)
        (mx-author (if (and mx-author (equal (aux-item->data mx-author) ""))
                       nil
                     mx-author)))
    (when (or mx-from mx-author)
      (setq author (lyskom-get-string 'email-name-prefix))
      (when mx-author (setq author (concat author (aux-item->data mx-author))))
      (when (and mx-from mx-author) (setq author (concat author " ")))
      (when mx-from
        (setq author (concat author
                      (lyskom-format 
                       "<%[%#1@%#2s%]>"
                       (lyskom-default-button 'email (aux-item->data mx-from))
                       (aux-item->data mx-from)))))
      (setq author (concat (lyskom-get-string 'email-name-suffix) author))
      )
    author))

(defun lyskom-format-mx-date (mx-date)
  (let ((date (lyskom-mx-date-to-time mx-date)))
    (if date
        (let ((tem (lyskom-format-time 'date-and-time date)))
          (if (or kom-show-mx-date-as-local (null (time->zone date)))
              tem
            (concat tem " " (lyskom-format-timezone (time->zone date)))))
      (aux-item->data mx-date))))




(defun lyskom-mx-date-to-time (mx-date)
  (when mx-date
    (let* ((date (parse-time-string (aux-item->data mx-date)))
           (now (decode-time))
           (sec (or (elt date 0) 0))
           (min (or (elt date 1) 0))
           (hour (or (elt date 2) 0))
           (day (or (elt date 3) (elt now 3)))
           (month (or (elt date 4) (elt now 4)))
           (year (or (elt date 5) (elt now 5)))
           (wday (elt date 6))
           (dst (elt date 7))
           (zone (elt date 8))
	   (convdate (decode-time (encode-time sec min hour day month year
					       wday dst zone))))
      (setq date
	    (if kom-show-mx-date-as-local
		convdate
	      (list sec min hour day month year
		    (or wday (elt convdate 6))
		    dst zone)))
      (lyskom-create-time (elt date 0)
                          (elt date 1)
                          (elt date 2)
                          (elt date 3)
                          (elt date 4)
                          (elt date 5)
                          (elt date 6)
                          0
                          (elt date 7)
                          (elt date 8)))))


;;; Local Variables: 
;;; eval: (put 'lyskom-traverse 'lisp-indent-hook 2)
;;; end: 
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: async.el
;;;;
;;;; These functions implement a nice service that give the user
;;;; continuous messages about what other users are doing and what
;;;; is happening inside the lyskom server.
;;;;
;;;; Author: Linus Tolke
;;;; Entry:  Inge Wallin
;;;;


(defun lyskom-is-ignoring-async (buffer message &rest args)
  (save-current-buffer
    (set-buffer buffer)
    (let ((tmp (assq message lyskom-ignoring-async-list)))
      (and tmp (equal args (cdr tmp))))))

(defun lyskom-parse-async (tokens buffer)
  "Parse an asynchronous message from the server.
The message consists of TOKENS tokens. Unknown messages are skipped.
Actions are taken to perform the various tasks that is required on reciept of
an asynchronous message.

If variable `kom-presence-messages-in-echo-area' is non-nil or some
minibuffer editing is going on then nothing is printed on the message
area. This function is called with the lyskom-unparsed-buffer as
current-buffer. All calls using the lyskom-variables have to be made
using the buffer BUFFER.

Be careful when editing this. All parsing is done with the buffer this
function is called with as the current-buffer, while all calls from
this function shall be with current-buffer the BUFFER."
  (let ((msg-no (lyskom-parse-num)))
    (cond
     ((or (eq msg-no 0)
          (eq msg-no 15)) ; New text
      (let* ((text-no (lyskom-parse-num))
	     (text-stat (if (eq msg-no 0)
                            (lyskom-parse-text-stat-old text-no)
                          (lyskom-parse-text-stat text-no))))
	(lyskom-save-excursion
	 (set-buffer buffer)
	 (lyskom-async-new-text text-stat)))) ;

     ((eq msg-no 1)			; Logout (obsolete)
      (lyskom-skip-tokens tokens))

     ((eq msg-no 2)			; Login, obsolete.
      (lyskom-skip-tokens tokens))

     ((eq msg-no 3)			; Conference deleted
      (lyskom-skip-tokens tokens))

     ((eq msg-no 4)			; Conference created
      (lyskom-skip-tokens tokens))

     ((eq msg-no 5)			; A person or conference has
					; changed name.
      (let ((conf-no (lyskom-parse-num))
	    (old-name (lyskom-parse-string))
	    (new-name (lyskom-parse-string)))
	(lyskom-save-excursion
	 (set-buffer buffer)
	 (if (and lyskom-pers-no (= conf-no lyskom-pers-no))
	     (lyskom-format-insert-before-prompt 
	      'you-changed-name-to
	      new-name
	      (lyskom-default-button 'conf conf-no)))
	 (let ((cached-stat (cache-get-conf-stat conf-no))
               (cached-ustat (cache-get-uconf-stat conf-no)))
	   (when cached-stat
             (set-conf-stat->name cached-stat new-name))
	   (when cached-ustat
             (set-uconf-stat->name cached-ustat new-name)))
	 (cond
	  ((lyskom-is-in-minibuffer))
          ((lyskom-show-presence conf-no kom-presence-messages-in-echo-area)
	   (lyskom-message "%s" (lyskom-format 'name-has-changed-to-name
					       old-name new-name))))
	 (cond
	  ((lyskom-show-presence conf-no kom-presence-messages-in-buffer)
	   (lyskom-format-insert-before-prompt
	    'name-has-changed-to-name-r 
	    old-name 
	    new-name
	    (lyskom-default-button 'conf conf-no)
	    (and kom-text-properties
		 (list 'face kom-presence-face)))))

         ;; Update in the mship-edit buffer

         (lp--maybe-update-entry-for-conf conf-no))))

     ((eq msg-no 6)			;i_am_on - something is moving
      (lyskom-parse-who-info))

     ((eq msg-no 7)			; Database is syncing.
      (lyskom-save-excursion
       (set-buffer buffer)
       ;; I removed the test for kom-presence-messages /david
       (if (and (not (lyskom-is-in-minibuffer))
                kom-show-sync-messages)
	   (lyskom-message "%s" (lyskom-get-string 'database-sync)))
       (setq mode-line-process (lyskom-get-string 'mode-line-saving))
       (setq lyskom-is-saving t)
       ;; I guess the following two lines could be replaced by
       ;; force-mode-line-update in a modern emacs.
       (set-buffer-modified-p (buffer-modified-p))
       (sit-for 0)
       (if (not lyskom-pending-calls)
	   (initiate-get-time 'async nil))))
     
     ((eq msg-no 8)			; Forced leave conference
      (let ((conf-no (lyskom-parse-num)))
        (unless (lyskom-is-ignoring-async buffer 8 conf-no)
          (lyskom-save-excursion
              (set-buffer buffer)
              (initiate-get-conf-stat 'follow
                                      'lyskom-async-forced-leave-conf
                                      conf-no
                                      conf-no)))))
     
     ((eq msg-no 9)			; A person has logged in
      (let ((pers-no (lyskom-parse-num))
	    (session-no (lyskom-parse-num)))
	(lyskom-save-excursion
	  (set-buffer buffer)
	  (if (and lyskom-pers-no
		   (not (zerop lyskom-pers-no)))
	      (let ((c (make-collector)))
		(initiate-get-conf-stat 'follow 'collector-push pers-no c)
		(initiate-get-static-session-info 'follow 'collector-push 
						  session-no c)
		(lyskom-run 'follow (lambda (c)
				      (lyskom-show-logged-in-person 
				       (elt (collector->value c) 1)
				       (elt (collector->value c) 0)))
			    c))))))

     ;; msg-no 10 is the old broadcast message. No longer used.
     
     ((eq msg-no 11)
      (lyskom-save-excursion
       (set-buffer buffer)
       (lyskom-insert-before-prompt (lyskom-get-string-sol 'lyskom-is-full))
       ))
     
     
     ((eq msg-no 12)			; Message to the user (or everybody)
      (let ((recipient (lyskom-parse-num))
	    (sender (lyskom-parse-num))
	    (message (lyskom-parse-string)))
	(lyskom-save-excursion
	 (set-buffer buffer)
	 (if (zerop recipient)
	     (initiate-get-conf-stat 'async
				     'lyskom-handle-personal-message
				     sender
				     0
				     message)
	   (lyskom-collect 'async)
	   (initiate-get-conf-stat 'async nil sender)
	   (initiate-get-conf-stat 'async nil recipient)
	   (lyskom-use 'async 'lyskom-handle-personal-message message)))))
     
     ((eq msg-no 13)			; New logout
      (let ((pers-no (lyskom-parse-num))
	    (session-no (lyskom-parse-num)))
	(lyskom-save-excursion
	 (set-buffer buffer)
	 (if (and lyskom-pers-no
		  (not (zerop lyskom-pers-no))
		  (or (lyskom-show-presence pers-no kom-presence-messages-in-echo-area)
		      (lyskom-show-presence pers-no kom-presence-messages-in-buffer)))
	     (initiate-get-conf-stat 'follow
				     'lyskom-show-logged-out-person
				     pers-no session-no)))))

     ((eq msg-no 14)                    ; Deleted text
      (let* ((text-no (lyskom-parse-num))
            (text-stat (lyskom-parse-text-stat text-no)))
        (lyskom-save-excursion
         (set-buffer buffer)
         (lyskom-async-deleted-text text-stat))))

     ((eq msg-no 16)                    ; New recipient
      (let ((text-no (lyskom-parse-num))
            (conf-no (lyskom-parse-num))
            (misc-type (lyskom-parse-num)))
        (cond ((eq misc-type 0) (setq misc-type 'RECPT))
              ((eq misc-type 1) (setq misc-type 'CC-RECPT))
              ((eq misc-type 15) (setq misc-type 'BCC-RECPT)))
        (when (symbolp misc-type)
          (lyskom-save-excursion
            (set-buffer buffer)
            (cache-del-text-stat text-no)
            (cache-del-conf-stat conf-no)
            (initiate-get-text-stat 'follow
                                    'lyskom-async-new-recipient
                                    text-no
                                    text-no conf-no misc-type)
            ))))

     ((eq msg-no 17)                    ; Deleted recipient
      (let ((text-no (lyskom-parse-num))
            (conf-no (lyskom-parse-num))
            (misc-type (lyskom-parse-num)))
        (lyskom-save-excursion
         (set-buffer buffer)
          (cache-del-conf-stat conf-no)
          (cache-del-text-stat text-no)

	  (let ((membership (lyskom-try-get-membership conf-no)))
	    (when (and membership
		       (lyskom-visible-membership membership))
	      (read-list-delete-text text-no lyskom-to-do-list)
	      (read-list-delete-text text-no lyskom-reading-list)))
          (lyskom-ignore misc-type)
         )))

     ((eq msg-no 18)                    ; New membership
      (let ((pers-no (lyskom-parse-num))
            (conf-no (lyskom-parse-num)))
        (unless (lyskom-is-ignoring-async buffer 18 pers-no conf-no)
          (lyskom-save-excursion
              (set-buffer buffer)
              (cache-del-pers-stat pers-no)
              (cache-del-conf-stat conf-no)
              (when (eq pers-no lyskom-pers-no)
                (lyskom-collect 'follow)
                (initiate-get-conf-stat 'follow nil conf-no)
                (initiate-query-read-texts 'follow nil pers-no conf-no t 0)
                (lyskom-use 'follow 'lyskom-async-new-membership pers-no conf-no))
            ))))

     ((eq msg-no 19)                    ; async-new-user-area
      (let* ((pers-no (lyskom-parse-num))
             (old-user-area (lyskom-parse-num))
             (new-user-area (lyskom-parse-num)))
        (lyskom-save-excursion
          (set-buffer buffer)
          (when (and (eq pers-no lyskom-pers-no)
                     (not (eq lyskom-current-user-area new-user-area)))
            (initiate-get-pers-stat 'follow 
                                    'lyskom-async-new-user-area
                                    pers-no
                                    old-user-area
                                    new-user-area)))))

     ((eq msg-no 20)                    ; async-new-presentation
      (let* ((conf-no (lyskom-parse-num))
             (old-pres (lyskom-parse-num))
             (new-pres (lyskom-parse-num)))
        (lyskom-ignore old-pres)
        (lyskom-save-excursion
          (set-buffer buffer)
          (when (cache-get-conf-stat conf-no)
            (set-conf-stat->presentation (cache-get-conf-stat conf-no)
                                         new-pres)
            (lp--maybe-update-entry-for-conf conf-no)))))

     ((eq msg-no 21)                    ; async-new-motd
      (let* ((conf-no (lyskom-parse-num))
             (old-motd (lyskom-parse-num))
             (new-motd (lyskom-parse-num)))
        (lyskom-save-excursion
          (set-buffer buffer)
          (lyskom-ignore old-motd)
          (when (cache-get-conf-stat conf-no)
            (set-conf-stat->msg-of-day (cache-get-conf-stat conf-no)
                                       new-motd)))))

     ((eq msg-no 22)                    ; async-text-aux-changes
      (let* ((text-no (lyskom-parse-num))
             (deleted-aux (lyskom-parse-aux-item-list))
             (added-aux (lyskom-parse-aux-item-list))
             (text-stat nil))
        (lyskom-save-excursion
          (set-buffer buffer)
          (setq text-stat (cache-get-text-stat text-no))
          (when text-stat
            (set-text-stat->aux-items text-stat
                                      (lyskom-aux-item-modify-list
                                       (text-stat->aux-items text-stat)
                                       deleted-aux
                                       added-aux))))))
     (t
      (lyskom-skip-tokens tokens)))))

(defun lyskom-async-new-user-area (pers-stat old-user-area new-user-area)
  (when pers-stat
    (let ((need-reread (and (eq lyskom-pers-no (pers-stat->pers-no pers-stat))
                            (not (eq lyskom-current-user-area new-user-area)))))
  ;; Update the cache
  (set-pers-stat->user-area pers-stat new-user-area)
  ;; Re-read
  (when (and need-reread
             new-user-area
             (not (zerop new-user-area)))
    (initiate-get-text 'follow (lambda (text)
                                 (when text
                                   (lyskom-format-insert-before-prompt 'reading-settings-from-server)
                                   (setq lyskom-saved-unknown-variables
                                    (lyskom-read-options-eval text))))
                       new-user-area)))))


(defun lyskom-async-forced-leave-conf (conf-stat conf-no)
  (if conf-stat
      (lyskom-format-insert-before-prompt 'no-longer-member conf-stat)
    (lyskom-format-insert-before-prompt 'no-longer-member-n conf-no))
  (lyskom-remove-membership conf-no)
  (when (eq conf-no lyskom-current-conf)
    (lyskom-leave-current-conf))
  (read-list-delete-read-info conf-no lyskom-to-do-list)
  (lyskom-update-prompt))

(defun lyskom-async-new-membership (conf-conf-stat
                                    membership
                                    pers-no 
                                    conf-no)
  ;; Are we already members?

  (when membership
    (let ((cur-mship (lyskom-try-get-membership conf-no t))
          (mship-type (membership->type membership)))
      (unless cur-mship
        (lyskom-format-insert-before-prompt 
         (cond ((membership-type->invitation mship-type)
                'have-become-invited-member)
               ((membership-type->passive mship-type)
                'have-become-passive-member)
               (t 'have-become-member))
         conf-conf-stat))

    (cond ((membership-type->passive (membership->type membership))
           (lyskom-replace-membership membership)
           (when (eq conf-no lyskom-current-conf)
             (lyskom-leave-current-conf))
           (read-list-delete-read-info conf-no lyskom-to-do-list)
           (lyskom-update-prompt))

          ;; Already a member. Perhaps the priority changed.
          ;; Update the cache. The reading list is probably also
          ;; not quite correct since the priority might have changed

          (cur-mship
           (lyskom-replace-membership membership)
           (lyskom-sort-to-do-list))

          ;; Not a member. Completely new. Deal with it.
          (t (lyskom-add-membership membership conf-no))))))
    

(defun lyskom-show-presence (num flag)
  "Returns non-nil if presence messages for NUM should be displayed
according to the value of FLAG."
  (cond ((null flag) nil)
        ((eq flag 'friends) (memq num kom-friends))
        ((eq flag 'morons) (memq num kom-morons))
        ((eq flag 'friends-and-morons) (or (memq num kom-friends)
                                           (memq num kom-morons)))
        ((listp flag) (lyskom-indirect-assq num flag))
        (t t)))


(defun lyskom-show-logged-in-person (conf-stat session-info)
  "Visa p} kommandoraden vem som loggat in."
  (let ((server (lyskom-session-nickname))
	(login-when-date (let ((kom-print-relative-dates nil))
			   (lyskom-format-time 'date)))
	(login-when-time (let ((kom-print-relative-dates nil))
			   (lyskom-format-time 'time))))
    (cond
     ((lyskom-is-in-minibuffer))
     ((lyskom-show-presence (conf-stat->conf-no conf-stat)
                            kom-presence-messages-in-echo-area)
      (lyskom-message
       "%s"
       (lyskom-format 'has-entered
                      (or conf-stat
                          (lyskom-get-string 'unknown-person))
                      server
                      ))))

    (cond
     ((lyskom-show-presence (conf-stat->conf-no conf-stat)
                            kom-presence-messages-in-buffer)
      (lyskom-format-insert-before-prompt 
       'has-entered-r
       (or conf-stat 
	   (lyskom-get-string
	    'unknown-person))
       (and kom-text-properties
	    (list 'face kom-presence-face))
       server
       login-when-date
       login-when-time
       (and session-info
	    nil
	    (lyskom-combine-username 
	     (static-session-info->username session-info)
	     (static-session-info->ident-user session-info)
	     (static-session-info->hostname session-info))))))))


(defun lyskom-show-logged-out-person (conf-stat session-no)
  "Visa p} kommandoraden vem som loggat ut."
  (let ((server (lyskom-session-nickname))
	(logout-when-date (let ((kom-print-relative-dates nil))
			    (lyskom-format-time 'date)))
	(logout-when-time (let ((kom-print-relative-dates nil))
			    (lyskom-format-time 'time))))
  (cond
   ((lyskom-is-in-minibuffer))
   ((lyskom-show-presence (conf-stat->conf-no conf-stat) 
                          kom-presence-messages-in-echo-area)
    (lyskom-message
     "%s"
     (lyskom-format 'has-left (or conf-stat
				  (lyskom-get-string 'unknown-person))
                    server))))
  (cond
   ((lyskom-show-presence (conf-stat->conf-no conf-stat)
                          kom-presence-messages-in-buffer)
    (lyskom-format-insert-before-prompt 'has-left-r 
					(or conf-stat
					    (lyskom-get-string
					     'unknown-person))
					(and kom-text-properties
					     (list 'face kom-presence-face))
					server
					logout-when-date
					logout-when-time)))))



(defun lyskom-is-in-minibuffer ()
  "Returns non-nil if I am using the minibuffer for some reading."
  (or lyskom-inhibit-minibuffer-messages
      cursor-in-echo-area
      (not (zerop (minibuffer-depth)))))


 (defun lyskom-show-personal-message (sender recipient message 
                                            &optional when nobeep)
  "Insert a personal message into the lyskom buffer.
Args: SENDER: conf-stat for the person sending the message.
      RECIPIENT: 0 if this message is for everybody, otherwise the conf-stat
                 of the recipient.
      MESSAGE: A string containing the message.
      WHEN: Optional time of arrival. A lyskom time structure.
      NOBEEP: True means don't beep. No matter what."
  (lyskom-insert-personal-message sender recipient message when nobeep)
  (setq lyskom-last-personal-message-sender 
        (if (stringp sender) sender (conf-stat->conf-no sender)))
  (setq lyskom-last-group-message-recipient 
        (if (and recipient
                 (not (eq 0 recipient))
                 (not (eq (conf-stat->conf-no recipient) lyskom-pers-no)))
            (conf-stat->conf-no recipient)
          nil))
  (run-hooks 'kom-personal-message-hook))


(defun lyskom-insert-personal-message (sender recipient message
                                              &optional when nobeep)
  "Insert a personal message in the current buffer.
Arguments: SENDER RECIPIENT MESSAGE.
SENDER is a conf-stat (possibly nil).
RECIPIENT is 0 if the message is public, otherwise the conf-stat of the
recipient.
MESSAGE is a string containing the message.
WHEN, if given, is the time when the message arrived. It must be a lyskom
time structure.
Non-nil NOBEEP means don't beep."
  (lyskom-handle-as-personal-message
   (lyskom-format-as-personal-message sender recipient message when nobeep)
   (conf-stat->conf-no sender) 
   nil))

(defun lyskom-format-as-personal-message (sender 
                                          recipient
                                          message
                                          &optional when nobeep)
  "Formats a personal message, returning it as a string.
Arguments: SENDER RECIPIENT MESSAGE.
SENDER is a conf-stat (possibly nil).
RECIPIENT is 0 if the message is public, otherwise the conf-stat of the
recipient.
MESSAGE is a string containing the message.
WHEN, if given, is the time when the message arrived. It must be a lyskom
time structure.
Non-nil NOBEEP means don't beep."
  (let ((lyskom-last-text-format-flags nil)
        (now (lyskom-current-client-time)))
    (when (null when) (setq when (lyskom-current-client-time)))
    (if (or kom-show-personal-message-date
            (not (eq (time->mday when) (time->mday now)))
            (not (eq (time->mon when) (time->mon now)))
            (not (eq (time->year when) (time->year now))))
        (setq when (let ((kom-print-relative-dates nil))
                     (lyskom-format-time 'date-and-time when)))
      (setq when (lyskom-format-time 'time when)))

    (setq nobeep (or nobeep (and kom-ansaphone-on
                                 kom-silent-ansaphone)))

    (cond ((or (null recipient)		; Have been seen to be nil when
                                        ; listing recorded
                                        ; messages. Should it be?
                                        ; /davidk
	       (eq recipient 0))	; Public message
           (if (not nobeep) (lyskom-beep kom-ding-on-common-messages sender))
           (lyskom-format (lyskom-get-string-sol 'message-broadcast)
                          (cond
                           ((stringp sender) sender)
                           (sender sender)
                           (t (lyskom-get-string 'unknown)))
                          message
                          when
                          (when kom-async-highlight-dashed-lines
                            `(face ,(or kom-async-dashed-lines-face
                                        lyskom-default-async-dashed-lines-face)))
                          (when kom-async-highlight-text-body
                            `(face ,(or kom-async-text-body-face
                                        lyskom-default-async-text-body-face)))))
          ((= (conf-stat->conf-no recipient) lyskom-pers-no) ; Private
           (if (not nobeep) (lyskom-beep kom-ding-on-personal-messages sender))
           (lyskom-format (lyskom-get-string-sol 'message-from)
                          (cond
                           ((stringp sender) sender)
                           (sender sender)
                           (t (lyskom-get-string 'unknown)))
                          message
                          when
                          (when kom-async-highlight-dashed-lines
                            `(face ,(or kom-async-dashed-lines-face
                                        lyskom-default-async-dashed-lines-face)))
                          (when kom-async-highlight-text-body
                            `(face ,(or kom-async-text-body-face
                                        lyskom-default-async-text-body-face)))))
          (t                            ; Group message
           (if (not nobeep) (lyskom-beep kom-ding-on-group-messages recipient))
           (lyskom-format (lyskom-get-string-sol 'message-from-to)
                          message
                          (cond
                           ((stringp sender) sender)
                           (sender  sender)
                           (t (lyskom-get-string 'unknown)))
                          (cond
                           ((stringp recipient) recipient)
                           (recipient recipient)
                           (t (lyskom-get-string 'unknown)))
                          when
                          (when kom-async-highlight-dashed-lines
                            `(face ,(or kom-async-dashed-lines-face
                                        lyskom-default-async-dashed-lines-face)))
                          (when kom-async-highlight-text-body
                            `(face ,(or kom-async-text-body-face
                                        lyskom-default-async-text-body-face))))))))


  
(defun lyskom-handle-as-personal-message (string from &optional filter)
  "Insert STRING as a personal message and beep if not from me and
supposed to. The buffer, is chosen according to the
kom-show-personal-messages-in-buffer variable value. The text is
converted, before insertion."
  (if (and filter
           (or
            (eq 0 (string-match "^Remote-command: [0-9]+ [0-9]+\n" string))
            (eq 0 (string-match "^Auto-reply:\n" string))))
      nil
    (let ((pop kom-pop-personal-messages))
      (lyskom-save-excursion
       (cond
	((eq kom-show-personal-messages-in-buffer t)
	 (lyskom-insert-before-prompt string)
	 (if pop (display-buffer (current-buffer))))
	((null kom-show-personal-messages-in-buffer))
	(t
         (let ((inhibit-read-only t))
           (let ((message-buffer
                  (car (lyskom-buffers-of-category 'personal-messages))))
             (if message-buffer
                 (set-buffer message-buffer)
               (set-buffer (lyskom-get-buffer-create 
                            'personal-messages 
                            kom-show-personal-messages-in-buffer t))
               (lyskom-view-mode)
               (setq buffer-read-only t)))
           (goto-char (point-max))
           (lyskom-insert string )
           (if pop
               (save-selected-window
                 (select-window (lyskom-display-buffer (current-buffer)
                                                       (not (eq pop t))))
                 (goto-char (point-max))
                 (recenter -1))))))))))
  


;;; ================================================================
;;; New recipient

;;; The text stat and might have been cached and thus invalid. Check
;;; for this. The conf-stat for the conf-no is almost certainly 
;;; invalid.

(defun lyskom-async-new-recipient (text-stat text-no conf-no misc-type)
  "Handle a new recipient message"

  ;; Check if we are added. A new letter!
  (when (and (eq  conf-no lyskom-pers-no)
             (not (eq (text-stat->author text-stat) lyskom-pers-no)))
    (lyskom-beep kom-ding-on-new-letter))

  ;; If the text is read in another conference, mark it as read here too
  ;; unless the new recipient is the mailbox
  (if (and kom-mark-read-texts-as-read-in-new-recipient
           (lyskom-text-read-at-least-once-p text-stat t)
           (not (eq conf-no lyskom-pers-no)))
      (lyskom-traverse misc-info (text-stat->misc-info-list text-stat)
        (when (and (memq (misc-info->type misc-info) lyskom-recpt-types-list)
                   (eq conf-no (misc-info->recipient-no misc-info)))
          (initiate-mark-as-read 'follow
                                 nil
                                 conf-no 
                                 (list (misc-info->local-no misc-info)))))

    ;; Text is previously unread or in the mailbox
    (let ((local-no nil))
      (lyskom-traverse misc-info (text-stat->misc-info-list text-stat)
        (when (and (eq (misc-info->type misc-info) misc-type)
                   (eq (misc-info->recipient-no misc-info) conf-no))
          (setq local-no (misc-info->local-no misc-info))))
      (when local-no
        (initiate-get-conf-stat 'async 'lyskom-add-new-text
                                conf-no
                                text-no
                                local-no)
        (lyskom-prefetch-text-all text-no)
        (lyskom-run 'async 'lyskom-default-new-recipient-hook text-stat)
        (lyskom-run 'async 'lyskom-prefetch-and-print-prompt)))))

(defun lyskom-default-new-recipient-hook (text-stat)
  (when (and (not lyskom-dont-change-prompt) ;We shall change it
             (not lyskom-executing-command)) ;We have time to do it.
    (lyskom-update-prompt))
  (run-hooks 'kom-new-recipient-hook))



;;; ================================================================
;;;            Functions for dealing with a new or deleted text


(defun lyskom-default-new-text-hook (text-stat)
  "Print a message if the user was waiting. Change the prompt. run hooks."
  (if (and (not lyskom-dont-change-prompt) ;We shall change it
	   (not lyskom-executing-command)) ;We have time to do it.
      (lyskom-update-prompt))

  (let ((no-message nil))
    (run-hooks 'kom-new-text-hook)
  
    (if (and (not no-message)
	     lyskom-is-waiting
	     (not (lyskom-is-in-minibuffer)))
	(lyskom-message "%s" (lyskom-format 'text-is-created
					    (text-stat->text-no text-stat))))))

(defun lyskom-default-deleted-text-hook (text-stat)
  "Update the prompt. Run hooks"
  (if (and (not lyskom-dont-change-prompt) ;We shall change it
	   (not lyskom-executing-command)) ;We have time to do it.
      (lyskom-update-prompt))
  (run-hooks 'kom-deleted-text-hook))

(defun lyskom-async-new-text (text-stat)
  "Take care of a message that a new text has been created."
  (cache-del-pers-stat (text-stat->author text-stat)) ;+++Borde {ndra i cachen i st{llet.
  
  (lyskom-traverse
      misc-info (text-stat->misc-info-list text-stat)
    (let ((type (misc-info->type misc-info)))
      (cond
       ((memq type lyskom-recpt-types-list)
	;; add on lyskom-reading-list and lyskom-to-do-list if
	;; this recipient is a recipient that has been checked.
	(if (and (eq  (misc-info->recipient-no misc-info)
                      lyskom-pers-no)
                 (not (eq (text-stat->author text-stat)
                          lyskom-pers-no)))
            (lyskom-beep kom-ding-on-new-letter))
        (initiate-get-conf-stat 'async 'lyskom-add-new-text
				(misc-info->recipient-no misc-info)
				(text-stat->text-no text-stat)
				(misc-info->local-no misc-info)))
       ((eq type 'COMM-TO)
	(cache-del-text-stat (misc-info->comm-to misc-info)))
       ((eq type 'FOOTN-TO)
	(cache-del-text-stat (misc-info->footn-to misc-info)))
       (t
	(signal 'lyskom-internal-error
		(list 'lyskom-async-new-text
		      "Unexpected misc-info in new text "
		      type))))))

  (lyskom-prefetch-text-stat-all text-stat)

  ;; Give a message if the user is waiting. Update the prompt.
  (lyskom-run 'async 'lyskom-default-new-text-hook text-stat)

  (lyskom-run 'async 'lyskom-prefetch-and-print-prompt))


(defun lyskom-async-deleted-text (text-stat)
  "Take care of a message that a text has been deleted."
      (cache-del-pers-stat (text-stat->author text-stat))
      (lyskom-traverse
          misc-info (text-stat->misc-info-list text-stat)
        (let ((type (misc-info->type misc-info)))
          (cond ((memq type lyskom-recpt-types-list)
                 (initiate-get-conf-stat 'async 'lyskom-delete-old-text
                                         (misc-info->recipient-no misc-info)
                                         (text-stat->text-no text-stat)
                                         (misc-info->local-no misc-info)))
                ((eq type 'COMM-TO)
                 (cache-del-text-stat (misc-info->comm-to misc-info)))
                ((eq type 'FOOTN-TO)
                 (cache-del-text-stat (misc-info->footn-to misc-info))))))
      (lyskom-run 'async 'lyskom-default-deleted-text-hook text-stat)
      (lyskom-run 'async 'lyskom-prefetch-and-print-prompt))

(defun lyskom-delete-old-text (recipient text-no local-no)
  "RECIPIENT is a conf-stat and previous recipient of TEXT-NO.
This call is used in response to a deleted text message"
  (when recipient

    ;; Update the cache

    (cache-del-text-stat text-no)
    (cache-del-text text-no)
    (set-conf-stat->no-of-texts 
     recipient
     (min (conf-stat->no-of-texts recipient)
          (min (conf-stat->no-of-texts recipient)
               (1- (conf-stat->no-of-texts recipient)))))

    ;; Update the read lists

    (let ((membership (lyskom-try-get-membership
                       (conf-stat->conf-no recipient))))
      (when (and membership
                 (lyskom-visible-membership membership))
        (read-list-delete-text text-no lyskom-to-do-list)
        (read-list-delete-text text-no lyskom-reading-list)))

    (lyskom-set-mode-line)))
                 

    

(defun lyskom-add-new-text (recipient text-no local-no)
  "RECIPIENT is a conf-stat and recipient of TEXT-NO.
Args: RECIPIENT TEXT-NO LOCAL-NO.
LOCAL-NO is the texts local number in RECIPIENT. This info is used
to update the no-of-texts field in the cache.

Also add this info in lyskom-to-do-list if info about RECIPIENT as been
fetched. Does not try to print prompt or do any prefetch. That will be
done after all the confs has been handled.

If recipient is nil this means we are crossposting to a protected conference.
In that case, just discard this call."

  (cond
   (recipient				;+++ Annan felhantering.
    ;; Update the cache.

    (set-conf-stat->no-of-texts
     recipient
     (max (conf-stat->no-of-texts recipient)
	  (+ local-no -1
	     (- (conf-stat->first-local-no recipient)))))

    ;; Update the read-lists.

    ;; Prefetch thoughts:
    ;; We need a way to check if a conferences is fetched.
    ;; davidk /960924
    
    (let ((membership (lyskom-try-get-membership
		       (conf-stat->conf-no recipient))))
      (if (and membership
	       ;; (lyskom-conf-fetched-p (conf-stat->conf-no recipient))
	       (lyskom-visible-membership membership)
	       (not (read-list-enter-text text-no recipient
					  lyskom-to-do-list)))
	  ;; If we have already read all texts in the conference or the
	  ;; text has not been prefetched
	  (let ((info (lyskom-create-read-info
		       'CONF
		       recipient
		       (membership->priority membership)
		       (lyskom-create-text-list (list text-no)))))
	    (read-list-enter-read-info info lyskom-to-do-list)
	    (if (= lyskom-current-conf (conf-stat->conf-no recipient))
		(read-list-enter-read-info info lyskom-reading-list)))

        ;; We don't have the membership yet. Treat it as an unread conf
        ;; and prefetch it. This might result in two prefetches of the
        ;; same conference, but the prefetch should be able to deal with
        ;; that.
        (unless membership
          (lyskom-prefetch-one-membership (conf-stat->conf-no recipient)
                                          lyskom-pers-no))))

    (lyskom-set-mode-line))))


;;; Local Variables: 
;;; eval: (put 'lyskom-traverse 'lisp-indent-hook 2)
;;; end: 
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: mship-edit.el
;;;;
;;;; Mode for editing the membership. Replaced prioritize.el
;;;;


;; KNOWN BUGS AND TO DO
;; --------------------
;;
;; Messed up redraw of an entry
;;
;; Sometimes an entry will be redrawn and appear twice, with one of
;; the old entries on the same line as one of the duplicates. This 
;; seems to happen if two redraws using delayed printing are initiated
;; at once (the second starts before the first one finishes).
;;
;; Remembers that lyskom-insert-membership and lyskom-replace-membership
;; call lp--update-buffer, so it's rarely necessary to call it from 
;; anywhere else.
;;
;; Endast-function to all marked memberships
;;
;; Uppskjut-function to all marked memberships
;;
;; Remove and add membership functions.
;;


(require 'advice)

;;; Code:

(def-komtype lp--entry
  (start-marker                         ; Where the entry is in the buffer
   end-marker                           ; Where it ends in the buffer
   priority                             ; The saved priority of the membership
   membership                           ; The membership
   selected                             ; Selected or not
   state                                ; Expanded display or not
   visible                              ; Non-nil when visible
   extents                              ; Alist of extents/overlays
   ))

(defvar lp--format-string nil "The format string for entries.")

;;; Local variables in the prioritize buffer
;;; There should be no reason to use these at all. There are functional
;;; abstractions that let you access their contents.

(defvar lp--entry-list nil)
(defvar lp--headers nil)
(defvar lp--header-end-marker nil)
(defvar lp--list-start-marker nil)
(defvar lp--list-end-marker nil)
(defvar lp--selected-entry-list nil)
(defvar lp--buffer-done nil)
(defvar lp--conf-name-width nil)
(defvar lp--inhibit-update nil)
(defvar lp--hidden-entries nil)
(defvar lp--entry-filter nil)



;;; ============================================================
;;; Utility functions and really basic stuff

(defun lp--entry-update-extents (entry)
  "Update the start and end positions for extents of ENTRY.
Update the start and end positions of all extents or overlays listed
in the extent list of ENTRY to match the start and end markers. If the
start or end markers point nowhere, detatch the extents. If overlays
are used, set the start and end positions to zero."
  (let ((extents (lp--entry->extents entry)))
    (while extents
      (lyskom-xemacs-or-gnu 
       (progn (set-extent-property (cdr (car extents)) 'end-open t)
              (set-extent-property (cdr (car extents)) 'start-open t)
              (set-extent-endpoints (cdr (car extents))
                                    (lp--entry->start-marker entry)
                                    (lp--entry->end-marker entry)))
       (progn (move-overlay (cdr (car extents)) 
                            (or (lp--entry->start-marker entry) 0)
                            (or (lp--entry->end-marker entry) 0))))
      (setq extents (cdr extents)))))

(defun lyskom-change-membership-priority (conf-no new-priority)
  "Change the priority of memberhip for CONF-NO to NEW-POSITION.
This function does not tell the server about the change."
  (let* ((mship (lyskom-get-membership conf-no t))
         (old-priority (and mship (membership->priority mship))))
    (when mship
      (set-membership->priority mship new-priority)
      (lyskom-replace-membership mship)

      (cond
       ((and (>= old-priority lyskom-session-priority)
             (>= new-priority lyskom-session-priority))
        ;; Do nothing
        )

       ((and (< old-priority lyskom-session-priority)
             (>= new-priority lyskom-session-priority))
        (let ((mship (lyskom-get-membership conf-no t)))
          (when mship (lyskom-prefetch-map conf-no mship))))

       ((and (>= old-priority lyskom-session-priority)
             (< new-priority lyskom-session-priority))
        (read-list-delete-read-info conf-no lyskom-to-do-list)
        (when (eq conf-no lyskom-current-conf)
          (lyskom-leave-current-conf))
        (lyskom-update-prompt t))
      ))))

(defun lyskom-change-membership-position (conf-no new-position)
  "Change the position of memberhip for CONF-NO to NEW-POSITION.
This function does not tell the server about the change."
  (let* ((mship (lyskom-get-membership conf-no t)))
    (when mship
      (set-membership->position mship new-position)
       (lyskom-replace-membership mship))))


;;; ============================================================
;; Entry drawing

(defun lp--entry-set-background (entry color)
  "Use extents or overlays to set the background of ENTRY to COLOR."
  (save-current-buffer
    (set-buffer (marker-buffer (lp--entry->start-marker entry)))
    (if (null color)
        (let* ((extent (assq 'color (lp--entry->extents entry))))
          (when extent
            (lyskom-xemacs-or-gnu
             (delete-extent (cdr extent))
             (delete-overlay (cdr extent)))
            (set-lp--entry->extents entry 
                                    (delq extent (lp--entry->extents entry)))))

      (let* ((extent (cdr (assq 'color (lp--entry->extents entry))))
             (facename (intern (format "lyskom-%s-background" 
				       (lyskom-safe-color-name color))))
             (face (or (lyskom-find-face facename) (lyskom-make-face facename t))))
        (unless extent
          (lyskom-xemacs-or-gnu
           (setq extent (make-extent (lp--entry->start-marker entry)
                                     (lp--entry->end-marker entry)))
           (setq extent (make-overlay (lp--entry->start-marker entry)
                                      (lp--entry->end-marker entry)
                                      nil t)))
          (set-lp--entry->extents entry (cons (cons 'color extent)
                                              (lp--entry->extents entry))))
        (set-face-background face color)
        (lyskom-xemacs-or-gnu (progn (set-extent-property extent 'end-open t)
                                     (set-extent-property extent 'start-open t)
                                     (set-extent-property extent 'priority 1000)
                                     (set-extent-property extent 'face face))
                              (progn (overlay-put extent 'priority 1000)
                                     (overlay-put extent 'face face)))))))

(defun lp--entry-set-foreground (entry color)
  "Use extents or overlays to set the foreground of ENTRY to COLOR."
  (save-current-buffer
    (set-buffer (marker-buffer (lp--entry->start-marker entry)))
    (if (null color)
        (let* ((extent (assq 'fcolor (lp--entry->extents entry))))
          (when extent
            (lyskom-xemacs-or-gnu
             (delete-extent (cdr extent))
             (delete-overlay (cdr extent)))
            (set-lp--entry->extents entry 
                                    (delq extent (lp--entry->extents entry)))))

      (let* ((extent (cdr (assq 'fcolor (lp--entry->extents entry))))
             (facename (intern (format "lyskom-%s-foreground" 
				       (lyskom-safe-color-name color))))
             (face (or (lyskom-find-face facename) (lyskom-make-face facename t))))
        (unless extent
          (lyskom-xemacs-or-gnu
           (setq extent (make-extent (lp--entry->start-marker entry)
                                     (lp--entry->end-marker entry)))
           (setq extent (make-overlay (lp--entry->start-marker entry)
                                      (lp--entry->end-marker entry) 
                                      nil t)))
          (set-lp--entry->extents entry (cons (cons 'fcolor extent)
                                              (lp--entry->extents entry))))
        (set-face-foreground face color)
        (lyskom-xemacs-or-gnu (progn (set-extent-property extent 'end-open t)
                                     (set-extent-property extent 'start-open t)
                                     (set-extent-property extent 'priority 1000)
                                     (set-extent-property extent 'face face))
                              (progn (overlay-put extent 'priority 1000)
                                     (overlay-put extent 'face face)))))))



(defmacro lp--save-excursion (&rest body)
  "Evecute BODY and restore the current location of point.
The location of point is in relation to the entry it is in.  Point
will move as the entry moves.  The location of point is restored
even if the character at point is deleted."
  `(let* ((lp--saved-entry  (lp--entry-at (point)))
          (lp--saved-column (and lp--saved-entry
                                 (- (point)
                                    (lp--entry->start-marker
                                     lp--saved-entry)))))
     (save-excursion ,@body)
     (if (and lp--saved-entry
              (lp--entry->start-marker lp--saved-entry))
         (goto-char (+ lp--saved-column (lp--entry->start-marker
                                         lp--saved-entry))))))


(defun lp--compute-format-string ()
  "Compute the format string for an entry in the buffer.
To save time, the format string is cached in `lp--format-string'.  It is
only recomputed if the window width changes."
  (let ((total (- (window-width) 1 3 3 2 12 2 5 2 4 1)))
    (setq lp--conf-name-width total)
    (setq lp--format-string
          (concat "%#1c %=3#2s %#10c %=-" (number-to-string total)
                  "#3M  %=-12#4s %[%#15@%=5#5s%]  %[%#11@%#6c%]%[%#12@%#7c%]%[%#13@%#8c%]%[%#14@%#9c%]"))))

(defun lp--format-insert-entry (entry)
  "Format ENTRY and insert it into the current buffer at point."
  (let* ((mship-conf-no (membership->conf-no (lp--entry->membership entry)))
         (adder-conf-no (membership->created-by (lp--entry->membership entry)))
         (mship-conf-stat (cache-get-conf-stat mship-conf-no))
         (adder-conf-stat (cache-get-conf-stat adder-conf-no)))

    (if (null mship-conf-stat)
        (lyskom-format-insert-at-point 
         "%#1D"
         (lyskom-create-defer-info 'get-conf-stat
                                   mship-conf-no
                                   'lp--format-entry
                                   (point-marker)
                                   (length lyskom-defer-indicator)
                                   "%#1s"
                                   entry))
      (lp--format-entry mship-conf-stat entry))

    (when (eq (lp--entry->state entry) 'expanded)
      (lyskom-insert-at-point "\n        ")
      (if (null adder-conf-stat)
          (lyskom-format-insert-at-point 
           "%#1D"
           (lyskom-create-defer-info 'get-conf-stat
                                     adder-conf-no
                                     'lp--format-entry-expansion
                                     (point-marker)
                                     (length lyskom-defer-indicator)
                                     "%#1s"
                                     entry))
        (lp--format-entry-expansion adder-conf-stat entry)))))


(defun lp--format-entry (conf-stat defer-info)
  (let ((entry nil))
    (if (lyskom-lp--entry-p defer-info)
        (progn (setq entry defer-info)
               (setq defer-info nil))
      (setq entry (defer-info->data defer-info)))
    (let* ((un (lyskom-find-unread (membership->conf-no
                                    (lp--entry->membership entry))))
           (string (lyskom-format
                    lp--format-string
                    (if (lp--entry->selected entry) ?* ?\ )
                    (if (zerop (membership->priority
                                (lp--entry->membership entry)))
                        "-"
                      (int-to-string (membership->priority
                                      (lp--entry->membership entry))))
                    (if (null conf-stat)
                        (lyskom-format 'conference-does-not-exist 
                                       (membership->conf-no 
                                        (lp--entry->membership entry)))
                      conf-stat)
                    (lyskom-format-time
                     'date
                     (membership->last-time-read (lp--entry->membership entry)))
                    (if un (int-to-string un) "")

                    (if (membership-type->invitation (membership->type (lp--entry->membership entry))) ?I ?.)
                    (if (membership-type->secret (membership->type (lp--entry->membership entry))) ?H ?.)
                    (if (membership-type->passive (membership->type (lp--entry->membership entry))) ?P ?.)
                    (if (membership-type->message-flag (membership->type (lp--entry->membership entry))) ?M ?.)
                    (if (and conf-stat (eq lyskom-pers-no (conf-stat->supervisor conf-stat))) ?O ?\ )
                    (lyskom-default-button 'prioritize-flag-menu
                                           (list entry 'invitation)
                                           (list "%#1s (%=#2M)"
                                                 (lyskom-get-string 'Invitation-mt-type)
                                                 (membership->conf-no
                                                  (lp--entry->membership entry))))
                    (lyskom-default-button 'prioritize-flag-menu
                                           (list entry 'secret)
                                           (list "%#1s (%=#2M)"
                                                 (lyskom-get-string 'Secret-mt-type)
                                                 (membership->conf-no
                                                  (lp--entry->membership entry))))
                    (lyskom-default-button 'prioritize-flag-menu
                                           (list entry 'passive)
                                           (list "%#1s (%=#2M)"
                                                 (lyskom-get-string 'Passive-mt-type)
                                                 (membership->conf-no
                                                  (lp--entry->membership entry))))
                    (lyskom-default-button 'prioritize-flag-menu
                                           (list entry 'message-flag)
                                           (list "%#1s (%=#2M)"
                                                 (lyskom-get-string 'Message-flag-mt-type)
                                                 (membership->conf-no
                                                  (lp--entry->membership entry))))
                    '(lp--unread t)
                    )))
      (if defer-info
          (lyskom-replace-deferred defer-info string)
        (lyskom-insert-at-point string))

      (if (membership-type->passive
           (membership->type (lp--entry->membership entry)))
          (lp--entry-set-foreground entry (lyskom-face-foreground kom-dim-face))
        (lp--entry-set-foreground entry nil))

      (cond ((lp--entry->selected entry)
             (lp--entry-set-background entry
                                       (lyskom-face-background kom-mark-face)))

            (t (lp--entry-set-background entry nil)))

      (cond ((eq (membership->conf-no (lp--entry->membership entry))
                 (lyskom-default-value 'lyskom-current-conf))
             (lp--entry-set-foreground entry (lyskom-face-foreground
                                              kom-url-face)))

            (t (lp--entry-set-foreground entry nil)))
)))


(defun lp--format-entry-expansion (conf-stat defer-info)
  (let ((entry nil))
    (if (lyskom-lp--entry-p defer-info)
        (progn (setq entry defer-info)
               (setq defer-info nil))
      (setq entry (defer-info->data defer-info)))

  (let* ((membership (lp--entry->membership entry))
         (string 
          (if (and (null conf-stat)
                   (eq 0 (time->sec (membership->created-at membership)))
                   (eq 0 (time->min (membership->created-at membership)))
                   (eq 1 (time->mon (membership->created-at membership)))
                   (eq 1 (time->mday (membership->created-at membership)))
                   (eq 1970 (time->year (membership->created-at membership))))
              (lyskom-get-string 'lp-no-creation-info)
            (lyskom-format "%#1s %#2s av %#3P"
                           (lyskom-get-string
                            (if (membership-type->invitation (membership->type (lp--entry->membership entry)))
                                'lp-invited 'lp-added))
                           (lyskom-format-time
                            'date-and-time
                            (membership->created-at (lp--entry->membership entry)))
                           (if (null conf-stat)
                               (lyskom-format 'person-does-not-exist
                                              (membership->created-by (lp--entry->membership entry)))
                             conf-stat)))))
    (if defer-info
        (lyskom-replace-deferred defer-info string)
      (lyskom-insert-at-point string)))))


(defun lp--print-entry (entry)
  "Print the entry ENTRY at the current position in the buffer.
The start and end markers of the entry are adjusted"
  (if (lp--entry-set-visible entry (lp--entry-compute-visible entry))
      (let ((buffer-read-only nil))
        (insert-before-markers "\n")
	(if (lp--entry->end-marker entry)
	    (set-marker (lp--entry->end-marker entry) nil))
        (set-lp--entry->end-marker entry (point-marker))
        (forward-char -1)
	(if (lp--entry->start-marker entry)
	    (set-marker (lp--entry->start-marker entry) nil))
        (set-lp--entry->start-marker entry (point-marker))
        (lp--format-insert-entry entry)

	; End marker should be before \n.
	(set-marker (lp--entry->end-marker entry)
		    (1- (lp--entry->end-marker entry)))

        (lp--entry-update-extents entry)
        (forward-char 1))
    (lp--erase-entry entry)))

(defun lp--erase-entry (entry)
  "Erase the printed representation of the entry ENTRY in the buffer."
  (let ((buffer-read-only nil))
    (when (and (lp--entry->start-marker entry)
               (lp--entry->end-marker entry))
      (delete-region (lp--entry->start-marker entry)
                     (1+ (lp--entry->end-marker entry))))
    (if (lp--entry->start-marker entry)
	(set-marker (lp--entry->start-marker entry) nil))
    (set-lp--entry->start-marker entry nil)
    (if (lp--entry->end-marker entry)
	(set-marker (lp--entry->end-marker entry) nil))
    (set-lp--entry->end-marker entry nil)
    (lp--entry-update-extents entry)))

(defun lp--redraw-entry-mark (entry)
  "Redraw the mark for ENTRY."
  (let ((buffer-read-only nil))
    (lp--save-excursion
     (goto-char (lp--entry->start-marker entry))
     (insert (if (lp--entry->selected entry) ?* ?\ ))
     (if (lp--entry->selected entry)
         (lp--entry-set-background entry (lyskom-face-background kom-mark-face))
       (lp--entry-set-background entry nil))
     (delete-char 1)
     (lp--entry-update-extents entry))))

(defun lp--redraw-entry (entry)
  "Redraw the entry ENTRY."
  (lp--save-excursion
   (if (null (lp--entry->start-marker entry))
       (goto-char
        (let ((ne (lp--get-entry
                   (lp--next-visible-entry 
                    (membership->position (lp--entry->membership entry))))))
          (if ne
              (lp--entry->start-marker ne)
            lp--list-end-marker)))
     (goto-char (lp--entry->start-marker entry))
     (lp--erase-entry entry))

   (lp--print-entry entry)))

(defun lp--perform-in-all-buffers (fn &rest args)
  "Perform FN in all prioritization buffers. ARGS are arguments for FN.
Normally there should only be one buffer, but who knows..."
  (when lyskom-buffer
    (save-current-buffer
      (set-buffer lyskom-buffer)
      (let ((prioritize-buffers (lyskom-buffers-of-category 'prioritize)))
        (lyskom-traverse buffer prioritize-buffers
          (when (buffer-live-p buffer)
            (set-buffer buffer)
            (apply fn args)))))))


(defun lp--maybe-update-entry-for-conf (conf-no)
  (lp--perform-in-all-buffers
   (lambda (conf-no)
     (let ((entry (lp--conf-no-entry conf-no)))
       (when entry (lp--redraw-entry entry))))
   conf-no))

(defun lp--maybe-update-unreads (conf-no)
    (lp--perform-in-all-buffers
     (lambda (conf-no unread)
       (let ((entry (lp--conf-no-entry conf-no)))
         (when entry
           (let ((vis (lp--entry-compute-visible entry)))
             (cond ((not (eq vis (lp--entry->visible entry)))
                    (lp--redraw-entry entry))

                   ((lp--entry->visible entry)
                    (let ((bounds (lyskom-next-property-bounds 
                                   (lp--entry->start-marker entry) 
                                   (lp--entry->end-marker entry) 'lp--unread)))
                      (when bounds
                        (lp--save-excursion
                         (let ((buffer-read-only nil))
                           (delete-region (car bounds) (cdr bounds))
                           (goto-char (car bounds))
                           (insert (lyskom-format "%#2@%=5#1s"
                                                  (if (and unread (> unread 0))
                                                      (int-to-string unread) "")
                                                  '(lp--unread t)))))))))))))
     conf-no (lyskom-find-unread conf-no)))


;;; ============================================================
;; List management

(defun lp--set-entry-list (entries)
  "Set the list of entries to ENTRIES."
  (setq lp--entry-list entries))

(defun lp--all-entries ()
  "Return a list of all entries."
  lp--entry-list)


(defun lp--conf-no-entry (conf-no)
  "Find the entry for a membership in CONF-NO."
  (let ((entries (lp--all-entries))
        (found nil))
    (while (and entries (null found))
      (when (eq conf-no (membership->conf-no (lp--entry->membership
                                              (car entries))))
        (setq found (car entries)))
      (setq entries (cdr entries)))
    found))

(defun lp--find-new-position (entry priority &optional pos)
  "Find the new position for ENTRY it is were given priority PRIORITY.
If priority is lower than the entry priority this is the last position
currently occupied by an entry with a higher priority.  If priority is
higher, then it is the first position with a priority less than the
entry priority"
  (let ((entries (lp--all-entries))
        (result nil)
        (tmp nil))
    (cond

     ;; Moving down. Return the last entry spotted with a higher
     ;; than requested priority
     ((if priority 
          (> (lp--entry->priority entry) priority)
        (< (lp--entry-position entry) pos))
      (while (and entries (null result))
        (when (if priority
                  (<= (lp--entry->priority (car entries)) priority)
                (>= (lp--entry-position (car entries)) pos))
          (setq result tmp))
        (setq tmp (car entries))
        (setq entries (cdr entries)))
      (unless result (setq result tmp)))

     ;; Moving up. Return the first entry with a priority strictly
     ;; less than the requested one
     ((if priority
          (< (lp--entry->priority entry) priority)
        (> (lp--entry-position entry) pos))
      (while (and entries (null result))
        (when (if priority
                  (< (lp--entry->priority (car entries)) priority)
                (>= (lp--entry-position (car entries)) pos))
          (setq result (car entries)))
        (setq entries (cdr entries))))
     (t (setq result entry)))
    result))
  

(defun lp--get-last-visible-entry ()
  "Return the last visible entry in the list."
  (let* ((pos (1- (length (lp--all-entries))))
         (entry (lp--get-entry pos)))
    (while (and (> pos 0) entry (not (lp--entry->visible entry)))
      (setq pos (1- pos))
      (setq entry (lp--get-entry pos)))
    (if (and entry (lp--entry->visible entry)) entry nil)))

(defun lp--get-entry (pos)
  "Return the entry at position POS in the list."
  (elt lp--entry-list pos))

(defun lp--entry-position (entry)
  "Return the position in the list for ENTRY."
  (- (length (lp--all-entries))
     (length (memq entry (lp--all-entries)))))


;;(defun lp--entry-at (where)
;;  "Return the entry at WHERE."
;;  (let* ((pos (save-excursion (goto-char where)
;;                              (beginning-of-line)
;;                              (point)))
;;         (idx (lyskom-binsearch 
;;               pos (lp--all-entries) nil nil
;;               (lambda (a b)
;;                 (cond ((numberp a)
;;                        (and (lp--entry->start-marker b)
;;                             (lp--entry->end-marker b)
;;                             (< a (lp--entry->start-marker b))))
;;                       (t
;;                        (and (lp--entry->start-marker a)
;;                             (lp--entry->end-marker a)
;;                             (< (lp--entry->end-marker a) b))))))))
;;    (and idx (lp--get-entry idx))))

(defun lp--entry-at (where)
  "Return the entry at WHERE."
  (let ((pos (save-excursion (goto-char where)
                             (beginning-of-line)
                             (point))))
    (lyskom-traverse entry (lp--all-entries)
      (when (and (lp--entry->visible entry)
                 (>= pos (lp--entry->start-marker entry))
                 (<= pos (lp--entry->end-marker entry)))
        (lyskom-traverse-break entry)))))


(defun lp--move-entry (entry to)
  "Move the entry ENTRY to position TO in the list."
  (when (or (< to 0) (> to (length (lp--all-entries))))
    (error "Position out of range: %d" to))
  (lp--erase-entry entry)
  (lp--set-entry-list (lp--remove-from-list entry (lp--all-entries)))
  (let ((cur (lp--get-entry (lp--next-visible-entry to))))
    (if cur
        (goto-char (lp--entry->start-marker cur))
      (goto-char lp--list-end-marker))
    (lp--set-entry-list (lp--add-to-list to entry (lp--all-entries)))
    (lp--print-entry entry)))


(defun lp--remove-from-list (elem l)
  "Destructively remove ELEM from the list L."
  (let* ((n (- (length l) (length (memq elem l)))))
    (cond ((= 0 n) (cdr l))
          ((= n (length l)) l)
          (t (setcdr (nthcdr (1- n) l) (nthcdr (1+ n) l))
             l))))

(defun lp--add-to-list (elem data l)
  "At the position ELEM, add DATA to the list L using side effects."
  (if (> elem (length l))
      (error "Args out of range: %S, %d" l elem))
  (if (= 0 elem)
      (cons data l)
    (setcdr (nthcdr (1- elem) l)
            (cons data (nthcdr elem l)))
    l))

(defun lp--list-move-element (el to list)
  "Move element EL by side effects so it appears at position TO in LIST."
  (lp--add-to-list to el (lp--remove-from-list el list)))

(defun lp--update-buffer (conf-no)
  "Update the entry for CONF-NO in the buffer.
If optional NEW-MSHIP is non-nil, then get the membership again."
  (unless lp--inhibit-update
    (lp--perform-in-all-buffers
     (lambda ()
       (lp--save-excursion
        (let ((entry (lp--conf-no-entry conf-no))
              (mship (lyskom-get-membership conf-no t)))

          (cond 
           ((and (null entry) mship)    ; New membership
            (let* ((pos (membership->position mship))
                   (elem (and pos (lp--get-entry 
                                   (lp--next-visible-entry pos))))
                   (entry (lyskom-create-lp--entry 
                           nil
                           nil
                           (membership->priority mship)
                           mship
                           nil
                           (if (memq (membership->created-by mship)
                                     (list lyskom-pers-no 0))
                               'contracted
                             'expanded)
                           t
                           nil)))
              (when pos
                (save-excursion
                  (goto-char (if elem
                                 (lp--entry->start-marker elem)
                               lp--list-end-marker))
                  (lp--set-entry-list
                   (lp--add-to-list pos entry (lp--all-entries)))
                  (lp--print-entry entry)))))

           ;; We have unsubscribed for good

           ((null mship)
            (when entry
              (lp--set-entry-list
               (lp--remove-from-list entry
                                     (lp--all-entries)))
              (lp--erase-entry entry)))

           ;; The priority or position of a membership has changed

           ((or (/= (lp--entry->priority entry)
                    (membership->priority mship))
                (/= (lp--entry-position entry)
                    (membership->position mship)))
            (let ((new-pos (or (membership->position mship)
                               (lp--entry-position
                                (lp--find-new-position 
                                 entry (membership->priority mship))))))
              (lp--set-entry-pri-and-pos
               entry (membership->priority mship) new-pos)
              (set-lp--entry->membership entry mship)))

           (t (set-lp--entry->membership entry mship)
              (lp--redraw-entry entry)))))))))



(defun lp--map-region (start end function &rest args)
  "For each element from START to END, apply FUNCTION.
Apply FUNCTION to each element in the region from START to END, returning
a list of results.  ARGS will be passed as additional arguments to FUNCTION.

Args: START END FUNCTION ARGS."
  (let ((results nil)
        (entry nil))
    (save-excursion
      (goto-char (lp--entry->start-marker (lp--entry-at start)))
      (setq start (set-marker (make-marker) (point)))
      (goto-char (lp--entry->end-marker (lp--entry-at end)))
      (setq end (set-marker (make-marker) (point)))
      (goto-char start)

      (while (<= (point) end)
        (setq entry (lp--entry-at (point)))
        (setq results (cons (apply function entry args) results))
        (goto-char (lp--entry->end-marker entry))
        (forward-char 2)))
    results))



;;; ============================================================
;; Menu and button functions

;;; ------------------------------------------------------------
;; Flag menu
;; Argument is a list of entry and flag

(defun lp--flag-menu-get (entry flag)
  "For the membership in ENTRY, return the value of flag FLAG.
FLAG must be one of 'invitation, 'secret or 'passive."
  (funcall
   (cond ((eq flag 'invitation) 'membership-type->invitation)
         ((eq flag 'secret) 'membership-type->secret)
         ((eq flag 'passive) 'membership-type->passive)
         ((eq flag 'message-flag) 'membership-type->message-flag))
   (membership->type (lp--entry->membership entry))))

(defun lp--flag-menu-set (entry flag value)
  "For the membership in ENTRY, set FLAG to VALUE.
FLAG must be one of 'invitation, 'secret or 'passive."
  (funcall
   (cond ((eq flag 'invitation) 'set-membership-type->invitation)
         ((eq flag 'secret) 'set-membership-type->secret)
         ((eq flag 'passive) 'set-membership-type->passive)
         ((eq flag 'message-flag) 'set-membership-type->message-flag))
   (membership->type (lp--entry->membership entry))
   value))

(defun lyskom-prioritize-flag-clear (buf arg text)
  "Clear the membership flag the user clicked on.
This function should not be called directly.  It is called in response to
a mouse click.

BUF is the buffer in which the mouse click took place.  ARG is a list
of (ENTRY FLAG), where ENTRY is the entry the flag belongs to and FLAG
is one of invitation, secret or passive.  TEXT is the text that the user
clicked on."
  (interactive)
  (let ((entry (elt arg 0))
        (flag (elt arg 1)))
    (when (lp--flag-menu-get entry flag)
      (lyskom-prioritize-flag-toggle buf arg text))))

(defun lyskom-prioritize-flag-set (buf arg text)
  "Set the membership flag the user clicked on.
This function should not be called directly.  It is called in response to
a mouse click.

BUF is the buffer in which the mouse click took place.  ARG is a list
of (ENTRY FLAG), where ENTRY is the entry the flag belongs to and FLAG
is one of invitation, secret or passive.  TEXT is the text that the user
clicked on."
  (interactive)
  (let ((entry (elt arg 0))
        (flag (elt arg 1)))
    (unless (lp--flag-menu-get entry flag)
      (lyskom-prioritize-flag-toggle buf arg text))))

(defun lyskom-prioritize-flag-toggle (buf arg text)
  "Toggle the membership flag the user clicked on.
This function should not be called directly.  It is called in response to
a mouse click.

BUF is the buffer in which the mouse click took place.  ARG is a list
of (ENTRY FLAG), where ENTRY is the entry the flag belongs to and FLAG
is one of invitation, secret or passive.  TEXT is the text that the user
clicked on."
  (interactive)
  (let ((entry (elt arg 0))
        (flag (elt arg 1)))
    (save-current-buffer
      (set-buffer (marker-buffer (lp--entry->start-marker entry)))
      (lp--flag-menu-set entry flag (not (lp--flag-menu-get entry flag)))

      ;; Attempt to perform the change
      (save-current-buffer
        (set-buffer lyskom-buffer)
        (let ((result (blocking-do 'set-membership-type
                                   lyskom-pers-no
                                   (membership->conf-no (lp--entry->membership entry))
                                   (membership->type (lp--entry->membership entry)))))

          (unless result
            (lyskom-message 
             (lyskom-format 'lp-nope (lyskom-get-error-text lyskom-errno)))))

        ;; Update the display
        (let ((mship
               (blocking-do 'query-read-texts
                            lyskom-pers-no
                            (membership->conf-no (lp--entry->membership entry))
                            t 0)))
          (lyskom-replace-membership mship)
          (set-lp--entry->membership entry mship)
          (when (eq flag 'passive)
            (cond ((membership-type->passive (membership->type mship))
                   (save-current-buffer
                     (set-buffer lyskom-buffer)
                     (when (eq (membership->conf-no mship) lyskom-current-conf)
                       (lyskom-leave-current-conf))
                     (read-list-delete-read-info (membership->conf-no mship)
                                                 lyskom-to-do-list)
                     (lyskom-update-prompt t)))
                  (t (lyskom-prefetch-map (membership->conf-no mship) mship))))
          (blocking-do 'get-conf-stat (membership->conf-no mship))))
      (lp--redraw-entry entry))))

(defun lp--selected-or-current-arg ()
  "Return a list of selected entries or the current entry.
This function is suitable to call from interactive."
  (if (and lp--selected-entry-list
	   (not (zerop (length lp--selected-entry-list))))
      lp--selected-entry-list
    (list (lp--entry-at (point)))))

(defun lp--postpone (arg entries)
  "Postpone reading of all but the last ENTRIES text in selected/
current conferences."
  (interactive (list current-prefix-arg
		     (lp--selected-or-current-arg)))
  (lyskom-with-lyskom-buffer
    (let ((n (or (and arg 
		      (prefix-numeric-value arg))
		 (lyskom-read-number 
		  (lyskom-get-string 'postpone-prompt)
		  kom-postpone-default)))
	  (l (mapcar
	      (lambda (i)
		(if (eq 'CONF (read-info->type i)) 
		    (list (read-info->conf-stat i) 
			  (read-info->text-list i))
		  '(nil nil)))
	      (read-list->all-entries lyskom-to-do-list))))
      (lyskom-traverse cur entries
	(unless cur
	  (error (lyskom-get-string 'lp-no-entry)))
	(let* ((conf-no (membership->conf-no (lp--entry->membership cur)))
	       (conf-stat (blocking-do 'get-conf-stat conf-no)))
	  (if (null conf-stat)
	      (lyskom-message (lyskom-format (lyskom-get-string 
					      'lp-skipping-missing-meeting)
					     conf-no))
	    (let ((rl (assoc conf-stat l)))
	      (when (and (nth 1 rl) ; Handle passive conferences
			 (< n (text-list->length (nth 1 rl))))
		(text-list->trim-head (nth 1 rl) n)
		(lp--update-buffer conf-no)
		(when (= lyskom-current-conf conf-no)
		  (lyskom-go-to-conf conf-no t))))))))
    (read-list-delete-text nil lyskom-reading-list)
    (read-list-delete-text nil lyskom-to-do-list)))

(defun lp--set-unread (arg entries)
  "Set number of unread messages in selected/current conference(s)."
  ; Should probably unify with
  (interactive (list current-prefix-arg
		      (lp--selected-or-current-arg)))
  (let ((num-unread (or (and arg
		     (prefix-numeric-value arg))
		(lyskom-read-num-range-or-date 
		 0
		 ; Does this really promote the user experience? Would it
		 ; be better to fix lyskom-read-num-range-or-date to 
		 ; be able to get a number or a date (ignoring ther range)?
		 (let ((max-unread 0))
		   (lyskom-traverse cur entries
		     (when cur
		       (setq max-unread 
			     (max max-unread
				  (conf-stat->no-of-texts
				   (blocking-do 'get-conf-stat 
						(membership->conf-no 
						 (lp--entry->membership cur))))))))
		   max-unread)
		 (lyskom-format 'lp--only-last)))))
    (lyskom-with-lyskom-buffer
      (lyskom-traverse cur entries
	(unless cur 
	  (error (lyskom-get-string 'lp-no-entry)))
	(let* ((conf-no (membership->conf-no (lp--entry->membership cur)))
	       (conf-stat (progn (cache-del-conf-stat conf-no)
				 (blocking-do 'get-conf-stat conf-no))))
	  (if (null conf-stat)
	      (lyskom-message (lyskom-format (lyskom-get-string 
					      'lp-skipping-missing-meeting)
					     conf-no))
	    (let ((num-unread-this-conf (if (listp num-unread)
					    num-unread
					  (min num-unread
					       (conf-stat->no-of-texts conf-stat)))))
	      (cond ((listp num-unread-this-conf)
		     (lyskom-format-insert 'set-unread-date 
					   (elt num-unread-this-conf 0)
					   (car (rassq (elt num-unread-this-conf 1) 
						       lyskom-month-names))
					   (elt num-unread-this-conf 2))
		     (let* ((target-date (lyskom-create-time 0 0 0 
							     (elt num-unread-this-conf 2) 
							     (elt num-unread-this-conf 1) 
							     (elt num-unread-this-conf 0) 
							     0 0 nil))
			    (text (lyskom-find-text-by-date conf-stat target-date)))
		       (when text
			 (blocking-do 'set-last-read 
				      (conf-stat->conf-no conf-stat)
				      (car text)))))
		    ((numberp num-unread-this-conf) 
		     (blocking-do 'set-unread conf-no num-unread-this-conf)))
	      ; FIXME: Does not handle the current conf right,
	      ; prefetch problem?
	      (lyskom-replace-membership (lp--entry->membership cur))
	      (when (= conf-no lyskom-current-conf)
		(read-list-delete-read-info conf-no lyskom-reading-list))
	      (read-list-delete-read-info conf-no lyskom-to-do-list)
	      (lyskom-prefetch-one-membership conf-no lyskom-pers-no)
	      (when (and (= conf-no lyskom-current-conf)
			    (not (zerop num-unread-this-conf)))
		(lyskom-go-to-conf conf-no t))))))
    (read-list-delete-text nil lyskom-reading-list)
    (read-list-delete-text nil lyskom-to-do-list))))


(defun lp--toggle-flag (flag entries)
  "Toggle the given FLAG for given ENTRIES."
  (lp--save-excursion
   (lyskom-traverse cur entries
     (cond ((null cur) (error (lyskom-get-string 'lp-no-entry)))
           (t (lyskom-prioritize-flag-toggle (current-buffer)
                                             (list cur flag)
                                             ""))))))

(defun lp--toggle-invitation (entries)
  "Toggle the invitation bit of the current entry"
  (interactive (list (lp--selected-or-current-arg)))
  (lp--toggle-flag 'invitation entries))

(defun lp--toggle-passive (entries)
  "Toggle the passive bit of the current entry"
  (interactive (list (lp--selected-or-current-arg)))
  (lp--toggle-flag 'passive entries))

(defun lp--toggle-message-flag (entries)
  "Toggle the message flag bit of current or selected entries"
  (interactive (list (lp--selected-or-current-arg)))
  (lp--toggle-flag 'message-flag entries))

(defun lp--toggle-secret (entries)
  "Toggle the secret bit of the current entry"
  (interactive (list (lp--selected-or-current-arg)))
  (lp--toggle-flag 'secret entries))


;;; ============================================================
;; Marking and unmarking memberships
;; 

(defun lp--select-entries (entry-list state)
  "Set the selection value of all entries in ENTRY-LIST to STATE.
Forces a mode line update"
  (lp--do-select-entries entry-list state)
  (lp--update-mode-line))

(defun lp--do-select-entries (entry-list state)
  "Set the selection value of all entries in ENTRY-LIST to STATE."
  (mapcar (lambda (entry)
            (when entry
              (if (and state (lp--entry->visible entry))
                  (add-to-list 'lp--selected-entry-list entry)
                (setq lp--selected-entry-list
                      (delq entry lp--selected-entry-list)))
              (set-lp--entry->selected entry state)
              (lp--redraw-entry-mark entry))) entry-list))

(defun lp--all-selected-entries ()
  "Return a list of all selected entries."
  lp--selected-entry-list)

(defun lp--set-selected-entries (entry-list)
  "Set the selected entries to exactly the entries in ENTRY-LIST.
Forces a mode line update"
  (lp--do-select-entries (lp--all-selected-entries) nil)
  (lp--do-select-entries entry-list t)
  (lp--update-mode-line))

;;; ============================================================
;;; Hiding and unhiding entries
;;; FIXME: This doesn't work yet

(defun lp--entry-compute-visible (entry)
  "Compute the visibility of ENTRY."
  (not (lyskom-traverse filter lp--entry-filter
         (unless (apply (car filter) entry (cdr filter))
           (lyskom-traverse-break t)))))

(defun lp--entry-set-visible (entry state)
  "Set the visibility of ENTRY to STATE"
  (let ((old-state (lp--entry->visible entry)))
    (unless (eq state old-state)
      (set-lp--entry->visible entry state)
      (if state
          (setq lp--hidden-entries (delq entry lp--hidden-entries))
        (setq lp--hidden-entries (cons entry lp--hidden-entries)))
      (lp--update-mode-line)))
  state)

(defun lp--show-all ()
  (interactive)
  (setq lp--entry-filter nil)
  (lyskom-traverse entry lp--hidden-entries
    (lp--redraw-entry entry))
  (setq lp--hidden-entries nil))




;;; ------------------------------------------------------------
;; Server update functions

(defun lp--update-membership (entry old-pri old-pos)
  "Update the server and local versions of membership in ENTRY."
  (save-current-buffer
    (let ((saved-pos (lp--entry-position entry)))
      (set-buffer lyskom-buffer)
      (let ((mship (lp--entry->membership entry)))
        (unless (eq old-pos saved-pos)
          (lyskom-change-membership-position (membership->conf-no mship)
                                             saved-pos))
        (unless (eq old-pri (lp--entry->priority entry))
          (lyskom-change-membership-priority (membership->conf-no mship)
                                             (lp--entry->priority entry)))
        (cond ((and (>= old-pri lyskom-session-priority)
                    (< (membership->priority mship) lyskom-session-priority))
               (when (eq lyskom-current-conf (membership->conf-no mship))
                 (lyskom-leave-current-conf))
               (read-list-delete-read-info (membership->conf-no mship)
                                           lyskom-to-do-list)
               (lyskom-update-prompt t))
              ((and (< old-pri lyskom-session-priority)
                    (>= (membership->priority mship) lyskom-session-priority))
               (lyskom-prefetch-map (membership->conf-no mship) mship)))

        (initiate-add-member 'background nil
                             (membership->conf-no mship)
                             lyskom-pers-no
                             (membership->priority mship)
                             (membership->position mship)
                             (membership->type mship))))))


;;; ============================================================
;;; Support

(defun lp--next-visible-entry (pos)
  "Return the position of the first visible entry following POS."
  (let ((max (length (lp--all-entries)))
        (result nil))
  (while (and (< pos max) (null result))
    (if (lp--entry->visible (lp--get-entry pos))
        (setq result pos
              pos max)
      (setq pos (1+ pos))))
  (or result max)))
  

(defun lp--calculate-distance (pos delta)
  "Return one more than the number of entries between POS and POS+DELTA.
This includes visible and invisible entries. The sign of the
returned value has the same sign as DELTA. The result is clipped to the
size of the list."
  (let ((step (lyskom-signum delta))
        (num (abs delta))
        (result 0))
    (while (> num 0)
      (setq pos (+ step pos))
      (if (< pos 0)
          (setq num 0)
        (setq result (1+ result))
        (let ((entry (lp--get-entry pos)))
          (cond ((null entry) (setq result (1+ result) num 0))
                ((lp--entry->visible (lp--get-entry pos)) (setq num (1- num)))))))
    (* result step)))


;;; ----------------------------------------------------------------
;;; Filtration

(defun lp--entry-filter-active (filter)
  "Return non-nil if FILTER is an active entry filter."
  (assq filter lp--entry-filter))

(defun lp--add-entry-filter (filter &rest filter-args)
  "Add entry filter FILTER.
FILTER is a function that should take one argument, an lp--entry,
and return non-nil if the entry should be visible.

If optional arguments FILTER-ARGS are supplied, these are also
passed to the filter function."
  (unless (symbolp filter) (error "entry filter must be a symbol"))
  (unless (assq filter lp--entry-filter)
    (setq lp--entry-filter (cons (cons filter filter-args) lp--entry-filter))
    (lp--apply-entry-filter)))

(defun lp--replace-entry-filter (filter &rest filter-args)
  "Replace existing entry filter FILTER.

See `lp--add-entry-filter' for more information."
  (unless (symbolp filter) (error "entry filter must be a symbol"))
  (if (assq filter lp--entry-filter)
      (setcdr (assq filter lp--entry-filter) filter-args)
    (setq lp--entry-filter (cons (cons filter filter-args) lp--entry-filter)))
  (lp--apply-entry-filter))


(defun lp--del-entry-filter (filter)
  "Remove the entry filter FILTER."
  (let ((el (assq filter lp--entry-filter)))
    (when el
      (setq lp--entry-filter (delq el lp--entry-filter))
      (lp--apply-entry-filter))))

(defun lp--apply-entry-filter ()
  "Apply the current filter list to all entries."
  (lp--save-excursion
   (lp--update-filter-description)
   (lyskom-traverse entry (lp--all-entries)
     (let ((vis (lp--entry-compute-visible entry)))
       (unless (eq vis (lp--entry->visible entry))
         (lp--redraw-entry entry))))))

(defun lp--entry-filter-description ()
  "Return a string representing the current entry filters."
  (let (res)
    (lyskom-traverse filter lp--entry-filter
      (let ((name (car filter)))
        (when name (setq res (cons name res)))))
    (or (and res (mapconcat 'symbol-name (nreverse res) ", "))
        (lyskom-get-string 'lp-no-active-filter))))

(defun lp--update-filter-description ()
  "Update the filter description shown in the buffer."
  (lp--set-header 'filter (lyskom-format 'lp-active-filters
                                         (lp--entry-filter-description))))


;; Filter functions should return the desired visibility state

(defun lp--entry-filter-read (entry)
  "Entry filter that displays only conferences with unread texts."
  (let ((n (lyskom-find-unread 
            (membership->conf-no (lp--entry->membership entry)))))
    (or (eq (membership->conf-no (lp--entry->membership entry))
            (lyskom-default-value 'lyskom-current-conf))
        (and n (> n 0)))))

(defun lp--entry-filter-passive (entry)
  "Entry filter that displays only active memberships."
  (not (membership-type->passive 
        (membership->type (lp--entry->membership entry)))))

(defun lp--entry-filter-after (entry time)
  "Hide entries read after a certain time"
  (not (lyskom-time-greater (membership->last-time-read
                             (lp--entry->membership entry))
                            time)))

(defun lp--entry-filter-before (entry time)
  "Hide entries read before a certain time"
  (lyskom-time-greater (membership->last-time-read
                        (lp--entry->membership entry))
                       time))


;;; ================================================================
;;; User-level functions


(defun lp--select-membership ()
  "Select the membership that point is on."
  (interactive)
  (let ((entry (lp--entry-at (point))))
    (when entry
      (lp--select-entries (list entry) t))))

(defun lp--deselect-membership ()
  "Deselect the membership that point is on."
  (interactive)
  (let ((entry (lp--entry-at (point))))
    (when entry
      (lp--select-entries (list entry) nil))))

(defun lp--toggle-membership-selection (where)
  "Toggle selection of the membership at WHERE."
  (interactive "d")
  (let ((entry (lp--entry-at where)))
    (when entry
      (lp--select-entries (list entry) (not (lp--entry->selected entry))))))

(defun lp--select-region (start end)
  "Select all entries in the region. With prefix arg, deselect.
START and END are the starting and ending points of the region."
  (interactive "r")
  (let ((entry-list (lp--map-region start end 'identity)))
    (lp--select-entries entry-list (not current-prefix-arg))))

(defun lp--select-priority (priority)
  "Select all entries with a priority PRIORITY.
With numeric prefix argument select entries with that priority."
  (interactive "P")
  (lp--do-select-priority priority t))

(defun lp--deselect-priority (priority)
  "Deselect all entries with a priority PRIORITY.
With numeric prefix argument deselect entries with that priority."
  (interactive "P")
  (lp--do-select-priority priority nil))

(defun lp--do-select-priority (priority select)
  "Select or deselect all entries with priority PRIORITY.
SELECT specifies new select."
  (when (not (numberp priority))
    (let ((entry (lp--entry-at (point))))
      (setq priority
            (lyskom-read-num-range 0 255 
                                   (lyskom-get-string
                                    (if select
                                        'lp-mark-mship-with-prio
                                      'lp-unmark-mship-with-prio))
                                   nil
                                   (and entry
                                        (membership->priority
                                         (lp--entry->membership entry)))))))

  (lp--select-entries
   (mapcar (lambda (entry)
             (and (eq priority (membership->priority
                                (lp--entry->membership entry)))
                  entry))
           (lp--all-entries))
   select))

(defun lp--deselect-all ()
  "Deselect all memberships."
  (interactive)
  (lp--set-selected-entries nil))



;;; ============================================================
;;; Reprioritization functions

(defun lp--set-entry-pri-and-pos (entry priority position)
  "Set the priority of ENTRY to PRIORITY and the position to POSITION.
If PRIORITY or POSITION is nil, the parameter is ignored.
If the position changes, lp--move-entry is called.
lp--update-membership is called automatically before this function exits."
  (let ((old-pri (lp--entry->priority entry))
        (old-pos (lp--entry-position entry))
        (lp--inhibit-update t)
        (need-redraw nil))
    (when (and priority (not (eq priority old-pri)))
      (set-lp--entry->priority entry priority)
      (set-membership->priority (lp--entry->membership entry) priority)
      (setq need-redraw t))
    (when (and position (not (eq position old-pos)))
      (lp--move-entry entry position)
      (set-membership->position (lp--entry->membership entry) position)
      (setq need-redraw nil))
    (lyskom-replace-membership (lp--entry->membership entry))
    (sit-for 0)
    (lp--update-membership entry old-pri old-pos)
    (when need-redraw (lp--redraw-entry entry))))

(defun lp--yank ()
  "Insert all the selected memberships before the entry at point."
  (interactive)
  (lp--save-excursion
   (let* ((cur (lp--entry-at (point)))
          (pos (and cur (lp--entry-position cur)))
          (priority (and cur (lp--entry->priority cur)))
          (entries (lp--all-selected-entries)))
     (cond ((null cur) (error (lyskom-get-string 'lp-no-entry)))
           ((null entries) (error (lyskom-get-string 'lp-no-selection)))
           (t (mapc 
               (lambda (entry)
                 (lp--set-entry-pri-and-pos
                  entry priority
                  (lp--entry-position (lp--find-new-position entry nil pos))))
               entries))))))
          


(defun lp--set-priority (priority)
  "Set the priority of selected memberships to PRIORITY.
Memberships that must be moved will be moved the shortest distance
possible in the list."
  (interactive "P")
  (let* ((entries (or (lp--all-selected-entries)
                      (list (lp--entry-at (point))))))
    (unless entries
      (error (lyskom-get-string 'lp-no-selection)))
    (unless (numberp priority)
      (cond ((> (length entries) 1)
             (setq priority
                   (lyskom-read-num-range
                    0 255 (lyskom-get-string 'priority-prompt-marked) t)))
            (t
             (setq priority
                   (lyskom-read-num-range
                    0 255 (lyskom-format 'priority-prompt
                                         (membership->conf-no
                                          (lp--entry->membership
                                           (car entries)))) t)))))
    (lp--save-excursion
     (mapc (lambda (entry)
               (let ((new-pos (lp--entry-position
                               (lp--find-new-position entry priority))))
                 (lp--set-entry-pri-and-pos entry priority new-pos)))
             entries))))

(defun lp--bump-priority (amount)
  "Increase the priority of the current entry by one"
  (lp--save-excursion
   (let* ((cur (lp--entry-at (point)))
          (pri (and cur (lp--entry->priority cur)))
          (pos (lp--entry-position cur))
          (new-pri (+ pri amount)))
     (when (> new-pri 255) (setq new-pri 255))
     (when (< new-pri 0) (setq new-pri 0))

     (when (and cur
                (eq pri new-pri)
                (eq pri 0)
                (eq pos (1- (length (lp--all-entries)))))
       (error (lyskom-get-string 'lp-at-min-prio)))

     (when (and cur
                (eq pri new-pri)
                (eq pri 255)
                (eq pos 0))
       (error (lyskom-get-string 'lp-at-max-prio)))

     (cond ((null cur) (error (lyskom-get-string 'lp-no-entry)))
           (t (let ((new-pos (lp--entry-position 
                              (lp--find-new-position cur (+ pri amount)))))
                (lp--set-entry-pri-and-pos cur new-pri new-pos)))))))


(defun lp--increase-priority (arg)
  (interactive "p")
  (lp--bump-priority arg))

(defun lp--decrease-priority (arg)
  (interactive "p")
  (lp--bump-priority (- arg)))



(defun lp--move-up ()
  "Move the current entry up one notch."
  (interactive)
  (lp--save-excursion
   (let* ((cur (lp--entry-at (point)))
          (pos (and cur (lp--entry-position cur)))
          (place (and cur (> pos 0) (+ pos (lp--calculate-distance pos -1))))
          (prev (and place (lp--get-entry place))))
     (cond ((null cur) (error (lyskom-get-string 'lp-no-entry)))
           ((null prev) (error (lyskom-get-string 'lp-beginning-of-list)))
           (t (if (/= (lp--entry->priority cur)
                      (lp--entry->priority prev))
                  (progn
                    (lp--set-entry-pri-and-pos cur
                                               (lp--entry->priority prev)
                                               (if (= pos (1+ place))
                                                   nil
                                                 (1+ place)))
                    (lp--redraw-entry cur))
                (lp--set-entry-pri-and-pos cur nil place)))))))

(defun lp--move-down ()
  "Move the current entry up down notch."
  (interactive)
  (lp--save-excursion
   (let* ((cur (lp--entry-at (point)))
          (pos (and cur (lp--entry-position cur)))
          (place (and cur (+ pos (lp--calculate-distance pos 1))))
          (prev (and place (lp--get-entry place))))
     (cond ((null cur) (error (lyskom-get-string 'lp-no-entry)))
           ((null prev) (error (lyskom-get-string 'lp-end-of-list)))
           (t (if (/= (lp--entry->priority cur)
                      (lp--entry->priority prev))
                  (progn
                    (lp--set-entry-pri-and-pos cur
                                               (lp--entry->priority prev)
                                               (if (= pos (1- place))
                                                   nil
                                                 (1- place)))
                    (lp--redraw-entry cur))
                (lp--set-entry-pri-and-pos cur nil place)))))))



;;; ============================================================
;;; Motion commands

(defmacro lp--save-column (&rest body)
  `(let ((lp--saved-column (current-column)))
     ,@body
     (end-of-line)
     (if (> (current-column) lp--saved-column)
         (progn (beginning-of-line)
                (forward-char lp--saved-column)))))

;;; FIXME: Deal with invisible entries

(defun lp--previous-entry (count)
  "Move the cursor up COUNT lines.
The cursor will always move to the start of the target entry."
  (interactive "p")
  (let* ((entry (lp--entry-at (point)))
         (last-entry (lp--get-last-visible-entry))
         (first-entry (lp--next-visible-entry 0))
         (pos (lp--entry-position entry))
         (new-pos (cond ((null last-entry) nil)
                        ((and (null entry) 
                              (< (point)
                                 (lp--entry->start-marker 
                                  (lp--get-entry first-entry))))
                         (lp--get-entry first-entry))
                        ((and (null entry)
                              (> (point) 
                                 (lp--entry->end-marker last-entry)))
                         last-entry)
                        (t (lp--get-entry
                            (max 0 (+ pos (lp--calculate-distance
                                           pos (- count)))))))))
    (condition-case nil
        (goto-char (lp--entry->start-marker new-pos))
      (error nil))))

(defun lp--next-entry (count)
  "Move the cursor down COUNT lines.
The cursor will always move to the start of the target entry."
  (interactive "p")
  (let* ((entry (lp--entry-at (point)))
         (last-entry (lp--get-last-visible-entry))
         (first-entry (lp--next-visible-entry 0))
         (pos (lp--entry-position entry))
         (new-pos (cond ((null last-entry) nil)
                    ((and (null entry) (< (point) 
                                          (lp--entry->start-marker 
                                           (lp--get-entry
                                            first-entry))))
                     (lp--get-entry first-entry))
                    ((and (null entry) (> (point) 
                                          (lp--entry->end-marker 
                                           last-entry)))
                     last-entry)
                    (t (lp--get-entry 
                        (min (1- (length (lp--all-entries)))
                             (+ pos (lp--calculate-distance pos count))))))))
    (condition-case nil
        (goto-char (lp--entry->start-marker new-pos))
      (error nil))))

(defun lp--first-entry ()
  "Move point to the first entry in the membership list."
  (interactive)
  (condition-case nil
      (let ((entry (lp--get-entry (lp--next-visible-entry 0))))
        (cond ((null entry) (goto-char lp--list-end-marker))
              (t (goto-char (lp--entry->start-marker 
                             (lp--get-entry (lp--next-visible-entry 0)))))))
    (error nil)))

(defun lp--last-entry ()
  "Move point to the last entry in the membership list."
  (interactive)
  (condition-case nil
      (let ((entry (lp--get-last-visible-entry)))
        (cond ((null entry) (goto-char lp--list-end-marker))
              (t (goto-char (lp--entry->start-marker entry)))))
    (error nil)))

(defun lp--goto-priority (priority)
  "Move to the closest entry with priority PRIORITY.
If there is no entry with the specified priority, move to the nearest
entry with an adjacent priority."
  (interactive "P")
  (let* ((entry (lp--entry-at (point)))
         (seen-me nil)
         (done nil)
         (entry-list (lp--all-entries)))

    ;; Get the priority to move to

    (unless (numberp priority)
      (setq priority
            (lyskom-read-num-range 
             0 255 (lyskom-get-string 'lp-goto-priority) t)))

    ;; Figure out where to move
    ;; Loop over all entries

    (while (and (not done) (cdr entry-list))
      (when (eq (car entry-list) entry) (setq seen-me t))
      (if (or (< (lp--entry->priority (car entry-list))
                 priority)
              (and seen-me (= (lp--entry->priority (car entry-list))
                              priority)))
          (setq done t)
        (setq entry-list (cdr entry-list))))

    (when entry-list
      (goto-char (lp--entry->start-marker (car entry-list))))))
    
(defun lp--toggle-entry-expansion ()
  "Toggle the expanded state of the current entry."
  (interactive)
  (let ((entry (lp--entry-at (point))))
    (when entry
      (set-lp--entry->state
       entry
       (if (eq (lp--entry->state entry) 'expanded) 'contracted 'expanded))
      (lp--redraw-entry entry))))

(defun lp--expand-entry (arg)
  "Expand selected entries. 
With prefix arg, expand only those that were created by someone else."
  (interactive "P")
  (let ((entries (or (lp--all-selected-entries)
                      (list (lp--entry-at (point)))))
        (hidden-list (list lyskom-pers-no 0)))
    (mapcar (lambda (entry)
              (when (and (or (null arg)
                             (not (memq (membership->created-by
                                         (lp--entry->membership entry))
                                        hidden-list)))
                         (not (eq (lp--entry->state entry) 'expanded)))
                (set-lp--entry->state entry 'expanded)
                (lp--redraw-entry entry)))
            entries)))

(defun lp--contract-entry (arg)
  "Contract selected entries.
With prefix arg, contract only those that were created by self."
  (interactive "P")
  (let ((entries (or (lp--all-selected-entries)
                      (list (lp--entry-at (point)))))
        (hidden-list (list lyskom-pers-no 0)))
    (mapcar (lambda (entry)
              (when (and (or (null arg)
                             (memq (membership->created-by
                                    (lp--entry->membership entry))
                                   hidden-list))
                         (not (eq (lp--entry->state entry) 'contracted)))
                (set-lp--entry->state entry 'contracted)
                (lp--redraw-entry entry)))
            entries)))

(defun lp--quit ()
  "Remove the membership buffer and quit"
  (interactive)
  (lyskom-undisplay-buffer))



(defun lp--scroll-advice (fn)
  (let ((cur (current-column)))
    (funcall fn)
    (when (and (boundp 'lyskom-buffer-category)
               (eq lyskom-buffer-category 'prioritize))
      (let ((last-entry (lp--get-last-visible-entry ))
            (first-entry (lp--next-visible-entry 0)))
        (cond ((null last-entry)
               (goto-char lp--list-end-marker))
              ((> (point) (lp--entry->end-marker last-entry))
               (goto-char (lp--entry->end-marker last-entry))
               (when (> (current-column) cur)
                 (beginning-of-line)
                 (forward-char cur)))

              ((< (point) (lp--entry->start-marker
                           (lp--get-entry first-entry)))
               (goto-char (lp--entry->start-marker 
                           (lp--get-entry first-entry)))
               (end-of-line)
               (when (> (current-column) cur)
                 (beginning-of-line)
                 (forward-char cur))))))))

(defadvice scroll-up-command (around lp--scroll-up-advice activate)
  (lp--scroll-advice (lambda () ad-do-it)))

(defadvice scroll-down-command (around lp--scroll-up-advice activate)
  (lp--scroll-advice (lambda () ad-do-it)))


;;; ============================================================
;; The mode

(defvar lp--mode-line '("--" 
                        mode-line-buffer-identification
                        "   "
                        global-mode-string
                        "   %[("
                        mode-name
                        mode-line-process
                        minor-mode-alist
                        ")%] "
                        lp--mode-line-selected
                        "--"
                        lp--mode-line-hidden
                        "--"
                        (-3 . "%p")
                        "-%-"))

(defvar lp--mode-line-selected ""
  "String showing number of selected entries.")

(defvar lp--mode-line-hidden ""
  "String showing number of hidden entries.")

(defun lp--update-mode-line ()
  (setq lp--mode-line-selected
        (cond ((= (length (lp--all-selected-entries)) 0)
               (lyskom-get-string 'no-selection))
               (t (lyskom-format 'selection
                                 (length (lp--all-selected-entries))))))
  (setq lp--mode-line-hidden
        (cond ((= (length lp--hidden-entries) 0)
               (lyskom-get-string 'lp-no-hidden))
               (t (lyskom-format 'lp-hidden
                                 (length lp--hidden-entries)))))
  (force-mode-line-update))


(def-kom-command kom-prioritize ()
  "Display a list of all memberships with the option to change order,
priority, flags, and a number of other things.

See `kom-prioritize-in-window'."
  (interactive)
  (lyskom-prioritize))

(defun lyskom-prioritize ()
  (let ((kom-deferred-printing nil))
    (set-buffer (lp--create-buffer)))
  (lp--mode)
  (lp--first-entry))

(defun lp--mode ()
  "\\<lyskom-prioritize-mode-map>Mode for prioritizing conferences in LysKOM.

\\[lp--move-up] moves a single conference up in the list.
\\[lp--mode-down] moves a single conference down in the list.

SPC toggles the selection of a single membership.
\\[lp--select-region] selects all conferences between point and mark.
\\[lp--select-priority] selects all conferences with a certain priority.
\\[lp--deselect-region] clears the selection.

\\[lp--yank] moves all selected conferences to where point is in the list.
\\[lp--set-priority] changes the priority of all selected conferences.

\\[lp--toggle-passive] makes passive memberships active and vice versa.
\\[lp--toggle-invitation] toggles the invitation flag of a membership.
\\[lp--toggle-secret] makes secret memberships non-secret and vice versa.
\\[lp--toggle-message-flag] toggles reception of group messages for that conference.

Changes take effect immediately.

This mode is still under development and is subject to problems. In
particular, it is unlikely to handle all error conditions gracefully,
which may lead to inconsistencies between the buffer and reality.
Should a problem occur, killing the buffer and reopening it usually
clears the problem.

All bindings:
\\{lyskom-prioritize-mode-map}

Entry to this mode runs lp--mode-hook, but since the name of this hook
is subject to change, be careful when using it."
  (interactive)
  (setq major-mode 'lp--mode)
  (setq mode-name (lyskom-get-string 'lp-mode-name))
  (make-local-variable 'lp--format-string)
  (make-local-variable 'lp--conf-name-width)
  (make-local-variable 'lp--entry-list)
  (make-local-variable 'lp--list-start-marker)
  (make-local-variable 'lp--list-end-marker)
  (make-local-variable 'lp--selected-entry-list)
  (make-local-variable 'lp--mode-line-selected)
  (make-local-variable 'lp--hidden-entries)
  (make-local-variable 'lp--entry-filter)

  (setq lp--hidden-entries nil)
  (setq lp--entry-filter nil)

  (lyskom-set-menus 'lp--mode lyskom-prioritize-mode-map)

  (setq lp--mode-line-selected "")
  (setq mode-line-format lp--mode-line)
  (lp--update-mode-line)
  (setq buffer-read-only t)
  (lyskom-use-local-map lyskom-prioritize-mode-map)
  (run-hooks 'lp--mode-hook))


(defun lp--set-header (id string)
  "Set header ID of the current membership buffer to STRING."
  (save-excursion
    (let ((inhibit-read-only t)
          (pos (assq id lp--headers)))
      (setq lp--headers (delq pos lp--headers))

      (if (not pos)
          (goto-char lp--header-end-marker)
        (goto-char (elt pos 1))
        (delete-region (elt pos 1) (elt pos 2)))

      (if (null string)
          (delete-char 1)
        (let ((start (point-marker)) (end nil))
          (set-marker-insertion-type start nil)
          (insert string)
          (setq end (point-marker))
          (unless pos (insert "\n"))
          (setq lp--headers (cons (list id start end) lp--headers)))))))



(defun lp--create-buffer ()
  "Create a buffer for managing memberships."
  (let ((buf (lyskom-get-buffer-create 'prioritize
                                       (concat (buffer-name) "-membership")
                                       t))
        (entry-list nil))

    (lyskom-save-excursion
      (set-buffer buf)
      (let ((buffer-read-only nil))
        (erase-buffer)
        (make-local-variable 'lp--entry-list)
        (make-local-variable 'lp--list-start-marker)
        (make-local-variable 'lp--list-end-marker)
        (make-local-variable 'lp--buffer-done)
        (make-local-variable 'lp--end-marker)
        (make-local-variable 'lp--header-end-marker)
        (make-local-variable 'lp--headers)
        (setq lp--entry-list nil)
        (setq lp--buffer-done nil)
        (lp--compute-format-string)

        (setq lp--header-end-marker (point-marker))
        (set-marker-insertion-type lp--header-end-marker nil)
        (lyskom-insert (make-string (1- (window-width)) ?=))
        (lyskom-insert "\n")
        (set-marker-insertion-type lp--header-end-marker t)

        (lp--set-header 'main
                        (lyskom-format 'lp-header-main
                                       lyskom-pers-no lyskom-server-name))

        (lyskom-format-insert 'lp-list-header
                              (concat (lyskom-get-string 'conference)
                                      (make-string (- lp--conf-name-width
                                                      (length (lyskom-get-string 'conference)))
                                                   ?\ )))

        (lyskom-insert (make-string (1- (window-width)) ?-))
        (lyskom-insert "\n")

        (setq lp--list-start-marker (point-marker))
        (goto-char (point-max))
        (lyskom-display-buffer buf)
        (lyskom-traverse-membership mship
          (let ((entry (lyskom-create-lp--entry nil ; Start
                                                nil ; End
                                                (membership->priority mship)
                                                mship
                                                nil
                                                (if (memq (membership->created-by mship)
                                                          (list lyskom-pers-no 0))
                                                    'contracted
                                                  'expanded)
                                                t
                                                nil)))
            (lp--print-entry entry)
            (setq entry-list (cons entry entry-list))))
        (lp--set-entry-list (nreverse entry-list))
        (setq lp--list-end-marker (point-marker))
        (lyskom-insert (make-string (1- (window-width)) ?=))
        (lyskom-insert (lyskom-get-string 'lp-help-footer))
        ))
    (lyskom-wait-queue 'deferred)
    buf
    ))




;;; ================================================================
;;; User functions for filtering


(defun lp--show-hide-memberships-read-before (arg)
  "Hide memberships that have not been read since a specific date.
With prefix arg, removes any such filter that has been applied."
  (interactive "P")
  (if arg
      (lp--del-entry-filter 'lp--entry-filter-before)

    (let* ((n (lyskom-read-date (lyskom-get-string 'lp-hide-read-sice)))
           (date (and n (lyskom-create-time 0 0 0 (elt n 2)
                                            (elt n 1) (elt n 0)
                                            0 0 nil))))
      (lp--replace-entry-filter 'lp--entry-filter-before date))))


(defun lp--show-hide-memberships-read-after (arg)
  "Hide memberships that have been read since a specific date.
With prefix arg, removes any such filter that has been applied."
  (interactive "P")
  (if arg
      (lp--del-entry-filter 'lp--entry-filter-after)

    (let* ((n (lyskom-read-date (lyskom-get-string 'lp-hide-read-after)))
           (date (and n (lyskom-create-time 0 0 0 (elt n 2)
                                            (elt n 1) (elt n 0)
                                            0 0 nil))))
      (lp--replace-entry-filter 'lp--entry-filter-after date))))


(defun lp--show-hide-read-memberships (arg)
  "Toggle display of memberships with no unread texts."
  (interactive "P")

  (if (lp--entry-filter-active 'lp--entry-filter-read)
      (lp--del-entry-filter 'lp--entry-filter-read)
    (lp--add-entry-filter 'lp--entry-filter-read)))


(defun lp--show-hide-passive-memberships (arg)
  "Toggle display of passive memberships."
  (interactive "P")

  (if (lp--entry-filter-active 'lp--entry-filter-passive)
      (lp--del-entry-filter 'lp--entry-filter-passive)
    (lp--add-entry-filter 'lp--entry-filter-passive)))





(provide 'mship-edit)

;;; mship-edit.el ends here
;;; Local Variables:
;;; eval: (put 'lp--save-excursion 'edebug-form-spec t)
;;; end:


;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: flags.el
;;;;
;;;; This file contains code for editing the user variables and saving
;;;; them in the user area.
;;;;

(eval-when-compile
  (require 'lyskom-command "command"))

(put 'lyskom-invalid-flag-type 'error-conditions
     '(error lyskom-error lyskom-invalid-flag-type))

(defvar lyskom-options-text nil
  "Text mass when reading options.")

(def-kom-var lyskom-options-done nil
  "When we have read all options this is turned non-nil."
  local)

(def-kom-command kom-save-options (arg)
  "Save all LysKOM settings. This will save settings in the server but
not save anything to your Emacs init file (usually .emacs). With a 
prefix argument, remove all unknown settings.

To edit settings, use `kom-customize'. This command can also save
settings to your emacs init file."
  (interactive "P")
  (when arg (setq lyskom-saved-unknown-variables nil))
  (lyskom-save-options (or lyskom-buffer 
                           (current-buffer))
                       (lyskom-get-string 'saving-settings)
                       (lyskom-get-string 'saving-settings-done)
                       (lyskom-get-string 'could-not-save-options)))

(def-kom-command kom-copy-options ()
  "Copy settings from one LysKOM session to another. This is useful if
you have configured on session to your liking and want this settings to
be used on other servers as well.

This command will save settings to the LysKOM server, but will not
save anything to your Emacs init file. Use `kom-customize' to edit
settings and save them to your emacs init file."
  (interactive)
  (let* ((completions
          (mapcar (lambda (x)
                    (cons (save-current-buffer
                            (set-buffer x)
                            (buffer-name))
                          x))
                  (delq (or lyskom-buffer (current-buffer))
                        (copy-sequence lyskom-buffer-list))))
         (from-session (completing-read 
                        (lyskom-get-string 'session-to-copy-options-from)
                        completions
                        nil t)))
    (lyskom-message (lyskom-get-string 'reading-settings-from) from-session)
    (setq lyskom-saved-unknown-variables (lyskom-read-options from-session t))
    (lyskom-message (lyskom-get-string 'reading-settings-from-done) from-session)

    ;; Inline kom-save-options
    (lyskom-save-options (or lyskom-buffer (current-buffer))
                         (lyskom-get-string 'saving-settings)
                         (lyskom-get-string 'saving-settings-done)
                         (lyskom-get-string 'could-not-save-options))
))

(defun lyskom-copy-indirect-assq (val)
  "Attempt to filter out non-portable stuff from an indirect assq"
  (cond ((eq val t) t)
	((eq val nil) nil)
	(t (delq nil 
		 (mapcar (lambda (el)
			   (cond ((numberp el) nil)
				 ((atom el) el)
				 ((and (consp el) (symbolp (car el))) el)
				 (t nil)))
			 val)))))


;;;============================================================
;;;  lyskom-save-options
;;;
;;;  Save user-area without feedback in the KOM buffer. This
;;;  is for use by all functions but edit-options.
;;;
;;;  Messages are given in the minibuffer

(defun lyskom-save-options (kombuf start-message done-message error-message)
  ;; `kom-presence-messages' isn't used directly by the emacs lisp client.
  ;; Since there is much finer control of what presence messages to give
  ;; it's hard to encode this is a single boolean, so usually don't try.
  ;; Ony if the user sees all or no presence messages is it rather clear
  ;; what the common block value should be:
  (cond ((or (eq kom-presence-messages-in-buffer t)
 	     (eq kom-presence-messages-in-echo-area t))
 	 (setq kom-presence-messages t))
 	((not (or kom-presence-messages-in-buffer
 		  kom-presence-messages-in-echo-area))
 	 (setq kom-presence-messages nil)))

  (let* ((print-readably t)
         (common-block 
          (concat
           (mapconcat (lambda (var)
                        (condition-case nil
                            (let ((common-name (elt var 0))
                                  (elisp-name (elt var 1))
                                  (type (elt var 2)))
                              (lyskom-format-objects
                               (symbol-name common-name)
                               (funcall 
                                (cdr (assq 'write
                                           (cdr (assq (or type t)
                                                      lyskom-global-variable-types))))
                                (symbol-value elisp-name))))
                          (lyskom-invalid-flag-type "")))
                      lyskom-global-variables
                      "\n")
           ))
         (elisp-block
          (concat (mapconcat
                   (lambda (var)
                     (let ((name (lyskom-encode-coding-string 
                                  (symbol-name var)
                                  (or lyskom-server-coding-system 'raw-text)))
                           (data (lyskom-encode-coding-string 
                                  (lyskom-flag-value-to-string var)
                                  (or lyskom-server-coding-system 'raw-text))))
                       (when (lyskom-multibyte-string-p data)
                         (lyskom-format-insert-before-prompt "Errors encoding %#1s\n" name))
                       (format "%dH%s %dH%s" 
                               (lyskom-string-bytes name) name
                               (lyskom-string-bytes data) data)))
                   lyskom-elisp-variables
                   "\n")
                  (mapconcat
                   (lambda (el)
                     (let ((var (lyskom-encode-coding-string
                                 (symbol-name (car el))
                                 (or lyskom-server-coding-system 'raw-text)))
                           (data (lyskom-encode-coding-string 
                                  (cdr el) 
                                  (or lyskom-server-coding-system 'raw-text))))
                       (format "\n%dH%s %dH%s" 
                               (lyskom-string-bytes var)
                               var
                               (lyskom-string-bytes data)
                               data)))
                   lyskom-saved-unknown-variables
                   "")))
         (user-area (apply 'lyskom-format-objects
		    (apply 'lyskom-format-objects
			   "common"
			   "elisp"
			   (mapcar 
			    (lambda (el)
			      (cons 'STRING (cons 'raw-text (car el))))
			    lyskom-other-clients-user-areas))
		    (cons 'STRING (cons 'raw-text common-block))
		    (cons 'STRING (cons 'raw-text elisp-block))
		    (mapcar (lambda (el)
                              (cons 'STRING (cons 'raw-text (cdr el))))
			    lyskom-other-clients-user-areas))))

    (if (or (lyskom-multibyte-string-p elisp-block)
            (lyskom-multibyte-string-p common-block)
            (memq t (mapcar 'lyskom-multibyte-string-p lyskom-other-clients-user-areas)))
        (lyskom-save-excursion
          (set-buffer kombuf)
          (lyskom-format-insert 'save-options-failed-internal
                                (lyskom-multibyte-string-p common-block)
                                (lyskom-multibyte-string-p elisp-block)
                                (memq t (mapcar 'lyskom-multibyte-string-p 
                                                lyskom-other-clients-user-areas)))
          (when error-message (lyskom-message "%s" error-message)))
      (save-current-buffer
        (set-buffer kombuf)
        (when start-message (lyskom-message "%s" start-message))
        (initiate-create-text
         'options 
         'lyskom-save-options-2
         (cons 'raw-text user-area)
         (lyskom-create-misc-list) 
         (list
          (lyskom-create-aux-item 
           0 1 nil nil
           (lyskom-create-aux-item-flags nil nil nil nil nil nil nil nil)
           0
           "x-kom/user-area"))
         kombuf
         done-message
         error-message)))))

;; This was in lyskom-save-options, to encode each string usgin
;; its own coding system
;;
;; (coding 
;; (lyskom-mime-charset-coding-system
;; (lyskom-mime-string-charset data)))
;; (val (condition-case nil
;; (lyskom-encode-coding-string data coding)
;; (error nil)))
;; (lyskom-ignore val)
;; (format "%dC%s%dH%s"
;;             (lyskom-string-bytes (symbol-name coding))
;;             (symbol-name coding)
;;             (lyskom-string-bytes val)
;;             val)


(defun lyskom-save-options-2 (text-no kombuf done-message error-message)
  (if text-no
      (progn (setq lyskom-current-user-area text-no)
             (initiate-set-user-area 'options 'lyskom-save-options-3
                                     lyskom-pers-no text-no kombuf 
                                     done-message error-message text-no))
    (save-current-buffer
     (set-buffer kombuf)
     (lyskom-insert-string 'could-not-save-options)
     (lyskom-message "%s" (lyskom-get-string 'could-not-save-options)))))

(defun lyskom-save-options-3 (success kombuf done-message
                                      error-message text-no)
  (save-current-buffer
    (set-buffer kombuf)
    (if success
        (let ((pers-stat (cache-get-pers-stat lyskom-pers-no)))
          (when pers-stat
            (set-pers-stat->user-area pers-stat text-no))
          (setq lyskom-current-user-area text-no)
          (when done-message (lyskom-message "%s" done-message)))
      (lyskom-format-insert 'could-not-set-user-area lyskom-errno)
      (when error-message (lyskom-message "%s" error-message)))))

(defun lyskom-read-options (&optional buffer portable-only)
  "Reads the user-area and sets the variables according to the choises.
Returns a association list of variables that were ignored. Each list
element is a cons (NAME . VALUE), where NAME is the name of the variable
and VALUE is the unparsed value (i.e. it is always a string).

If optional BUFFER is non-nil, read settings in that buffer.

If optional PORTABLE-ONLY is non-nil, skip variables marked non-portable."
  (if (and lyskom-pers-no
	   (not (zerop lyskom-pers-no)))
      (let ((pers-stat 
             (save-current-buffer
               (when buffer (set-buffer buffer))
               (blocking-do 'get-pers-stat lyskom-pers-no))))
	(if (not pers-stat)             ;+++ Other error handler.
	    (progn (lyskom-insert-string 'you-dont-exist)
                   nil)
	  (setq lyskom-other-clients-user-areas nil)
	  (if (zerop (pers-stat->user-area pers-stat))
	      (progn
		;; (lyskom-tell-phrases-validate)
		(setq lyskom-options-done t)
                nil)
	    (lyskom-read-options-eval 
             (save-current-buffer
               (when buffer (set-buffer buffer))
               (blocking-do 'get-text
                            (pers-stat->user-area pers-stat)))
	     portable-only))))
    nil))

(defun lyskom-read-options-eval (text &optional portable-only)
  "Handles the call from where we have the text.
Returns a alist of variables that were ignored. See lyskom-read-options
for more information.

If optional PORTABLE-ONLY is non-nil, skip variables marked non-portable."
  (let ((ignored-user-area-vars nil))
    (condition-case nil
        (when text                        ;+++ Other error handler
          (let* ((lyskom-options-text (text->text-mass text))
                 (pointers (lyskom-read-options-eval-get-holerith t))
                 common-no elisp-no
                 (rest lyskom-options-text)
                 working
                 (r 1))
            (let* ((lyskom-options-text pointers)
                   word
                   (r 1))
              (while (> (length lyskom-options-text) 2)
                (setq word (lyskom-read-options-eval-get-holerith t))
                (cond
                 ((lyskom-string= word "common")
                  (setq common-no r))
                 ((lyskom-string= word "elisp")
                  (setq elisp-no r))
                 (t
                  ;; Build up lyskom-other-clients-user-areas so that it
                  ;; contains a list of pairs: (name . number). (string, int).
                  (setq lyskom-other-clients-user-areas
                        (cons (cons word r) lyskom-other-clients-user-areas))))
                (++ r)))
            (setq lyskom-other-clients-user-areas 
                  (nreverse lyskom-other-clients-user-areas))
            (setq lyskom-options-text rest)
            (while (> (length lyskom-options-text) 2)
              (setq working (lyskom-read-options-eval-get-holerith t))
              (cond
               ;; Note that common-no may be nil here, so the comparison
               ;; cannot be performed with '=.
               ((equal r common-no)
                (let ((lyskom-options-text working)
                      spec name value)
                  (while (> (length lyskom-options-text) 2)
                    (setq name (intern (lyskom-read-options-eval-get-holerith)))
                    (setq value (lyskom-read-options-eval-get-holerith))
                    (setq spec
                          (cond ((lyskom-flag-global-variable-from-common name))
                                (t (let ((tmp (vector name
                                                      (intern (format "lyskom-UNK-%S" name))
                                                      nil)))
                                     (setq lyskom-global-variables
                                           (cons tmp lyskom-global-variables))
                                     tmp))))

                    (unless (lyskom-maybe-set-var-from-string (elt spec 1) value (elt spec 2) portable-only)
                      (setq ignored-user-area-vars
                            (cons (cons (elt spec 1) value) ignored-user-area-vars))))))
               ;; Note that elisp-no may be nil here, so the comparison
               ;; cannot be performed with '=.
               ((equal r elisp-no)
                (let ((lyskom-options-text working)
                      name value)
                  (while (> (length lyskom-options-text) 2)
                    (setq name (intern (lyskom-read-options-eval-get-holerith)))
                    (setq value (lyskom-read-options-eval-get-holerith))
                    (if (lyskom-maybe-set-var-from-string name value nil portable-only)
                        (when (functionp (cdr (assq name lyskom-transition-variables)))
                          (set name (funcall (cdr (assq name lyskom-transition-variables))
                                             (symbol-value name))))
                      (setq ignored-user-area-vars
                            (cons (cons name value) ignored-user-area-vars))))))
               (t
                (let ((pos lyskom-other-clients-user-areas))
                  (while (and pos
                              (not (equal
                                    (cdr (car pos)) ;The position or the string.
                                    r)))
                    (setq pos (cdr pos)))
                  (if pos
                      (setcdr (car pos) working))))) ;Insert the string
					;where the position
					;was stored.
              (++ r))

            (mapc 'lyskom-recompile-filter kom-permanent-filter-list)
            (mapc 'lyskom-recompile-filter kom-session-filter-list)

            (setq lyskom-filter-list (append kom-permanent-filter-list
                                             kom-session-filter-list))
            (setq lyskom-do-when-done (cons kom-do-when-done kom-do-when-done))
            ;; Remove not found user-areas
            (let ((pos lyskom-other-clients-user-areas))
              (if pos
                  (progn
                    (while (stringp (cdr (car (cdr pos))))
                      (setq pos (cdr pos)))
                    (setcdr pos nil)))))
          (setq lyskom-current-user-area (text->text-no text)))
      (error (lyskom-message "%s" (lyskom-get-string 'error-in-options-short))))
    (setq lyskom-options-done t)
    (setq lyskom-current-user-area (if text (text->text-no text) 0))
    ignored-user-area-vars))


(defun lyskom-read-options-eval-get-holerith (&optional no-coding)
  (let ((coding (if no-coding 'raw-text lyskom-server-coding-system)))
    (while (string-match "\\(\\s-\\|[\n\r]\\)" (substring lyskom-options-text 0 1))
      (setq lyskom-options-text (substring lyskom-options-text 1)))

    ;; Read the explicit coding, if any

    (when (string-match "^[0-9]+C" lyskom-options-text)
      (let ((len (lyskom-string-to-number lyskom-options-text)))
        (setq coding (intern
                      (substring lyskom-options-text 
                                 (match-end 0)
                                 (+ (match-end 0) len))))
        (setq lyskom-options-text (substring lyskom-options-text
                                             (+ (match-end 0) len))))) 

    ;; Read the string

    (let ((len (lyskom-string-to-number lyskom-options-text))
          (start (progn (string-match "[0-9]+H" lyskom-options-text)
                        (match-end 0))))
      (let ((name (substring lyskom-options-text start (+ start len))))
        (setq lyskom-options-text (substring lyskom-options-text
                                             (+ start len)))
        (condition-case nil
            (lyskom-decode-coding-string name coding)
          (error name))))))

(defun lyskom-get-holerith (string &optional no-coding)
  "Get the first holerith string in STRING.
If optional NO-CODING is set, assume the string has internal coding."
  (let ((coding (if no-coding 'raw-text lyskom-server-coding-system)))

    ;; Strip initial whitespace

    (when (string-match "\\`\\(\\s-\\|[\n\r]\\)+" string)
      (setq string (substring string (match-end 0))))

    ;; Read the explicit coding, if any

    (when (string-match "\\`[0-9]+C" string)
      (let ((len (lyskom-string-to-number string)))
        (setq coding (intern
                      (substring string 
                                 (match-end 0)
                                 (+ (match-end 0) len))))
        (setq string (substring string (+ (match-end 0) len)))))

    ;; Read the string

    (let ((len (lyskom-string-to-number string))
          (start (progn (string-match "[0-9]+H" string)
                        (match-end 0))))
      (let ((name (substring string start (+ start len))))
        (setq string (substring string (+ start len)))
        (condition-case nil
            (cons (lyskom-decode-coding-string name coding) string)
          (error (cons name string)))))))

(defun lyskom-maybe-set-var-from-string (var string &optional type portable-only)
  "This is a wrapper around lyskom-set-var-from-string that does nothing
if the variable is in kom-dont-read-saved-variables.

Return non-nil if the variable shouldn't have been set in the first place."
  (prog1 
      (cond ((eq kom-dont-read-saved-variables t) t)
	    ((memq var kom-dont-read-saved-variables) t)
	    ((and portable-only (memq var lyskom-non-portable-server-variables)) nil)
	    ((not (or (memq var lyskom-elisp-variables)
		      (assq var lyskom-transition-variables)
		      (lyskom-flag-global-variable-from-elisp var))) nil)
	    (t (lyskom-set-var-from-string var string type) t))
	(when (and portable-only (assq var lyskom-copy-transition-variables))
	  (set var (funcall (cdr (assq var lyskom-copy-transition-variables))
			    (symbol-value var))))))

(defun lyskom-set-var-from-string (var string &optional type)
  "This is a wrapper aroud read-from-string.
It returns nil, and writes a message when an error occurs."
  (set var (condition-case nil
               (funcall 
                (cdr (assq 'read
                           (cdr (assq (or type t)
                                      lyskom-global-variable-types))))
                string)
             (error
              (lyskom-format-insert (lyskom-get-string 'error-in-options)
                                    var string)
              nil))))


(defun lyskom-flag-value-to-string (symbol)
  "Convert value of SYMBOL to a string."
  (cond ((eq symbol 'kom-permanent-filter-list)
         (prin1-to-string 
          (mapcar (lambda (filter)
                    (lyskom-create-filter (filter->pattern filter)
                                          (filter->attribute-list filter)
                                          nil))
                  kom-permanent-filter-list)))
        (t (prin1-to-string (symbol-value symbol)))))


(defun lyskom-flag-global-variable-from-common (var)
  "Return the element from lyskom-global-variables corresponding to VAR."
  (lyskom-traverse el lyskom-global-variables
    (when (eq (aref el 0) var)
      (lyskom-traverse-break el))))

(defun lyskom-flag-global-variable-from-elisp (var)
  "Return the element from lyskom-global-variables corresponding to
elisp variable VAR."
  (lyskom-traverse el lyskom-global-variables
    (when (eq (aref el 1) var)
      (lyskom-traverse-break el))))

(defun lyskom-flag-write-boolean (val)
  (if val "1" "0"))

(defun lyskom-flag-read-boolean (str)
  (string= "1" str))

(defun lyskom-flag-write-symbol-list (val)
  (cond ((symbolp val)
         (lyskom-format-objects (symbol-name val)))
        (t (mapconcat (lambda (x) (lyskom-format-objects (symbol-name x)))
                      val " "))))

(defun lyskom-flag-read-symbol-list (str)
  (mapcar 'intern (lyskom-get-holerith-list str)))

(defun lyskom-flag-read-from-string (str)
  (car (read-from-string str)))

(defun lyskom-flag-read-integer (str)
  (cond ((equal str "nil") nil)
        (t (lyskom-string-to-number str))))

(defun lyskom-flag-write-integer (val)
  (cond ((integerp val) (prin1-to-string val))
        (t (signal 'lyskom-invalid-flag-type nil))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: messages.el
;;;; Author: David Byers
;;;;
;;;; This file implements the personal message handler queue
;;;;


(defvar lyskom-personal-message-handlers nil
  "A list of personal message handlers.

Each element of the list is a function of four arguments, MESSAGE-TYPE
SENDER RECIPIENT and TEXT. MESSAGE-TYPE is one of personal, group or
common and denotes the type of message. SENDER is the conf-stat of the
sender of the message. RECIPIENT is the conf-stat of the message
recipient or zero for common messages. 

The functions may use the lyskom-set-current-message-text function to
modify the message text. A non-nil return value from the function
indicates that the message was handled and no other handlers need to
be called and a nil return value means that the message was not
handled and should be sent to the next handler.")

(defvar lyskom-message-current-text ""
  "The text of the current message. Use
lyskom-set-current-message-text to modify this variable.")

(defun lyskom-set-current-message-text (text)
  "Set the current message text to TEXT. For use by personal message
handlers."
  (setq lyskom-message-current-text text))

(defun lyskom-handle-personal-message (sender recipient text)
  "Handle a personal message.

SENDER is the sender of the message (a conf-stat). RECIPIENT is the
recipient of the message (a conf-stat or 0 for common messages).
TEXT is the text of the message."
  (let ((message-type (cond ((eq recipient 0) 'common)
                            ((= (conf-stat->conf-no recipient)
                                lyskom-pers-no) 'personal)
                            (t 'group)))
        (lyskom-message-current-text text)
        (handlers lyskom-personal-message-handlers)
        (done nil))
    (while (and (not done)
                handlers)
      (setq done
            (funcall (car handlers) message-type sender recipient 
                   lyskom-message-current-text))
      (setq handlers (cdr handlers)))
    (if (not done)
        (lyskom-show-personal-message sender recipient 
                                      lyskom-message-current-text))))


(defun lyskom-add-personal-message-handler (handler 
                                            &optional place relative new)
  "Add HANDLER to the queue of personal message handlers. 
Optional argument PLACE can be one of 'before or 'after. Optional
argument RELATIVE can be another handler in the queue. IF fourth argument
NEW is t, the handler is only added if it does not already exist in the list.

The new handler is placed first in the queue if PLACE is 'before and
RELATIVE is not specified; last if PLACE is 'after and RELATIVE is not 
specified; or before or after the handler RELATIVE in the queue, depending
on the value of PLACE. If PLACE is nil, 'after is assumed."

  (if (or (not new)
          (not (memq handler lyskom-personal-message-handlers)))
      (progn
        (setq place (or (and (eq place 'before) 'before) 'after))
  (setq relative (car-safe (memq relative lyskom-personal-message-handlers)))
  (let ((pos (if relative
                 (- (length lyskom-personal-message-handlers)
                    (length (memq relative 
                                  lyskom-personal-message-handlers))))))
    (cond ((and relative
                (eq place 'after))
           (setcdr (nthcdr pos lyskom-personal-message-handlers)
                   (cons handler (nthcdr (1+ pos)
                                         lyskom-personal-message-handlers)))
           )
          ((and relative 
                (eq place 'before)
                (> pos 0))
           (setcdr (nthcdr (1- pos) lyskom-personal-message-handlers)
                   (cons handler
                         (nthcdr pos lyskom-personal-message-handlers)))
           )
          ((and lyskom-personal-message-handlers (eq place 'after))
           (setcdr (nthcdr (1- (length lyskom-personal-message-handlers))
                           lyskom-personal-message-handlers)
                   (cons handler nil)))
          ((or (null lyskom-personal-message-handlers) (eq place 'before))
           (setq lyskom-personal-message-handlers 
                 (cons handler
                       lyskom-personal-message-handlers)))
          (t (setcdr (nthcdr (1- (length lyskom-personal-message-handlers))
                             lyskom-personal-message-handlers)
                     (cons handler nil)))))
  lyskom-personal-message-handlers)))
        

(defun lyskom-info-request-handler (message-type sender recipient text)
  (if (lyskom-string= text "\011\016\006\017")
      (progn
        (initiate-send-message 
         'follow 
         nil
         (conf-stat->conf-no sender)
         (format "emacs-version:  %s\nclient-version: %s"
                 (emacs-version)
                 lyskom-clientversion))
        t)
    nil))

(defun lyskom-filter-message-handler (message-type sender recipient text)
  "Optionally kill messages from certain senders or to certain recipients.
See the documentation for kom-ignore-message-recipients and 
kom-ignore-message-senders for more information."
  (condition-case nil
      (or (memq (cond ((lyskom-conf-stat-p sender) (conf-stat->conf-no sender))
                      ((lyskom-uconf-stat-p sender) (uconf-stat->conf-no sender))
                      (t sender)) 
                kom-ignore-message-senders)
          (memq (cond ((lyskom-conf-stat-p recipient) (conf-stat->conf-no recipient))
                      ((lyskom-uconf-stat-p recipient) (uconf-stat->conf-no recipient))
                      (t recipient)) 
                kom-ignore-message-recipients))
    (error nil)))


(lyskom-add-personal-message-handler 'lyskom-filter-message-handler 'before)
(lyskom-add-personal-message-handler 'lyskom-info-request-handler 'before)

(eval-and-compile (provide 'lyskom-messages))

;;; messages.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: ansaphone.el
;;;; Author: David Byers
;;;;
;;;; This file implements the auto-reply facility.
;;;; It must be loaded after messages.el
;;;;

(eval-when-compile
  (require 'lyskom-vars "vars")
  (require 'lyskom-macros "macros")
  (require 'lyskom-command "command")
  (require 'lyskom-language "language")
  (require 'lyskom-messages "messages")
  (require 'lyskom-services "services"))


(defconst lyskom-ansaphone-tag "Auto-reply:\n")


;;;============================================================
;;;
;;; User functions
;;;

(def-kom-command kom-change-auto-reply (&optional message)
  "Change the default automatic reply message.
This message is sent in reply to personal messages when auto
reply is turned on.

See `kom-toggle-auto-reply'."
  (interactive)
  (let ((message (or message
                    (lyskom-read-from-minibuffer 
                     (lyskom-get-string 'ansaphone-new-message)
                     (if (stringp kom-ansaphone-default-reply)
                          (cons kom-ansaphone-default-reply 0)))))
        (lyskom-last-text-format-flags nil))
    (setq kom-ansaphone-default-reply message)
    (lyskom-format-insert (lyskom-get-string-sol 'ansaphone-message)
                          kom-ansaphone-default-reply)))


(def-kom-command kom-toggle-auto-reply ()
  "Toggle automatic replies to personal messages.
When this is turned on, replies will be sent automatically to 
personal messages (sent with `kom-send-message'). The reply sent
depents on `kom-ansaphone-default-reply' and `kom-ansaphone-replies'.

Messages received when automatic replies are on may also be recorded.
See `kom-ansaphone-record-messages'"
  (interactive)
  (setq kom-ansaphone-on (not kom-ansaphone-on))
  (lyskom-format-insert (lyskom-get-string 'ansaphone-state-r)
                        (lyskom-get-string (if kom-ansaphone-on
                                               'state-on
                                             'state-off)))
  (if kom-ansaphone-on
      (progn
        (setq lyskom-ansaphone-when-set (lyskom-format-time
                                         'timeformat-yyyy-mm-dd-hh-mm))
        (lyskom-format-insert (lyskom-get-string-sol 'ansaphone-message)
                              kom-ansaphone-default-reply))))


(def-kom-command kom-list-messages ()
  "List messages recorded when automatic replies are on. To erase
recorded messages, use `kom-erase-messages'.

See `kom-toggle-auto-reply' and `kom-ansaphone-record-messages'."
  (interactive)
  (if (null lyskom-ansaphone-messages)
      (lyskom-format-insert (lyskom-get-string 'ansaphone-no-messages))
    (progn
      (lyskom-format-insert (lyskom-get-string 'ansaphone-message-list-start))
      (mapc (function
             (lambda (msg)
               (lyskom-show-personal-message 
                (blocking-do 'get-conf-stat (elt msg 0))
                (blocking-do 'get-conf-stat (elt msg 1))
                (elt msg 2)
                (elt msg 3)
                'nobeep)))
              (reverse lyskom-ansaphone-messages))
      (lyskom-format-insert (lyskom-get-string 'ansaphone-message-list-end)))))


(def-kom-command kom-erase-messages ()
  "Erase messages recorded when automatic replies are on. To list
recorded messages, use `kom-list-messages'.

See `kom-toggle-auto-reply' and `kom-ansaphone-record-messages'."
  (interactive)
  (lyskom-message "%s" (lyskom-get-string 'ansaphone-messages-gone))
  (setq lyskom-ansaphone-messages nil))



(defun lyskom-ansaphone-send-message (recipient message)
  (initiate-send-message 'async 
                         nil
                         (if (numberp recipient)
                             recipient
                           (conf-stat->conf-no recipient))
                         (concat lyskom-ansaphone-tag
                                 message)))



(defun lyskom-ansaphone-message-handler (message-type sender recipient text)
  "Personal message handler.

Automatically reply to certain personal messages and strip auto-reply
identification from messages.

See kom-ansaphone-on"

  (let ((is-automatic (eq 0 (string-match lyskom-ansaphone-tag text))))
    (if is-automatic
        (progn
          (string-match (concat "^"
                                lyskom-ansaphone-tag 
                                "\\(\\(.\\|\n\\)*\\)") text)
          (lyskom-set-current-message-text (substring text
                                                      (match-beginning 1)
                                                      (match-end 1)))))

    ;;
    ;; See if we want to reply to this message
    ;;

    (if (and kom-ansaphone-on
             sender
	     recipient
             (not is-automatic))
        (let ((reply (lyskom-ansaphone-find-reply 
                      message-type
		      (conf-stat->conf-no sender)
                      (cond ((numberp recipient) recipient)
			    (t (conf-stat->conf-no recipient)))
                      text)))
          (if (and reply (elt reply 4))
              (progn
                (setq reply
                      (concat 
                       (lyskom-format
                        (lyskom-get-string 'ansaphone-message-header)
                        lyskom-ansaphone-when-set)
                       (elt reply 4)))
                (lyskom-ansaphone-send-message sender reply)))))

    ;;
    ;; See if we want to record this message
    ;;

    (if (and kom-ansaphone-on 
             kom-ansaphone-record-messages
             sender)
        (lyskom-ansaphone-record-message sender
                                         recipient
                                         lyskom-message-current-text)))

  ;;
  ;; Perhaps we want to show the message, perhaps not
  ;;
  
  (if kom-ansaphone-on
      (not kom-ansaphone-show-messages)
    nil))



(defun lyskom-ansaphone-find-reply (message-type sender recipient text)
  "Find an automatic reply suitable for messages of type MESSAGE-TYPE from
SENDER to RECIPIENT consisting of TEXT. See the documentation for
kom-ansaphone-default-reply and kom-ansaphone-replies."
  (let ((exprs kom-ansaphone-replies)
        (result nil))
    (while exprs
      (if (and (or (null (elt (car exprs) 0))
                   (eq (elt (car exprs) 0) message-type))
               (or (null (elt (car exprs) 1))
                   (eq (elt (car exprs) 1) sender)
                   (and (listp (elt (car exprs) 1))
                        (memq sender (elt (car exprs) 1))))
               (or (null (elt (car exprs) 2))
                   (eq (elt (car exprs) 2) recipient)
                   (and (listp (elt (car exprs) 2))
                        (memq recipient (elt (car exprs) 2))))
               (or (null (elt (car exprs) 3))
                   (string-match (elt (car exprs) 3) text)))
          (progn
            (setq result (car exprs))
            (setq exprs nil)))
      (setq exprs (cdr-safe exprs)))
    (or result (and (eq message-type 'personal)
                    (list nil nil nil nil kom-ansaphone-default-reply)))))
        

(defun lyskom-ansaphone-record-message (sender recipient text)
  (if (not (numberp sender))
      (setq sender (conf-stat->conf-no sender)))
  (if (not (numberp recipient))
      (setq recipient (conf-stat->conf-no recipient)))
  (setq lyskom-ansaphone-messages (cons (list sender recipient text
                                              (lyskom-current-client-time))
                                        lyskom-ansaphone-messages)))



(lyskom-add-personal-message-handler 'lyskom-ansaphone-message-handler
                                     'before
                                     nil
                                     t)


(eval-and-compile (provide 'lyskom-ansaphone))

;;; ansaphone.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: remote-control.el
;;;; Author: David Byers
;;;;
;;;; This file implements the remote control mechanism.
;;;; It must be loaded after messages.el
;;;;

(eval-when-compile
  (require 'lyskom-command "command"))


;;;============================================================
;;;
;;;     Internal variables.
;;;

(defconst lyskom-remote-commands
  '(("set message" . lyskom-remote-set-message)
    ("list messages" . lyskom-remote-list-messages)
    ("erase messages" . lyskom-remote-erase-messages)
    ("autoreply" . lyskom-remote-autoreply)
    ("quit" . lyskom-remote-quit)))


;;;============================================================
;;;
;;;     Interactive functions
;;;

(def-kom-command kom-remote-autoreply (&optional session-no state)
  "Remotely turn on or off the auto reply facility of another client.
This is similar to `kom-toggle-auto-reply', but operates on another
session.

You will be prompted for which session to control. For remote control
to work the target session must have remote control turned on, and the
user you are logged on as must have permission to manipulate that
session.

See `kom-remote-controllers' and `kom-self-control' for settings that
affect remote control."
  (interactive)
  (setq session-no (or session-no
                       (car (lyskom-read-session-no
                             'remote-control-who
                             nil nil t))))
  (setq state (or state
                  (cdr-safe (assoc
                             (lyskom-completing-read
                              (lyskom-get-string 'remote-control-autoreply)
                              (lyskom-maybe-frob-completion-table
			       lyskom-onoff-table t)
			      nil t nil nil)
                             (lyskom-maybe-frob-completion-table
			      lyskom-onoff-table t)))))
  (let ((info (blocking-do 'get-session-info session-no)))
    (lyskom-send-message (session-info->pers-no info)
                         (format "Remote-command: %d %d\nautoreply\n%s"
                                 lyskom-session-no
                                 session-no
                                 (cond ((eq state 'on) "on")
                                       ((eq state 'off) "off")
                                       (t "")))
			 t)))

(def-kom-command kom-remote-set-message (&optional session-no message)
  "Remotely set the default auto reply message of another client. This
is similar to `kom-change-auto-reply', but operates on another
session.

You will be prompted for which session to control. For remote control
to work the target session must have remote control turned on, and the
user you are logged on as must have permission to manipulate that
session.

See `kom-remote-controllers' and `kom-self-control' for settings that
affect remote control."
  (interactive)
  (setq session-no (or session-no
                       (car (lyskom-read-session-no
                             'remote-control-who
                             nil nil t))))
  (setq message (or message
                    (lyskom-read-string
                     (lyskom-get-string 'message-prompt))))
  (let ((info (blocking-do 'get-session-info session-no)))
    (lyskom-send-message (session-info->pers-no info)
                         (format "Remote-command: %d %d\nset message\n%s"
                                 lyskom-session-no
                                 session-no
                                 message)
			 t)))

(def-kom-command kom-remote-list-messages (&optional session-no)
  "List messages collected from a remote auto reply facility. This is
similar to `kom-list-messages', but operates on another session.

You will be prompted for which session to control. For remote control
to work the target session must have remote control turned on, and the
user you are logged on as must have permission to manipulate that
session.

See `kom-remote-controllers' and `kom-self-control' for settings that
affect remote control."
  (interactive)
  (setq session-no (or session-no
                       (car (lyskom-read-session-no
                             'remote-control-who
                             nil nil t))))
  (let ((info (blocking-do 'get-session-info session-no)))
    (lyskom-send-message (session-info->pers-no info)
                         (format "Remote-command: %d %d\nlist messages\n"
                                 lyskom-session-no
                                 session-no)
			 t)))



(def-kom-command kom-remote-erase-messages (&optional session-no)
  "Erase stored messages on a remote auto reply facility. This is
similar to `kom-erase-messages', but operates on another session.

You will be prompted for which session to control. For remote control
to work the target session must have remote control turned on, and the
user you are logged on as must have permission to manipulate that
session.

See `kom-remote-controllers' and `kom-self-control' for settings that
affect remote control."
  (interactive)
  (setq session-no (or session-no
                       (car (lyskom-read-session-no
                             'remote-control-who
                             nil nil t))))
  (let ((info (blocking-do 'get-session-info session-no)))
    (lyskom-send-message (session-info->pers-no info)
                         (format "Remote-command: %d %d\nerase messages\n"
                                 lyskom-session-no
                                 session-no)
			 t)))


(def-kom-command kom-remote-quit (&optional session-no)
  "Quit a remote client. The difference between this command and
`kom-force-logout' is that the latter uses server facilities to
forcefully disconnect the session, while the former allows the client
to cleanly disconnect from the server.

You will be prompted for which session to control. For remote control
to work the target session must have remote control turned on, and the
user you are logged on as must have permission to manipulate that
session.

See `kom-remote-controllers' and `kom-self-control' for settings that
affect remote control."
  (interactive)
  (setq session-no (or session-no
                       (car (lyskom-read-session-no
                             'remote-control-who
                             nil nil t))))
  (let ((info (blocking-do 'get-session-info session-no)))
    (lyskom-send-message (session-info->pers-no info)
                         (format "Remote-command: %d %d\nquit\n"
                                 lyskom-session-no
                                 session-no)
			 t)))

;;;============================================================
;;;
;;;     Main handler function
;;;


(defun lyskom-remote-handler (message-type sender recipient text)
  "Personal message handler.

Handler to implement remote control of the ansaphone."
  (let* ((error nil)
         (is-remote (eq 0 
                        (string-match "^Remote-command: \\([0-9]+\\) \\([0-9]+\\)\n" text)))
         (is-from-me (and is-remote
                          (= (string-to-number (substring text
                                                          (match-beginning 1)
                                                          (match-end 1)))
                             lyskom-session-no)))
         (is-to-me (and is-remote
                        (= (string-to-number (substring text
                                                        (match-beginning 2)
                                                        (match-end 2)))
                           lyskom-session-no)))
         (is-valid (eq 0 
                       (string-match
                        "^Remote-command: \\([0-9]+\\) \\([0-9]+\\)\n\\(.*\\)\n\\(\\(\n\\|.\\)*\\)$"
                        text)))
         (is-trusted (or (memq (conf-stat->conf-no sender)
                               kom-remote-controllers)
                         (and kom-self-control
                              (eq (conf-stat->conf-no sender)
                                  lyskom-pers-no)))))
    (cond ((not is-remote) nil)
          (is-from-me t)
          ((not is-to-me) t)
          ((not is-trusted)
           (lyskom-ansaphone-send-message
            lyskom-pers-no
            (lyskom-format (lyskom-get-string 'illegal-remote)
                           (lyskom-format-time 'timeformat-yyyy-mm-dd-hh-mm)
                           sender
                           recipient
                           text))
           (lyskom-ansaphone-send-message
            sender
            (lyskom-format (lyskom-get-string 'illegal-remote-reply)
                           (lyskom-get-string 'remote-not-in-list)))
           t)
          ((not is-valid) (setq error 'remote-bad-command))
          (t (let* ((command (substring text (match-beginning 3)
                                        (match-end 3)))
                   (arg (substring text (match-beginning 4) (match-end 4)))
                   (desc (assoc command lyskom-remote-commands)))
               (if (null desc)
                   (setq error 'remote-bad-command)
                 (setq error (funcall (cdr desc)
                                      arg
                                      sender
                                      recipient
                                      text))))))
    (if error
        (progn
          (lyskom-ansaphone-send-message 
           sender
           (lyskom-format (lyskom-get-string 'illegal-remote-reply)
                          (or (lyskom-get-string error) 
                              (lyskom-get-string 'remote-unknown-error))))))
    is-remote))




(defun lyskom-remote-set-message (arg sender recipient text)
  (if arg
      (let ((lyskom-last-text-format-flags nil))
        (setq kom-ansaphone-default-reply arg)
        (setq lyskom-ansaphone-when-set (lyskom-format-time
                                         'timeformat-yyyy-mm-dd-hh-mm))
        (lyskom-ansaphone-send-message sender
                                       (concat 
                                        (lyskom-get-string
                                         'ansaphone-new-message)
                                        arg))
        (lyskom-insert-before-prompt
         (lyskom-format (lyskom-get-string-sol 'remote-set-message)
                        sender
                        (lyskom-format-time 'timeformat-yyyy-mm-dd-hh-mm)
                        arg))
        nil)
    'remote-bad-command))
  


;;;============================================================
;;;
;;;     Command handlers
;;;


(defun lyskom-remote-autoreply (arg sender recipient text)
  (cond ((string= (downcase arg) "on") (setq kom-ansaphone-on t))
        ((string= (downcase arg) "off") (setq kom-ansaphone-on nil))
        (t (setq kom-ansaphone-on (not kom-ansaphone-on))))
  (lyskom-ansaphone-send-message 
   sender (lyskom-format (lyskom-get-string 'ansaphone-state)
                         (lyskom-get-string
                          (if kom-ansaphone-on 
                              'state-on 'state-off))))
  (lyskom-insert-before-prompt
   (lyskom-format (lyskom-get-string 'remote-set-ansaphone)
                  sender
                  (lyskom-format-time 'timeformat-yyyy-mm-dd-hh-mm)
                  (lyskom-get-string (if kom-ansaphone-on 
                                         'state-on 'state-off))))
  nil)



(defun lyskom-remote-list-messages (arg sender recipient text)
  (if (null lyskom-ansaphone-messages)
      (lyskom-ansaphone-send-message 
       sender
       (lyskom-get-string 'ansaphone-no-messages))
    (progn
      (lyskom-collect 'follow)
      (let ((tmp (reverse lyskom-ansaphone-messages)))
        (while tmp
          (initiate-get-conf-stat 'follow nil (elt (car tmp) 0))
          (initiate-get-conf-stat 'follow nil (elt (car tmp) 1))
          (setq tmp (cdr tmp))))
      (lyskom-use 'follow 'lyskom-remote-list-messages-1 sender)))
  nil)

(defun lyskom-remote-list-messages-1 (sender &rest pairs)
  (let ((message "")
        (tmp (reverse lyskom-ansaphone-messages))
        (from nil)
        (to nil))
  (while pairs
    (setq from (car pairs) to (car (cdr pairs)))
    (setq pairs (cdr (cdr pairs)))
    (setq message (concat message
                          (lyskom-format-as-personal-message
                           from
                           to
                           (elt (car tmp) 2)
                           (elt (car tmp) 3))
                          (if pairs "\n\n" "")))
    (setq tmp (cdr tmp)))
  (lyskom-ansaphone-send-message sender message)
  (lyskom-insert-before-prompt
   (lyskom-format (lyskom-get-string 'remote-list-messages)
		  sender
                  (lyskom-format-time 'timeformat-yyyy-mm-dd-hh-mm)))))





(defun lyskom-remote-erase-messages (arg sender recipient text)
  (setq lyskom-ansaphone-messages nil)
  (lyskom-format-insert-before-prompt
    (lyskom-get-string 'remote-erase-messages)
    sender
    (lyskom-format-time 'timeformat-yyyy-mm-dd-hh-mm))
  nil)


(defun lyskom-remote-quit (arg sender recipient text)
  (lyskom-insert-before-prompt
   (lyskom-format (lyskom-get-string 'remote-quit)
                  sender
                  (lyskom-format-time 'timeformat-yyyy-mm-dd-hh-mm)))
   (lyskom-quit))


;;;============================================================
;;;
;;;     Clean-up and installation
;;;

(lyskom-add-personal-message-handler 'lyskom-remote-handler
                                     'before
                                     'lyskom-ansaphone-message-handler
                                     t)
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: menus.el
;;;; Author: Niels Möller
;;;;
;;;;


(lyskom-external-function set-buffer-menubar)
(lyskom-external-function popup-menu)
(lyskom-external-function add-submenu)


(def-kom-var lyskom-current-menu-category nil
  "Category of menus currently used in buffer"
  local)


(defvar lyskom-menu-template
  '((menu read
          ((item kom-view-next-text)
           (item kom-list-news)
           (item kom-list-summary)
           (item kom-go-to-conf)
           (hline)
           (menu review
                 ((item kom-view)
                  (item kom-review-next)
                  (item kom-review-last-normally-read)
                  (item kom-review-noconversion)
                  (item kom-review-converted)
                  (item kom-review-rot13)
                  (item kom-review-mail-headers)
                  (hline)
                  (item kom-find-root)
                  (item kom-view-commented-text)
                  (item kom-review-comments)
                  (item kom-review-more-comments)
		  (item kom-review-cross-references)
                  (item kom-review-tree)
                  (item kom-find-root-review)
                  (hline)
                  (item kom-review-by-to)
                  (item kom-review-roots-by-to)
                  (item kom-review-first)
                  (item kom-review-first-roots)
                  (item kom-review-all)
                  (item kom-review-all-roots)
                  (item kom-review-more)
                  (hline)
                  (item kom-make-review-mark-as-read)
                  (item kom-make-review-not-mark-as-read)
                  (hline)
                  (item kom-review-stack)
                  (item kom-review-backward)
                  (item kom-review-clear)))
           (menu unread
                 ((item kom-mark-unread)
                  (item kom-unread-last-normally-read)
                  (hline)
                  (item kom-unread-root)
                  (item kom-unread-commented-text)
                  (item kom-unread-comments)
                  (item kom-unread-tree)
                  (item kom-unread-root-review)
                  (hline)
                  (item kom-unread-by-to)
                  (item kom-unread-roots-by-to)
                  (item kom-unread-first)
                  (item kom-unread-first-roots)
                  (item kom-unread-all)
                  (item kom-unread-all-roots)
                  (item kom-unread-more)
                  (hline)
                  (item kom-unread-presentation)
                  (item kom-unread-faq)
                  (item kom-unread-server-faq)))
           (menu marks
                 ((item kom-mark-text)
                  (item kom-unmark-text)
                  (hline)
                  (item kom-list-marks)
                  (item kom-review-marked-texts)
                  (item kom-review-all-marked-texts)
                  (hline)
                  (item kom-unread-marked-texts)
                  (item kom-unread-all-marked-texts)))
           (menu filters
                 ((item kom-filter-author)
                  (item kom-filter-subject)
                  (item kom-filter-text)
                  (item kom-filter-recipient)
                  (hline)
                  (item kom-list-filters)
                  (item kom-filter-edit)))
           (hline)
           (item kom-save-text)
           (item kom-save-text-body)
           (item kom-view-url-in-text)
           (hline)
           (item kom-compare-texts)
           (item kom-diff-texts)
           (hline)
           (item kom-postpone)
           (item kom-jump)
           (item kom-super-jump)
           (item kom-set-unread)))

    (menu write
          ((item kom-write-text)
           (item kom-send-letter)
           (item kom-write-comment)
           (item kom-private-answer)
           (item kom-write-footnote)
           (hline)
           (menu receivers
                 ((item kom-will-person-read-text)
                  (hline)
                  (item kom-add-recipient)
                  (item kom-add-copy)
                  (item kom-add-bcc)
                  (item kom-sub-recipient)
                  (hline)
                  (item kom-move-text)
                  (item kom-move-text-tree)))
           (menu commented
                 ((item kom-add-comment)
                  (item kom-sub-comment)
		  (item kom-move-comment)
                  (item kom-add-footnote)
                  (item kom-sub-footnote)))
           (menu aux-items 
                 ((item kom-add-cross-reference)
                  (item kom-add-private-answer)
                  (item kom-add-no-comments)
                  (item kom-add-request-confirm)
                  (item kom-fast-reply)
                  (item kom-agree)))
           (hline)
           (item kom-next-kom)
           (item kom-next-unread-kom)
           (item kom-previous-kom)
           (hline)
           (item kom-set-session-priority)
           (hline)
           (item kom-send-message)))

    (menu conference
          ((item kom-go-to-conf)
           (item kom-go-to-next-conf)
           (hline)
           (item kom-review-presentation)
           (item kom-status-conf)
           (item kom-review-faq)
           (item kom-list-faqs)
           (hline)
           (item kom-list-conferences)
           (item kom-list-re)
           (item kom-list-new-conferences)
           (hline)
           (menu conf-admin
                 ((item kom-change-presentation)
                  (item kom-set-presentation)
                  (item kom-remove-presentation)
                  (hline)
                  (item kom-change-name)
                  (item kom-change-supervisor)
                  (item kom-set-garb-nice)
                  (item kom-set-super-conf)
                  (item kom-set-permitted-submitters)
                  (item kom-change-conf-type)
                  (item kom-limit-import)
                  (hline)
                  (item kom-change-conf-motd)
                  (item kom-set-motd-text)
                  (item kom-unset-conf-motd)
                  (hline)
                  (item kom-change-conf-faq)
                  (item kom-add-faq)
                  (item kom-del-faq)
                  (hline)
                  (item kom-sub-member)
                  (hline)
                  (item kom-create-conf)
                  (item kom-delete-conf)
                  (item kom-list-created-conferences)))
           (hline)
           (item kom-add-self)
           (item kom-sub-self)
           (item kom-add-member)
           (item kom-prioritize)))

    (menu person
          ((item kom-who-is-on)
           (item kom-who-is-on-in-conference)
           (item kom-who-is-present-in-conference)
           (item kom-who-is-on-and-friend)
           (hline)
           (item kom-list-persons)
           (item kom-list-re)
           (item kom-list-new-persons)
           (hline)
           (item kom-review-presentation)
           (item kom-status-person)
           (item kom-status-session)
           (item kom-list-created-conferences)
           (item kom-is-person-member-of-conference)
           (item kom-will-person-read-text)
           (hline)
           (menu membership
                 ((item kom-add-self)
                  (item kom-sub-self)
                  (hline)
                  (item kom-join-all-conferences)
                  (item kom-leave-all-conferences)
                  (hline)
                  (item kom-change-priority)
                  (item kom-change-message-flag)
                  (item kom-prioritize)))
           (menu pers-admin
                 ((item kom-change-presentation)
                  (item kom-change-name)
                  (item kom-change-parenthesis)
                  (item kom-change-password)
                  (item kom-redirect-comments)
                  (item kom-change-privileges)))
           (hline)
           (item kom-befriend)
           (item kom-moronify)
           (hline)
           (item kom-who-am-i)))

    (menu server
          ((item kom-status-server)
           (item kom-review-server-faq)
           (item kom-list-server-faqs)
           (hline)
           (menu server-admin
                 ((item kom-change-server-faq)
                  (item kom-add-server-faq)
                  (item kom-del-server-faq)
                  (hline)
                  (item kom-set-motd)
                  (item kom-remove-motd)
                  (hline)
                  (item kom-recommend-conference)
                  (hline)
                  (item kom-enable-adm-caps)
                  (item kom-disable-adm-caps)
                  (hline)
                  (item kom-shutdown-server)
                  (item kom-sync-database)))
           ))


    (menu other
          ((item kom-help)
           (menu language
                 ((item kom-change-global-language)
                  (item kom-change-local-language)))
           (item kom-where-is)
           (item kom-display-time)
           (item kom-calculate)
           (hline)
           (item kom-customize)
           (item kom-copy-options)
           (item kom-save-options)
           (item kom-show-user-area)
           (hline)
           (menu remote-control
                 ((item kom-remote-autoreply)
                  (item kom-remote-set-message)
                  (item kom-remote-list-messages)
                  (item kom-remote-erase-messages)
                  (item kom-remote-quit)))
           (menu autoreply
                 ((item kom-toggle-auto-reply)
                  (item kom-change-auto-reply)
                  (item kom-list-messages)
                  (item kom-erase-messages)))
           (hline)
           (item kom-keep-alive)
           (item kom-stop-keep-alive)
           (hline)
           (item kom-recover)
           (item kom-start-anew)
           (item kom-force-logout)
	   (item kom-list-sessions)
           (item kom-quit))))

  "The menus used in LysKOM.")

(defvar lyskom-popup-menu-template
  `(menu lyskom (,@lyskom-menu-template))
  "Popup-menu in the backgrouond of the LysKOM window")


(defvar lyskom-edit-menu-template
  '((menu lyskom
	  ((item kom-ispell-message)
           (item kom-edit-send)
	   (hline receiver-separator)
	   (item kom-edit-add-recipient)
	   (item kom-edit-add-copy)
           (item kom-edit-add-bcc)
           (item kom-edit-move-text)
           (item kom-edit-add-cross-reference)
           (hline special-separator)
           (item kom-edit-add-no-comments)
           (item kom-edit-add-personal-comments)
           (item kom-edit-add-read-confirm-request)
	   (hline comment-separator)
	   (item kom-edit-show-commented)
	   (hline send-separator)
	   (item kom-edit-quit))))
  "The menus for editing LysKOM messages.")

(defvar lyskom-prioritize-menu-template
  '((menu lyskom
          ((item lp--toggle-membership-selection)
           (hline select-separator-1)
           (item lp--select-region)
           (item lp--select-priority)
           (hline select-separator-2)
           (item lp--deselect-priority)
           (item lp--deselect-all)
           (hline select-separator-3)
           (menu lp--membership-priority
                 ((item lp--set-priority)
                  (item lp--move-up)
                  (item lp--move-down)
                  (item lp--increase-priority)
                  (item lp--decrease-priority)
                  (item lp--yank)))
           (menu lp--membership-type
                 ((item lp--toggle-invitation)
                  (item lp--toggle-passive)
                  (item lp--toggle-message-flag)
                  (item lp--toggle-secret)))
           (menu lp--filter
                 ((item lp--show-hide-memberships-read-before)
                  (item lp--show-hide-memberships-read-after)
                  (item lp--show-hide-read-memberships)
                  (item lp--show-hide-passive-memberships)
                  (hline filter-separator)
                  (item lp--show-all)))
           (menu lp--membership-expansion
                 ((item lp--toggle-entry-expansion)
                  (item lp--expand-entry)
                  (item lp--contract-entry))
                 (hline quit-separator)
                 (item lp--quit))))))



(defvar lyskom-menu-list '((lyskom-mode . lyskom-menu)
                           (lyskom-edit-mode . lyskom-edit-menu)
                           (lp--mode . lyskom-prioritize-menu))
  "List of menu sets in LysKOM")

(defvar lyskom-menu nil
  "A keymap describing the LysKOM top menu.")

(defvar lyskom-edit-menu nil
  "A keymap the LysKOM menu in the edit buffer.")

(defvar lyskom-prioritize-menu nil
  "Menus for the prioritize mode.")

(defvar lyskom-popup-menu nil
  "A keymap the LysKOM menu in the edit buffer.")

;;; ================================================================
;;; Hack for swedish bindings and shortcuts

(defvar lyskom-swedish-bindings 
  `((å . (aring Aring ,(elt (kbd "å") 0) ,(elt (kbd "Å") 0)
                   å Å 229 197 2277 2245 3909 3941))
    (ä . (adiaeresis Adiaeresis ,(elt (kbd "ä") 0) ,(elt (kbd "Ä") 0)
                        ä Ä 228 196 2276 2244 3908 3940)))
  "Hack to deal with binding swedish characters")

(defvar lyskom-swedish-bindings-reverse
  (let ((tmp nil)
        (seq lyskom-swedish-bindings))
    (while seq
      (let ((el (car seq)))
        (setq tmp (nconc tmp (mapcar (lambda (x)
                                       (cons x (car el)))
                                     (cdr el)))))
      (setq seq (cdr seq)))
    tmp)
  "Hack to deal with binding swedish characters")

(defun lyskom-menu-guess-shortcuts (keymap &optional prefix result force)
  (lyskom-traverse-keymap
   (lambda (key binding)
     (when binding
       (let ((force (or force (assq key lyskom-swedish-bindings-reverse))))
         (if (keymapp binding)
             (setq result
                   (lyskom-menu-guess-shortcuts
                    (cond ((symbolp binding) (symbol-value binding))
                          (t binding))
                    (append prefix (list key))
                    result
                    force))
           (when force
             (setq key `(,@prefix ,key))
             (if (assq binding result)
                 (setcdr (assq binding result)
                         (cons (apply 'vector key)
                               (cdr (assq binding result))))
               (setq result (cons (cons binding 
                                        (list (apply 'vector key)))
                                  result))))))))
   keymap)
  result)


;;; ============================================================
;;; Creating menus

(defvar lyskom-menu-encoding-rules
  (lyskom-xemacs-or-gnu
   '((title . iso-8859-1)
     (item . iso-8859-1)
     (keys . iso8859-1))
   (cond ((< emacs-major-version 22) '((title . iso-8859-1)
				      (item . iso-8859-1)
				      (keys . iso8859-1)))
	 ((boundp 'gtk-version-string) '((title . utf-8)
					(item . utf-8)
					(keys . utf-8)))
	 ((eq window-system 'ns) '((title . utf-8)
				   (item . utf-8)
				   (keys . utf-8)))
	 ((eq window-system 'mac) '((title . mac-roman)
				    (item . nil)
				    (keys . nil)))
         ((featurep 'mac-carbon) '((title . mac-roman)
                                  (item . mac-roman)
                                  (keys . iso8859-1)))
	 ((memq window-system '(win32 mswindows w32)) '((title . utf-8)
				   (item . nil)
				   (keys . utf-8)))
	 (t '((title . iso-8859-1) (item . iso-8859-1) (keys . iso8859-1)))))
  "Rules for encoding menu strings")

(defun lyskom-menu-encode (string what)
  "Encode a string for use in menus.
STRING is the string to encode
WHAT is one of title, item or keys"
  (let ((coding (cdr (assq what lyskom-menu-encoding-rules))))
    (cond ((and coding (coding-system-p coding))
	   (lyskom-encode-coding-string string coding))
	  ((functionp coding)
	   (funcall coding string))
	  (t string))))

(defun lyskom-build-menus ()
  "Create menus according to LYSKOM-MENUS"
  (lyskom-xemacs-or-gnu (lyskom-build-menus-xemacs)
                        (lyskom-build-menus-gnu)))


(defun lyskom-build-menus-xemacs ()
  (setq lyskom-prioritize-menu (lyskom-define-menu-xemacs lyskom-prioritize-menu-template))
  (setq lyskom-edit-menu (lyskom-define-menu-xemacs lyskom-edit-menu-template))
  (setq lyskom-popup-menu (lyskom-define-menu-xemacs
                               lyskom-popup-menu-template))
  (setq lyskom-menu (lyskom-define-menu-xemacs lyskom-menu-template)))


(defun lyskom-build-menus-gnu ()
  "Rebuilds the LysKOM menus"
  (setq lyskom-menu (make-sparse-keymap))
  (setq lyskom-edit-menu (make-sparse-keymap))
  (setq lyskom-popup-menu (make-sparse-keymap))
  (setq lyskom-prioritize-menu (make-sparse-keymap))

  (lyskom-define-menu-gnu lyskom-prioritize-menu lyskom-prioritize-menu-template)
  (lyskom-define-menu-gnu lyskom-menu lyskom-menu-template)
  (lyskom-define-menu-gnu lyskom-edit-menu lyskom-edit-menu-template)
  (lyskom-define-menu-gnu lyskom-popup-menu
                          (list lyskom-popup-menu-template))
  (setq lyskom-popup-menu (lookup-key lyskom-popup-menu [lyskom])))

(defun lyskom-define-menu-xemacs (menus &optional specials)
  (let ((specials (or specials
		      (when (current-local-map)
			(lyskom-menu-guess-shortcuts (current-local-map))))))
    (cond ((null (car menus)))
          ((listp (car menus))          ; Menu bar
           (mapcar (lambda (x) (lyskom-define-menu-xemacs x specials)) menus))
          ((eq (car menus) 'menu)       ; A menu
           (let ((menu-title (car (cdr menus)))
                 (menu-items (car (cdr (cdr menus)))))
             (cons (lyskom-menu-encode 
		    (lyskom-get-menu-string menu-title) 'title)
                   (mapcar
		    (lambda (item)
		      (let ((type (car item))
			    (symbol (car (cdr item))))
			(cond ((eq type 'item)
			       (let ((shortcut nil))
				 (when (assq symbol specials)
				   (unless (lyskom-traverse key (cdr (assq symbol specials))
							    (unless (condition-case nil
									(eq (lookup-key (current-local-map) 
											key) symbol)
								      (error nil))
							      (lyskom-traverse-break t)))
				     (setq shortcut
					   (mapconcat 
					    (lambda (key)
					      (if (assq key lyskom-swedish-bindings-reverse)
						  (symbol-name 
						   (cdr (assq key lyskom-swedish-bindings-reverse)))
						(single-key-description key)))
					    (car (cdr (assq symbol specials)))
					    " "))))
				 (if shortcut
				     (vector (lyskom-menu-encode
					      (lyskom-get-menu-string symbol)
					      'item)
					     symbol
					     ':active t
					     ':keys (lyskom-menu-encode
						     shortcut 'keys))
				   (vector (lyskom-menu-encode 
					    (lyskom-get-menu-string symbol)
					    'item) symbol ':active t))))
			      ((eq type 'hline)
			       "--:shadowEtchedIn")
			      ((eq type 'menu)
			       (lyskom-define-menu-xemacs item specials))
			      (t (error "Bad menu item: %S" item)))))
		    menu-items))))

          (t nil))))

(defvar lyskom-define-menu-gnu-title-encoding-type 'title)
(defun lyskom-define-menu-gnu (map menus &optional specials)
  (let ((specials (or specials
		      (when (current-local-map)
			(lyskom-menu-guess-shortcuts (current-local-map))))))
    (when menus
      (lyskom-define-menu-gnu map (cdr menus) specials)
      (let ((type (car (car menus)))
            (symbol (car (cdr (car menus)))))
        (cond ((eq 'hline type)
               (define-key map (vector (lyskom-gensym)) '("--")))
              ((eq 'menu type)
               (let* ((name (lyskom-menu-encode
			     (lyskom-get-menu-string symbol) 
			     lyskom-define-menu-gnu-title-encoding-type))
                      (submap (make-sparse-keymap name))
		      (lyskom-define-menu-gnu-title-encoding-type 'item))
                 (define-key map (vector symbol)
                   (cons name submap))
                 (lyskom-define-menu-gnu submap
                                         (car (cdr (cdr (car menus))))
                                         specials)))
              ((eq 'item type)
               (let ((shortcut nil))
                 (when (assq symbol specials)
                   (unless (lyskom-traverse key (cdr (assq symbol specials))
                             (unless (condition-case nil
                                         (eq (lookup-key (current-local-map) 
                                                         key) symbol)
                                       (error nil))
                               (lyskom-traverse-break t)))
                       (setq shortcut
                             (mapconcat 
                              (lambda (key)
                                (if (assq key lyskom-swedish-bindings-reverse)
                                    (symbol-name 
                                     (cdr (assq key lyskom-swedish-bindings-reverse)))
                                  (single-key-description key)))
                              (car (cdr (assq symbol specials)))
                              " "))))
                 (if shortcut
                     (define-key map (vector symbol)
                              `(menu-item ,(lyskom-menu-encode
					    (lyskom-get-menu-string symbol)
					    'item) ,symbol
                                          :keys ,(lyskom-menu-encode shortcut 'keys)))
                   (define-key map (vector symbol)
                     (cons (lyskom-menu-encode (lyskom-get-menu-string symbol) 'item) symbol)))))
              (t (error "Menu description invalid in lyskom-define-menu")))))))


(defun lyskom-get-menu-category (menu-category)
  (symbol-value (cdr (assq menu-category lyskom-menu-list))))

(defun lyskom-update-menus ()
  (lyskom-build-menus)
  (when (and (boundp 'lyskom-current-menu-category)
             lyskom-current-menu-category)
    (mapcar (lambda (mc)
	      (lyskom-set-menus mc (current-local-map)))
            lyskom-current-menu-category)))

(defun lyskom-set-menus (menu-category  keymap)
  (lyskom-xemacs-or-gnu (lyskom-set-menus-xemacs menu-category)
                        (lyskom-set-menus-gnu menu-category keymap)))

(defun lyskom-set-menus-gnu (menu-category keymap)
  "Update the menus"
  (define-key keymap [menu-bar] (lyskom-get-menu-category menu-category))
  (make-local-variable 'lyskom-current-menu-category)
  (if (not (boundp 'lyskom-current-menu-category))
      (setq lyskom-current-menu-category (list menu-category))
    (add-to-list 'lyskom-current-menu-category menu-category)))

(eval-when-compile (defvar default-menubar nil))

(defun lyskom-set-menus-xemacs (menu-category)
  "Update the menus"
  (make-local-variable 'current-menubar)
  (make-local-variable 'lyskom-current-menu-category)
  (set-buffer-menubar default-menubar)
  (mapc (function
           (lambda (menu)
             (add-submenu nil menu)))
          (lyskom-get-menu-category menu-category))
  (setq lyskom-current-menu-category (list menu-category)))

;;;
;;; This function would have been completely unnecessary if Gnu Emacs
;;; didn't carry around an ancient version of popup-menu that is
;;; completely incompatible with XEmacs version of the same function. 
;;; Sometimes I hate elisp.
;;;

(defun lyskom-do-popup-menu (menu event)
  "Pop up a menu"
  (lyskom-xemacs-or-gnu 
   (popup-menu menu event)
   (let* ((result (x-popup-menu (or event t) menu)))
     (cond ((null result))
           ((listp result)
            (apply (car result)
                   (cdr result)))
           ((commandp result)
            (call-interactively result))
           ((functionp result)
            (funcall result))
           (t nil)))))


(defun lyskom-background-menu (pos event)
  "Pop up a menu with LysKOM commands and execute the selected command."
  (let* ((menu lyskom-popup-menu)
	 (result (lyskom-do-popup-menu menu event)))))




;;;;(Återse) Baklänges		   Addera extra kopiemottagare
;;;;Addera kommentar		   Addera medlem
;;;;Addera mottagare		   Automatsvar
;;;;Avmarkera (inlägg)		   Bli medlem i möte
;;;;Börja med nytt namn		   Endast läsa senaste
;;;;Filtrera författare		   Filtrera innehåll
;;;;Filtrera ärende			   Fjärrkontrollera automatsvar
;;;;Fjärrkontrollera avsluta	   Fjärrkontrollera lista meddelanden
;;;;Fjärrkontrollera radera meddelanden
;;;;Fjärrkontrollera ändra svarsmeddelande
;;;;Flytta inlägg			   Fotnot till inlägg
;;;;Få skäll			   Få uppmuntran
;;;;Gå till möte			   Gå till nästa möte
;;;;Hjälp				   Hoppa över alla kommentarer
;;;;Kasta ut en session		   Kommentera föregående inlägg
;;;;Kommentera inlägget		   Ladda ner fil
;;;;Ladda upp fil			   Lista (med) regexpar
;;;;Lista filarean			   Lista filter
;;;;Lista klienter			   Lista meddelanden
;;;;Lista medlemsskap		   Lista möten
;;;;Lista nyheter			   Lista personer
;;;;Lista ärenden			   Läsa nästa inlägg
;;;;Långsamma kommandon		   Markera (inlägg)
;;;;Personligt svar			   Personligt svar på föregående inlägg
;;;;Prioritera möten		   Radera inlägg
;;;;Radera meddelanden		   Se tiden
;;;;Skapa möte			   Skicka brev
;;;;Skriva ett inlägg		   Sluta
;;;;Snabba kommandon		   Spara text (på fil)
;;;;Spara variabler			   Status (för) möte
;;;;Status (för) person		   Status (för) session
;;;;Subtrahera kommentar		   Subtrahera mottagare
;;;;Superhoppa			   Sända meddelande
;;;;Sätt lapp på dörren		   Sätt läsnivå
;;;;Ta bort lapp på dörren		   Uppskjuta läsning
;;;;Uteslut medlem			   Utplåna
;;;;Utträda ur möte			   Var (är) jag
;;;;Vilka är inloggade		   Visa user-arean
;;;;Vänta på ett inlägg		   Ändra filter
;;;;Ändra livslängd			   Ändra lösenord
;;;;Ändra mötestyp			   Ändra namn
;;;;Ändra organisatör		   Ändra presentation
;;;;Ändra supermöte			   Ändra svarsmeddelande
;;;;Ändra tillåtna författare	   Ändra variabler
;;;;Återse alla			   Återse alla kommentarer
;;;;Återse alla kommentarer rekursivt  Återse alla markerade
;;;;Återse det föregående kommenterade
;;;;Återse det kommenterade		   Återse första
;;;;Återse hoppa			   Återse igen
;;;;Återse inlägg			   Återse lista
;;;;Återse markerade		   Återse nästa
;;;;Återse omodifierat		   Återse presentation
;;;;Återse senaste			   Återse träd
;;;;Återse urinlägget		   Återstarta kom
;;;;Övergå till administratörsmod

(eval-and-compile (provide 'lyskom-menus))

;;; menus.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: slow.el
;;;;
;;;; This file contains the code that makes it possible to run a 
;;;; long-commands mode in the lyskom-buffer.
;;;;
;;;; ================================================================
;;;;

;;; Author: Linus Tolke
;;; Modified by: David Kågedal

(defvar lyskom-slow-mode-map
  (make-sparse-keymap)
  "Mode map for the `slow' lyskom command mode.")

(define-key lyskom-slow-mode-map (kbd "RET") 'kom-parse-command-and-execute)
(define-key lyskom-slow-mode-map (kbd "SPC") 'kom-expand-slow-or-next-command)
(define-key lyskom-slow-mode-map (kbd "C-a") 'kom-slow-start-of-line)
(define-key lyskom-slow-mode-map (kbd "?")   'kom-slow-list-completions)
(define-key lyskom-slow-mode-map (kbd "*")  'kom-slow-button-press)
(define-key lyskom-slow-mode-map (kbd "=")  'kom-slow-menu-button-press)
(define-key lyskom-slow-mode-map (kbd "TAB") 'kom-expand-slow-command-or-next-link)
(define-key lyskom-slow-mode-map (kbd "M-TAB") 'kom-previous-link)
(define-key lyskom-slow-mode-map (kbd "C-i")   'kom-expand-slow-command-or-next-link)
(define-key lyskom-slow-mode-map (kbd "M-C-i") 'kom-previous-link)
(define-key lyskom-slow-mode-map (kbd (lyskom-keys 'button2up)) 'kom-slow-click-or-yank)
(define-key lyskom-slow-mode-map (kbd (lyskom-keys 'button3up)) 'kom-popup-menu)



(defun lyskom-slow-start-of-line ()
  "Move point to start of command, after LysKOM prompt."
  (goto-char (point-max))
  (save-restriction
    (when (> lyskom-last-viewed (point-max))
      (setq lyskom-last-viewed (point-max-marker)))
    (narrow-to-region lyskom-last-viewed (point-max))
    (if (search-backward lyskom-current-prompt-text nil t)
        (forward-char (length lyskom-current-prompt-text))
      (goto-char (point-max))
      (beginning-of-line))
    (when (looking-at "\\(\\s-+\\)")
      (goto-char (match-end 0)))))

(defun lyskom-slow-start-of-line-pos ()
  "Return the starting position for entry text on the current line."
  (save-excursion (lyskom-slow-start-of-line) (point)))


(defun lyskom-get-entered-slow-command ()
  "Get the text that the user has entered after the last prompt.
Note that this function leaves point at the end of the prompt.

If no text is entered, nil is returned."
  (lyskom-slow-start-of-line)
  (if (= (point) (point-max))
      nil
    (buffer-substring (point) (point-max))))

(defun lyskom-slow-on-prompt-line (&optional where)
  "Return non-nil if point is on the line containing the prompt.
Currently the prompt is assumed to be on the last line of the buffer."
  (eq (save-excursion (when where (goto-char where)) (beginning-of-line) (point))
      (save-excursion (goto-char (point-max)) (beginning-of-line) (point))))

(defun kom-slow-click-or-yank (event)
  "Click on a LysKOM button or do the default action if after the prompt."
  (interactive "@e")
  (let ((pos (lyskom-event-closest-point event)))
    (if (and (lyskom-slow-on-prompt-line pos)
             (<= (lyskom-slow-start-of-line-pos) pos))
        (let ((fn (lookup-key global-map (this-command-keys))))
          (when (commandp fn) (call-interactively fn)))
      (kom-button-click event))))

(defun kom-slow-button-press ()
  "Run kom-button-press unless on the prompt line."
  (interactive)
  (if (lyskom-slow-on-prompt-line)
      (call-interactively 'self-insert-command)
    (kom-button-press)))

(defun kom-slow-menu-button-press ()
  "Run kom-menu-button-press unless on the prompt line."
  (interactive)
  (if (lyskom-slow-on-prompt-line)
      (call-interactively 'self-insert-command)
    (kom-menu-button-press)))

(defun kom-slow-start-of-line ()
  "Move to the beginning of line or start of command if on the prompt line."
  (interactive)
  (if (lyskom-slow-on-prompt-line)
      (lyskom-slow-start-of-line)
    (beginning-of-line)))

(defun lyskom-expand-slow-command (try-exact eager-completion)
  (let* ((saved-point (point-marker))
         (text (lyskom-get-entered-slow-command))
         (exact (and text try-exact (lyskom-complete-command text nil 'lambda)))
         (completes (and text (all-completions text 'lyskom-complete-command)))
	 (command nil))
    (cond
     ((null text)
      (lyskom-beep t)
      nil)
     ((null completes)
      (goto-char saved-point)
      (lyskom-insert-before-prompt (lyskom-get-string 'no-such-command))
      (lyskom-beep t)
      nil)
     (exact
      (setq command (cons (lyskom-lookup-command-by-name (elt exact 0)) (point))))
     ((= (length completes) 1)
      (setq command (cons (lyskom-lookup-command-by-name (car completes)) (point)))
      (delete-region (point) (point-max))
      (insert (car completes)))
     ((> (length completes) 1)
      (let ((longest (try-completion text 'lyskom-complete-command)))
	(cond
	 ((eq longest 't)
	  (delete-region (point) (point-max))
	  (insert (car completes)))
	 ((stringp longest)
	  (delete-region (point) (point-max))
	  (insert longest)

          ;; If the completion is the same as the entered string
          ;; then we either add a space if there was none at the
          ;; end or list all possible completions *and* add a 
          ;; space (since it has been killed).

          (let* ((have-space (string-match "\\s-+$" text))
                 (text (if have-space (substring text 0 (match-beginning 0)) text))
                 (longest (if (string-match "\\s-+$" longest)
                              (substring longest 0 (match-beginning 0))
                            longest)))
	  (when (lyskom-string= (lyskom-unicase longest) (lyskom-unicase text))
            (if (or have-space eager-completion)
		(lyskom-slow-list-completions completes))
            (unless (or eager-completion (eq ?\  (lyskom-char-before (point))) (insert " "))))
          ))
	 (t (signal 'lyskom-internal-error '()))))))
    command))

(defun kom-expand-slow-command-or-next-link ()
  "If on the prompt line, run kom-expand-slow-command.
Otherwise run kom-next-link."
  (interactive)
  (if (lyskom-slow-on-prompt-line)
      (call-interactively 'kom-expand-slow-command)
    (call-interactively 'kom-next-link)))

(defun kom-expand-slow-command (&optional try-exact)
  "Tries to complete the command at point.
If optional TRY-EXACT is non-nil, look for an exact match.

If the completion was exact return a pair `(COMMAND . POINT)' where
COMMAND is the command and POINT is the point where the command text
starts.

If the completion was not exact it returns nil."
  (interactive)
  (lyskom-expand-slow-command try-exact t))

(defun kom-expand-slow-or-next-command (&optional try-exact)
  "If any part of a slow command has been entered, call
`kom-expand-slow-command'. Otherwise, do `kom-next-command'."
  (interactive)
  (if (save-excursion (lyskom-get-entered-slow-command))
      (lyskom-expand-slow-command try-exact nil)
    (buffer-disable-undo)
    (kom-next-command)))

(defun kom-parse-command-and-execute ()
  "Reads a command from the last line in the buffer and executes it."
  (interactive)
  (let* ((text (lyskom-get-entered-slow-command))
	 (command (and text (lyskom-expand-slow-command t nil))))
    (buffer-disable-undo)
    (cond
     ((null text)
      (call-interactively 'kom-next-command))
     (command
      (delete-region (cdr command) (point-max))
      (call-interactively (car command))))))

(defun lyskom-slow-list-completions (completes)
  "List strings in COMPLETES as possible completions for a command."
  (cond (completes (lyskom-format-insert-before-prompt
		    'command-completions
		    (mapconcat 'identity (sort completes 'string-lessp) "\n ")))
	(t (lyskom-insert-before-prompt (lyskom-get-string 'no-such-command)))))  

(defun kom-slow-list-completions ()
  (interactive)
  (save-excursion
    (lyskom-slow-list-completions 
     (all-completions (or (lyskom-get-entered-slow-command) "")
		      'lyskom-complete-command))))


(def-kom-command kom-slow-mode ()
  "Starts the slow-command-mode. In this mode you write commands in the
main LysKOM buffer instead of using keyboard shortcuts."
  (interactive)
  (unless lyskom-slow-mode
    (setq lyskom-saved-read-only buffer-read-only)
    (setq lyskom-slow-mode t)
    (setq buffer-read-only nil)
    (use-local-map lyskom-slow-mode-map)))

(def-kom-command kom-quick-mode ()
  "Starts the quick command mode. This is the standard mode for LysKOM,
where you use keyboard shortcuts for most commands, and the \"x\" prefix
for all other commands."
  (interactive)
  (when lyskom-slow-mode
    (setq buffer-read-only lyskom-saved-read-only)
    (setq lyskom-slow-mode nil)
    (use-local-map lyskom-mode-map)))


;;;;; -*-coding: iso-8859-1;-*-
;;;; This file contains some miscellaneous string functions

;; Copyright (C) 1991-1995 Free Software Foundation

;; Author: Sebastian Kremer <sk@thp.Uni-Koeln.DE>
;;	Per Cederqvist <ceder@lysator.liu.se>
;;	Inge Wallin <inge@lysator.liu.se>
;; Maintainer: elib-maintainers@lysator.liu.se
;; Created: before 9 May 1991
;; Keywords: extensions, lisp

;;;; This file is part of the GNU Emacs lisp library, Elib.
;;;;
;;;; GNU Elib is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;;
;;;; GNU Elib is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with GNU Elib; see the file COPYING.  If not, write to
;;;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;;;; Boston, MA 02111-1307, USA
;;;; 
;;;; Author: Sebastian Kremer
;;;;         sk@thp.Uni-Koeln.DE
;;;; 


;;; Commentary:

;;;
;;; This file is part of the elisp library Elib.
;;; It implements simple generic string functions for use in other 
;;; elisp code: replace regexps in strings, split strings on regexps.
;;; 


;;; NOTE NOTE NOTE NOTE NOTE
;;;
;;; This package has been slightly modified in the following section:


(defvar elib-newtext)
(defvar elib-string)

;;; Code:

(provide 'string)


;; This function is a near-equivalent of the elisp function replace-match
;; which work on strings instead of a buffer.  The FIXEDCASE parameter
;; of replace-match is not implemented.

(defun string-replace-match (regexp elib-string elib-newtext &optional literal global)
  "Replace first match of REGEXP in ELIB-STRING with ELIB-NEWTEXT.
If no match is found, nil is returned instead of the new string.

Optional arg LITERAL non-nil means to take ELIB-NEWTEXT literally. If LITERAL is 
nil, character `\\' is the start of one of the following sequences:
  \\\\   will be replaced by a single \\.
  \\&   will be replaced by the text which matched the regexp.
  \\N   where N is a number and 1 <= N <= 9, will be replaced
       by the Nth subexpression in REGEXP. Subexpressions are grouped
       inside \\( \\).

Optional arg GLOBAL means to replace all matches instead of only the first."

  (let ((data (match-data)))
    (unwind-protect

	(if global
	    (let ((result "") 
		  (start 0)
		  matchbeginning
		  matchend)
	      (while (string-match regexp elib-string start)
		(setq matchbeginning (match-beginning 0)
		      matchend (match-end 0)
		      result (concat result
				     (substring elib-string start matchbeginning)
				     (if literal
					 elib-newtext
				       (elib-string-expand-newtext)))
		      start matchend))

	      (if matchbeginning	; matched at least once
		  (concat result (substring elib-string start))
		nil))

	  ;; not GLOBAL
	  (if (not (string-match regexp elib-string 0))
	      nil
	    (concat (substring elib-string 0 (match-beginning 0))
		    (if literal elib-newtext (elib-string-expand-newtext))
		    (substring elib-string (match-end 0)))))
      (store-match-data data))))


(defun elib-string-expand-newtext ()
  ;; Expand \& and \1..\9 (referring to ELIB-STRING) in ELIB-NEWTEXT.
  ;; Uses match data and fluid vars `elib-newtext', `elib-string'.
  ;; Note that in Emacs 18 match data are clipped to current buffer
  ;; size...so the buffer should better not be smaller than ELIB-STRING.
  (let ((pos 0)
	(len (length elib-newtext))
	(expanded-newtext ""))
    (while (< pos len)
      (setq expanded-newtext
	    (concat expanded-newtext
		    (let ((c (aref elib-newtext pos)))
		      (if (= ?\\ c)
			  (cond ((= ?\& (setq c (aref elib-newtext
						      (setq pos (1+ pos)))))
				 (substring elib-string
					    (match-beginning 0)
					    (match-end 0)))
				((and (>= c ?1) 
				      (<= c ?9))
				 ;; return empty string if N'th
				 ;; sub-regexp did not match:
				 (let ((n (- c ?0)))
				   (if (match-beginning n)
				       (substring elib-string
						  (match-beginning n)
						  (match-end n))
				     "")))
				(t (char-to-string c)))
			(char-to-string c)))))
      (setq pos (1+ pos)))
    expanded-newtext))


(defun string-split (pattern string &optional limit)
  "Splitting on regexp PATTERN, turn string STRING into a list of substrings.
Optional third arg LIMIT (>= 1) is a limit to the length of the
resulting list."

  (let ((data (match-data)))
    (unwind-protect
	(let* ((start (string-match pattern string))
	       (result (list (substring string 0 start)))
	       (count 1)
	       (end (if start (match-end 0))))
	  (if end			; else nothing left
	      (while (and (or (not (integerp limit))
			      (< count limit))
			  (string-match pattern string end))
		(setq start (match-beginning 0)
		      count (1+ count)
		      result (cons (substring string end start) result)
		      end (match-end 0)
		      start end)))
	  (if (and (or (not (integerp limit))
		       (< count limit))
		   end)			; else nothing left
	      (setq result
		    (cons (substring string end) result)))
	  (nreverse result))
      (store-match-data data))))

;;; string.el ends here
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: option-edit.el
;;;;
;;;; Customization for LysKOM
;;;;


(lyskom-external-function widget-default-format-handler)
(lyskom-external-function popup-mode-menu)

;;; ======================================================================
;;; Require Per Abrahamsens widget package, version 0.991 or later.
;;;

;;; ======================================================================
;;; Global variables

(defvar lyskom-widgets nil
  "List of widgets in the customize buffer.")

;(setq lyskom-customize-buffer-format '([kom-format-html-authors]))

(defvar lyskom-customize-buffer-format
  '("\n"
    (lyskom bold centered)
    "\n\n"
    lyskom-doc
    "\n\n"
    [kom-customize-format]
    "\n\n"
    section
    (look-and-feel-misc bold centered)
    section
    "\n"
    [kom-default-language]
    [kom-show-namedays]
    [kom-show-week-number]
    [kom-print-relative-dates]
    [kom-show-mx-date-as-local]
    [kom-print-seconds-in-time-strings]
    "\n"
    [kom-extended-status-information]
    [kom-auto-list-faqs]
    [kom-auto-review-faqs]
    "\n"
    [kom-auto-confirm-new-conferences]
    "\n"
    [kom-idle-hide]
    [kom-relogin-behaviour]
    [kom-relogin-inhibit-commands]
    [kom-remember-password]
    [kom-show-where-and-what]
    [kom-show-since-and-when]
    "\n"
    [kom-highlight-conferences]
    [kom-friends]
    [kom-morons]
    [kom-person-list-1]
    [kom-person-list-2]
    [kom-person-list-3]
    [kom-person-list-4]
    "\n"
    [kom-show-unread-in-frame-title]
    [kom-unread-mode-line-type]
    [kom-presence-messages-in-echo-area]
    [kom-presence-messages-in-buffer]
    "\n"
    [kom-text-no-prompts]
    [kom-keyboard-menu-immediate-selection]
    [kom-page-before-command]
    [kom-deferred-printing]
    [kom-max-buffer-size]
    [kom-trim-buffer-minimum]
    [kom-bury-buffers]
    [kom-keep-alive-interval]
    [kom-lost-session-notification]
    [kom-show-sync-messages]
    "\n"
    [kom-agree-text]
    [kom-mercial]
    "\n"
    [kom-user-prompt-format]
    [kom-user-prompt-format-executing]
    [kom-anonymous-prompt-format]
    [kom-anonymous-prompt-format-executing]
    [kom-enabled-prompt-format]
    [kom-enabled-prompt-format-executing]
    "\n\n"
    section
    (window-locations bold centered)
    section
    "\n"
    (windows-where bold centered)
    "\n"
    windows-doc
    "\n\n"
    [kom-customize-in-window]
    [kom-edit-filters-in-window]
    [kom-prioritize-in-window]
    [kom-list-membership-in-window]
    [kom-write-texts-in-window]
    [kom-view-commented-in-window]
    [kom-personal-messages-in-window]
    
    "\n\n"
    section
    (predefined-fonts bold centered)
    section
    "\n"
    [kom-active-face]
    [kom-url-face]
    [kom-me-face]
    [kom-highlight-face]
    [kom-subject-face]
    [kom-text-no-face]
    [kom-friends-face]
    [kom-morons-face]
    [kom-presence-face]
    [kom-first-line-face]
    [kom-warning-face]
    [kom-mark-face]
    [kom-dim-face]
    [kom-text-body-face]
    [kom-dashed-lines-face]
    [kom-async-text-body-face]
    [kom-async-dashed-lines-face]
    "\n\n"
    section
    (other-fonts bold centered)
    section
    "\n"
    [kom-active-highlight-face]
    [kom-active-strikethrough-face]
    "\n\n"
    section
    (audio-cues bold centered)
    section
    "\n"
    [kom-audio-player]
    "\n"
    (audio-cues-when bold centered)
    "\n"
    audio-cues-doc
    "\n\n"
    [kom-ding-on-priority-break]
    [kom-ding-on-new-letter]
    [kom-ding-on-wait-done]
    [kom-ding-on-no-subject]
    [kom-ding-on-common-messages]
    [kom-ding-on-group-messages]
    [kom-ding-on-personal-messages]
    "\n"
    [kom-silent-ansaphone]
    "\n\n"
    section
    (reading bold centered)
    section
    "\n"
    [kom-default-session-priority]
    [kom-print-number-of-unread-on-entrance]
    [kom-allow-incompleteness]
    [kom-permissive-completion]
    [kom-complete-numbers-before-names]
    [kom-continuous-scrolling]
    [kom-inhibit-typeahead]
    [kom-confirm-add-recipients]
    "\n"
    [kom-higher-priority-breaks]
    [kom-review-priority]
    [kom-server-priority]
    [kom-server-priority-breaks]
    [kom-session-nickname]
    "\n"
    [kom-read-depth-first]
    [kom-read-related-first]
    [kom-reading-puts-comments-in-pointers-last]
    [kom-show-footnotes-immediately]
    [kom-follow-comments-outside-membership]
    [kom-follow-attachments]
    [kom-mark-read-texts-as-read-in-new-recipient]
    [kom-created-texts-are-read]
    [kom-created-texts-are-saved]
    [kom-review-uses-cache]
    [kom-review-marks-texts-as-read]
    "\n"
    [kom-saved-file-name]
    [kom-default-mark]
    [kom-symbolic-marks-alist]
    [kom-membership-default-message-flag]
    [kom-membership-default-priority]
    [kom-membership-default-placement]
    [kom-unsubscribe-makes-passive]
    [kom-postpone-default]
    "\n"
    [kom-autowrap]
    [kom-autowrap-timeout]
    [kom-show-author-at-end]
    [kom-show-creating-software]
    [kom-show-imported-importer]
    [kom-show-imported-external-recipients]
    [kom-show-imported-envelope-sender]
    [kom-show-imported-message-id]
    [kom-text-properties]
    [kom-fontify-text]
    [kom-smileys]
    [kom-w3-simplify-body]
    [kom-format-html-authors]
    [kom-format-show-images]
    "\n"
    [kom-max-overlays]
    [kom-highlight-first-line]
    [kom-highlight-dashed-lines]
    [kom-highlight-text-body]
    [kom-async-highlight-dashed-lines]
    [kom-async-highlight-text-body]
    "\n"
    [kom-dashed-lines]
    [kom-long-lines]
    [kom-text-footer-format]
    [kom-text-header-dash-length]
    [kom-text-footer-dash-length]
    "\n"
    [kom-truncate-threshold]
    [kom-truncate-show-lines]
    "\n"
    [kom-ignore-message-senders]
    [kom-ignore-message-recipients]
    [kom-show-personal-message-date]
    [kom-filter-outgoing-messages]
    "\n\n"
    section
    (writing bold centered)
    section
    "\n"
    [kom-edit-hide-add-button]
    [kom-cite-string]
    [kom-ispell-dictionary]
    "\n"
    sending-doc
    "\n\n"
    [kom-confirm-multiple-recipients]
    [kom-check-commented-author-membership]
    [kom-dont-check-commented-authors]
    [kom-check-for-new-comments]

    "\n\n"
    section
    (urls bold centered)
    section
    "\n"
    [kom-text-links]
    [kom-url-transformation-rules]
    [kom-url-viewer-preferences]
    [kom-windows-browser-command]
    [kom-mosaic-command]
    [kom-netscape-command]
    [kom-netscape-variant]
    [kom-galeon-command]
    [kom-lynx-terminal]
    [kom-lynx-xterm-command]
    [kom-lynx-terminal-command]


    "\n\n"
    section
    (personal-messages bold centered)
    section
    "\n"
    [kom-show-personal-messages-in-buffer]
    [kom-pop-personal-messages]
    [kom-default-message-recipient]
    "\n"
    (automatic-replies bold centered)
    "\n"
    [kom-ansaphone-record-messages]
    [kom-ansaphone-show-messages]
    "\n"
    [kom-ansaphone-default-reply]
    "\n"
    [kom-ansaphone-replies]
    "\n\n"
    section
    (remote-control bold centered)
    section
    "\n"
    [kom-remote-control]
    [kom-self-control]
    "\n"
    [kom-remote-controllers]
    "\n\n"
    section
    (hooks bold centered)
    section
    "\n"
    [kom-login-hook]
    [kom-do-when-done]
    ))

;;; ======================================================================
;;;     User functions
;;;

(defun lyskom-customize-apply ()
  "Set the variables in the LysKOM buffer according to their values in the
customize buffer but do not save them to the server."
  (interactive)
  (let ((tmp lyskom-widgets))
    (save-current-buffer
      (set-buffer lyskom-buffer)
      (when (listp kom-dont-read-saved-variables)
        (setq kom-dont-read-saved-variables nil))
      (while tmp
        (set (car (car tmp))
             (widget-value (cdr (car tmp))))
        (when (not (widget-value (widget-get (cdr (car tmp)) 
                                             ':lyskom-storage-widget)))
          (set-default (car (car tmp)) (widget-value (cdr (car tmp))))
          (when (and (listp kom-dont-read-saved-variables)
                     (or (memq (car (car tmp)) lyskom-elisp-variables)
                         (lyskom-flag-global-variable-from-elisp (car (car tmp)))))
            (setq kom-dont-read-saved-variables 
                  (cons (car (car tmp)) kom-dont-read-saved-variables))))
        (setq tmp (cdr tmp))))))

(eval-when-compile (defvar save-options-init-file nil))

(defun lyskom-customize-send ()
  "Save variables to the server"
  (save-current-buffer
    (set-buffer lyskom-buffer)
    (lyskom-save-options (current-buffer)
                         (lyskom-get-string 'saving-settings)
                         (lyskom-get-string 'saving-settings-done)
                         (lyskom-get-string 'could-not-save-options))
    (let ((var-list nil))
      (mapc
       (lambda (e)
         (when (and (vectorp e)
                    (symbolp (elt e 0))
                    (or 
                     (and (not (memq (elt e 0) lyskom-elisp-variables))
                          (not (lyskom-flag-global-variable-from-elisp (elt e 0))))
                     (memq (elt e 0) kom-dont-read-saved-variables))
                    (boundp (elt e 0)))
           (setq var-list (cons (cons (elt e 0)
                                      (symbol-value (elt e 0)))
                                var-list))))
       lyskom-customize-buffer-format)

      (let* ((actual-save-options-init-file
              (or (and (boundp 'save-options-init-file)
                       save-options-init-file)
                  (and (not (equal user-init-file ""))
                       user-init-file)
                  (and (eq system-type 'ms-dos)
                       (concat "~" (user-login-name) "/_emacs"))
                  (concat "~" (user-login-name) "/.emacs")))
             (init-output-buffer (find-file-noselect
                                  actual-save-options-init-file))
             (init-output-marker nil))

        (save-current-buffer
          (set-buffer init-output-buffer)
          ;;
          ;; Find and delete the previously saved data, and position to write.
          ;;
          (goto-char (point-min))
          (if (re-search-forward "^;;; LysKOM Settings *\n" nil 'move)
              (let ((p (match-beginning 0)))
                (goto-char p)
                (or (re-search-forward
                     "^;;; End of LysKOM Settings *\\(\n\\|\\'\\)"
                     nil t)
                    (error "can't find END of saved state in .emacs"))
                (delete-region p (match-end 0)))
            (goto-char (point-max))
            (insert "\n"))
          (setq init-output-marker (point-marker)))

    (let ((standard-output init-output-marker))
      (princ ";;; LysKOM Settings\n")
      (princ ";;; =====================\n")
      (mapc (lambda (x)
                (princ (format "(setq-default %S %s%S)\n" 
                               (car x)
                               (cond ((eq (cdr x) t) "")
                                     ((null (cdr x)) "")
                                     ((symbolp (cdr x)) "'")
                                     ((listp (cdr x)) "'")
                                     (t ""))
                               (cdr x))))
       var-list)
      (princ ";;; ============================\n")
      (princ ";;; End of LysKOM Settings\n"))

    (set-marker init-output-marker nil)
    (save-current-buffer
      (set-buffer init-output-buffer)
      (save-buffer))
    ))))

(defun lyskom-customize-save ()
  "Apply changes and save them to the server."
  (interactive)
  (lyskom-customize-apply)
  (lyskom-customize-send))

(defun lyskom-customize-quit ()
  "Quit the customize buffer without saving"
  (interactive)
  (let ((buf (current-buffer)))
    (lyskom-undisplay-buffer buf)
    (kill-buffer buf)))

(defun lyskom-customize-save-and-quit ()
  "Save and quit the customize buffer"
  (interactive)
  (lyskom-customize-apply)
  (lyskom-customize-send)
  (lyskom-customize-quit))

(defun lyskom-customize-help ()
  (interactive)
  (message (lyskom-get-string 'customize-help)))

(defun lyskom-widget-click (event)
  (interactive "e")
  (let ((pos (lyskom-event-point event)))
    (if (and pos (widget-at pos))
        (widget-button-click event)
      (popup-mode-menu))))

(defun lyskom-customize-mode ()
  "Major mode for editing LysKOM settings.
See the top of the buffer for localized help.

These commands work outside editable text fields.
\\[lyskom-customize-save-and-quit] to save and quit,
\\[lyskom-customize-apply] to apply without saving or quitting,
\\[lyskom-customize-save] to save without quitting,
\\[lyskom-customize-quit] to quit without saving.
\\[widget-forward] moves to the next setting
\\[widget-button-press] changes the value
Press C-h m for a complete list of commands.

Documentation:  [?] Show documentation    [!] Hide documentation
Lists etc.   :  [INS] Add a line   [DEL] Remove a line   [*] Modify

If the box before the name of the setting is selected, the setting will
be saved in the server. Otherwise it will be saved in your .emacs.

Click prompts with the middle mouse button to change values and bring 
up menus.

All key bindings:
\\{lyskom-customize-map}
"
  (kill-all-local-variables)
  (use-local-map lyskom-customize-map)
  (make-local-variable 'lyskom-widgets)
  (setq lyskom-widgets nil)
  (let ((inhibit-read-only t))
    (erase-buffer))
  (condition-case nil
      (lyskom-copy-face kom-active-face 'widget-button-face)
    (error nil))
  (mapc 'lyskom-custom-insert lyskom-customize-buffer-format)
  (widget-setup)
  (mapc (function
           (lambda (variable)
             (widget-value-set (cdr variable)
                               (save-current-buffer
                                 (set-buffer lyskom-buffer)
                                 (symbol-value (car variable))))))
          lyskom-widgets)
  (widget-setup)
  (setq mode-name "LysKOM Settings")
  (setq major-mode 'lyskom-customize-mode)
  )

(defun kom-customize ()
  "Open the customize buffer"
  (interactive)
  (let ((buf (lyskom-get-buffer-create 'customize
                                       (lyskom-format
                                        (lyskom-custom-string 'buffer-name)
                                        lyskom-server-name)
                                       t)))
    (unwind-protect
        (progn
          (lyskom-start-of-command 'kom-customize)
          (sit-for 0)
          (save-current-buffer
            (set-buffer buf)
            (lyskom-customize-mode))
           (lyskom-display-buffer buf))
      (save-current-buffer
        (set-buffer lyskom-buffer)
        (lyskom-end-of-command)))
    (goto-char (point-min))))


;;; ============================================================
;;; Non-user functions
;;;

(defun lyskom-custom-insert (w)
  (cond ((symbolp w)
         (widget-insert (substitute-command-keys (lyskom-custom-string w))))
        ((listp w)
         (let ((start (point))
               (end nil)
               (inhibit-read-only t))
           (widget-insert (lyskom-custom-string (car w)))
           (setq end (point))
           (mapc (lambda (fn)
                   (funcall (intern (concat "lyskom-custom-insert-"
                                            (symbol-name fn)))
                            start end))
                   (cdr w))))
        ((stringp w)
         (widget-insert w))
        ((vectorp w)
         (setq lyskom-widgets (cons (cons (aref w 0)
                                          (lyskom-create-widget (aref w 0)))
                                    lyskom-widgets)))))

;;UNUSED: lyskom-custom-insert-bold
(defun lyskom-custom-insert-bold (s e)
  (add-text-properties s e (list 'face 'bold 'end-closed nil)))

;;UNUSED: lyskom-custom-insert-centered
(defun lyskom-custom-insert-centered (s e)
  (save-excursion (goto-char s)
                  (center-line 1)))


(defun lyskom-custom-get-value (var)
  (save-current-buffer
    (set-buffer lyskom-buffer)
    (symbol-value var)))

(defun lyskom-custom-string (s)
  (lyskom-get-string s 'lyskom-custom-strings))

(defvar lyskom-custom-variables-missing '(kom-permanent-filter-list
                                          kom-quit-hook 
                                          lyskom-last-known-conf-no
                                          lyskom-last-known-pers-no
                                          kom-presence-messages
                                          kom-dont-complain-about-missing-presentation
					  kom-respect-ancient-content-types
					  kom-read-last-text-hook-conf-list
					  kom-list-summary-hook-conf-list
                                          lyskom-settings-version)
  "List of user variables not in lyskom-custom-variables.")

(defvar lyskom-custom-variables
  '(
    (kom-max-overlays (choice ((number nil
                                       :tag limited-number
                                       :format "%[%t%] (%v)")
                               (const (unlimited-number nil)))))
    (kom-highlight-first-line (toggle (on off)))
    (kom-highlight-dashed-lines (toggle (on off)))
    (kom-highlight-text-body (toggle (on off)))
    (kom-async-highlight-dashed-lines (toggle (on off)))
    (kom-async-highlight-text-body (toggle (on off)))
    (kom-personal-messages-in-window (open-window))
    (kom-write-texts-in-window (open-window))
    (kom-list-membership-in-window (open-window))
    (kom-edit-filters-in-window (open-window))
    (kom-prioritize-in-window (open-window))
    (kom-customize-in-window (open-window))
    (kom-view-commented-in-window (open-window))
    (kom-edit-filters-in-window (open-window))
    (kom-list-membership-in-window (open-window))
    (kom-customize-format (choice ((const (long-format long))
                                   (const (short-format short)))))
    (kom-default-language (repeat (language-choice nil)))
    (kom-user-prompt-format (string))
    (kom-user-prompt-format-executing (string))
    (kom-anonymous-prompt-format (string))
    (kom-anonymous-prompt-format-executing (string))
    (kom-enabled-prompt-format (string))
    (kom-enabled-prompt-format-executing (string))
    (kom-cite-string (string))
    (kom-created-texts-are-read (toggle (yes no)))
    (kom-mark-read-texts-as-read-in-new-recipient (toggle (yes no)))
    (kom-review-uses-cache (toggle (yes no)))
    (kom-review-marks-texts-as-read (toggle (yes no)))
    (kom-created-texts-are-saved (choice ((const (no nil))
                                          (file nil :tag to-file))))
    (kom-default-mark (choice ((number (0 255) 
                                       :tag selected-mark
                                       :format "%[%t%] (%v)"
                                       :size 0)
                               (const (ask nil)))))
    (kom-symbolic-marks-alist (repeat (mark-association
                                       nil
                                       :tag symbolic-mark-association
                                       :mark-key-prompt symbolic-mark-name
                                       :mark-value-prompt mark-type-to-assoc)))
    (kom-reading-puts-comments-in-pointers-last (toggle (after before)))
    (kom-autowrap (choice ((const (on t))
                           (const (off nil))
                           (number nil :tag max-text-length))))
    (kom-autowrap-timeout (choice ((const (no-timeout nil))
                                   (number nil :tag autowrap-timeout))))
    (kom-auto-list-faqs (toggle (on off)))
    (kom-auto-review-faqs (toggle (on off)))
    (kom-dashed-lines (toggle (on off)))
    (kom-show-author-at-end (toggle (on off)))

    (kom-truncate-threshold (choice ((const (off nil))
				     (number nil :tag truncate-threshold-lines))))
    (kom-truncate-show-lines (number nil))

    (kom-print-number-of-unread-on-entrance (toggle (yes no)))
    (kom-show-where-and-what (toggle (yes no)))
    (kom-show-since-and-when (toggle (yes no)))
    (kom-remember-password (toggle (yes no)))
    (kom-idle-hide (number))
    (kom-show-footnotes-immediately (toggle (yes no)))
    (kom-follow-comments-outside-membership (toggle (yes no)))
    (kom-read-depth-first (toggle (depth-first time-order)))
    (kom-read-related-first (choice ((const (off nil))
				     (const (ancestor oldest-ancestor))
				     (const (relative oldest-relative)))))
    (kom-continuous-scrolling  (toggle (on off)))
    (kom-deferred-printing (toggle (on off)))
    (kom-higher-priority-breaks (choice ((const (express-break express))
                                         (const (break t))
                                         (const (no-break nil)))))
    (kom-server-priority (number))
    (kom-server-priority-breaks (choice ((const (express-server-break express))
                                         (const (express-server-letters-break express-letters))
                                         (const (server-break t))
                                         (const (letters-server-break letters))
                                         (const (after-conf-server-break after-conf))
                                         (const (after-conf-letters-server-break after-conf-letters))
                                         (const (when-done-server-break when-done))
                                         (const (no-server-break nil)))))
    (kom-session-nickname (choice ((const (automatic-session-name nil))
				   (string nil 
					   :tag given-session-name
					   :help-echo select-buffer))))			 

    (kom-relogin-inhibit-commands (repeat (command nil :tag command)))
    (kom-login-hook (repeat (command nil :tag command)))
    (kom-do-when-done (repeat (choice ((command nil :tag command)
                                       (kbd-macro nil :tag kbd-macro))
                                      :tag execute
                                      :help-echo select-what-to-execute
                                      :format "%[%t%] %v")))
    (kom-page-before-command (choice ((const (page-none nil))
                                      (const (page-all t))
                                      (repeat (command nil
                                                       :tag command)
                                              :tag page-some
                                              :menu-tag page-some
                                              :value (kom-view-next-text)))
                                     ))
    (kom-permissive-completion (noggle (on off)))
    (kom-membership-default-priority
     (choice ((const (ask-every-time ask))
              (const (ask-every-time nil))
              (number (0 255)
                      :tag fixed-priority 
                      :help-echo select-priority
                      :format "%[%t%] (%v)"
                      :size 0))))
    (kom-membership-default-message-flag
     (choice ((const (ask-every-time ask))
              (const (yes t))
              (const (no nil)))))
    (kom-show-personal-messages-in-buffer
     (choice ((const (messages-in-lyskom-buffer t))
              (const (discard-messages nil))
              (string nil :tag in-named-buffer :help-echo select-buffer))))
    (kom-pop-personal-messages (choice ((const (yes t))
                                        (const (yes-dont-pop yes))
                                        (const (no nil)))))
    (kom-ding-on-new-letter (ding))
    (kom-ding-on-priority-break (ding))
    (kom-ding-on-wait-done (ding))
    (kom-ding-on-common-messages (ding))
    (kom-ding-on-group-messages (ding))
    (kom-ding-on-personal-messages (ding))
    (kom-ding-on-no-subject (ding))
    (kom-audio-player (file))
    (kom-default-message-recipient 
     (choice ((const (everybody-rcpt everybody))
              (const (group-rcpt group))
              (const (sender-rcpt sender))
              (const (last-recipient-recpt last-recipient)))))
    (kom-filter-outgoing-messages (noggle (yes no)))
    (kom-highlight-conferences
     (repeat (cons ((choice ((const (other-persons t))
			     (const (morons kom-morons))
                             (const (friends kom-friends))
                             (const (me lyskom-pers-no))
                             (const (i-am-supervisor lyskom-highlight-i-am-supervisor))
                             (const (has-no-presentation lyskom-highlight-has-no-presentation))
			     (const (pers-list-1 kom-person-list-1))
			     (const (pers-list-2 kom-person-list-2))
			     (const (pers-list-3 kom-person-list-3))
			     (const (pers-list-4 kom-person-list-4))
			     (symbol nil :tag other-list)
			     (person nil :tag name :lyskom-predicate (pers))
                             (repeat (person nil :tag name) 
                                     :indent 12
                                     :tag conflist
                                     :lyskom-predicate (pers conf)
                                     :menu-tag conflist)
                             )
                            :tag highlight-conferences
			    :format "%[%t%] %v\n"
                            )
                    (face (kom-active-face
                           kom-active-highlight-face
                           kom-active-strikethrough-face
                           kom-friends-face
                           kom-morons-face
                           kom-me-face) :tag highlight-face))
                   :format "%v"
                   )
             ))

    (kom-presence-messages-in-buffer 
     (choice ((const (always-show-presence t))
	      (const (never-show-presence nil))
	      (symbol nil :tag obsolete-setting)
	      (repeat (cons ((choice ((const (other-persons t))
				      (const (me lyskom-pers-no))
				      (const (morons kom-morons))
				      (const (friends kom-friends))
				      (const (pers-list-1 kom-person-list-1))
				      (const (pers-list-2 kom-person-list-2))
				      (const (pers-list-3 kom-person-list-3))
				      (const (pers-list-4 kom-person-list-4))
				      (symbol nil :tag other-list)
				      (person nil :tag name :lyskom-predicate (pers))
				      (repeat (person nil :tag name) 
					      :indent 12
					      :tag conflist
					      :lyskom-predicate (pers)
					      :menu-tag conflist)
				      )
				     :tag some-persons
				     :format "%[%t%] %v\n"
				     )
			     (toggle (yes no) :tag show-presence))
			    )
		      :tag detailed-setting
		      ))))

    (kom-presence-messages-in-echo-area
     (choice ((const (always-show-presence t))
	      (const (never-show-presence nil))
	      (symbol nil :tag obsolete-setting)
	      (repeat (cons ((choice ((const (other-persons t))
				      (const (me lyskom-pers-no))
				      (const (morons kom-morons))
				      (const (friends kom-friends))
				      (const (pers-list-1 kom-person-list-1))
				      (const (pers-list-2 kom-person-list-2))
				      (const (pers-list-3 kom-person-list-3))
				      (const (pers-list-4 kom-person-list-4))
				      (symbol nil :tag other-list)
				      (person nil :tag name :lyskom-predicate (pers))
				      (repeat (person nil :tag name) 
					      :indent 12
					      :tag conflist
					      :lyskom-predicate (pers)
					      :menu-tag conflist)
				      )
				     :tag some-persons
				     :format "%[%t%] %v\n"
				     )
			     (toggle (yes no) :tag show-presence))
			    )
		      :tag detailed-setting
		      ))))

    (kom-friends (repeat (person nil :tag name)))
    (kom-morons (repeat (person nil :tag name)))
    (kom-person-list-1 (repeat (person nil :tag name)))
    (kom-person-list-2 (repeat (person nil :tag name)))
    (kom-person-list-3 (repeat (person nil :tag name)))
    (kom-person-list-4 (repeat (person nil :tag name)))
    (kom-url-viewer-preferences (repeat (url-viewer nil :tag viewer-program)))
    (kom-windows-browser-command (file))
    (kom-mosaic-command (choice ((file nil :tag ext-simple-command :format "%[%t:%] %v")
                                     (repeat (string nil :tag ext-complex-component :format "%[%t:%] %v")
                                             :tag ext-complex-command
                                             :menu-tag ext-complex-command))))
    (kom-netscape-command (choice ((file nil :tag ext-simple-command :format "%[%t:%] %v")
                                     (repeat (string nil :tag ext-complex-component :format "%[%t:%] %v")
                                             :tag ext-complex-command
                                             :menu-tag ext-complex-command))))
    (kom-galeon-command (choice ((file nil :tag ext-simple-command :format "%[%t:%] %v")
                                     (repeat (string nil :tag ext-complex-component :format "%[%t:%] %v")
                                             :tag ext-complex-command
                                             :menu-tag ext-complex-command))))
    (kom-lynx-terminal (choice ((const (lynx-xterm xterm))
                                (const (lynx-emacs terminal)))))
    (kom-lynx-terminal-command (file))
    (kom-lynx-xterm-command  (choice ((file nil :tag ext-simple-command :format "%[%t:%] %v")
                                     (repeat (string nil :tag ext-complex-component :format "%[%t:%] %v")
                                             :tag ext-complex-command
                                             :menu-tag ext-complex-command))))
    (kom-confirm-multiple-recipients
     (choice ((const (dont-check nil))
              (const (check-before-open before))
              (const (check-before-send after)
                     :match (lambda (w v) (and v (not (eq v 'before))))))))
    (kom-check-commented-author-membership (toggle (yes no)))
    (kom-check-for-new-comments (toggle (yes no)))
    (kom-inhibit-typeahead (noggle (yes no)))
    (kom-max-buffer-size (choice ((const (no-size-limit nil))
                                  (number nil 
                                          :tag max-size-in-bytes
                                          :help-echo select-buffer-size))))

    (kom-ansaphone-record-messages (toggle (yes no)))
    (kom-ansaphone-show-messages (toggle (yes no)))
    (kom-ansaphone-default-reply (string nil :format "%[%t%]\n%v"))
    (kom-remote-control (toggle (on off)))
    (kom-remote-controllers (repeat (person nil :tag name)))
    (kom-self-control (toggle (yes no)))
    (kom-ispell-dictionary (ispell-dictionary))
    (kom-show-namedays (choice ((const (off nil))
                                (const (default-namedays t))
                                (repeat (nameday nil :format "%[%t%]: `%v'" 
						     :tag specific-namedays)
                                        :tag several-name-lists
                                        :menu-tag several-name-lists)
                                (nameday nil :tag specific-namedays))))

    (kom-show-week-number (toggle (on off)))

    (kom-relogin-behaviour (choice ((const (off nil))
				    (const (ask ask))
				    (const (on t)))))

    (kom-membership-default-placement (choice ((const (last last))
                                               (const (first first))
                                               (number nil
                                                       :tag specific-placement
                                                       ))))
    (kom-show-imported-importer (toggle (on off)))
    (kom-show-imported-envelope-sender (toggle (on off)))
    (kom-show-imported-external-recipients (toggle (on off)))
    (kom-show-imported-message-id (toggle (on off)))
    (kom-agree-text (choice ((string nil :tag a-string)
                             (repeat (string nil
                                             :tag a-string
                                             :format "%[%t%] `%v'")
                                     :tag some-string
                                     :menu-tag some-string))))
    (kom-silent-ansaphone (noggle (on off)))
    (kom-default-session-priority (choice ((const (unspecified nil))
                                           (number nil
                                                   :tag priority))))
    (kom-unsubscribe-makes-passive (toggle (on off)))
    (kom-review-priority (choice ((const (same-as-conf nil))
                                  (number nil :tag fixed-priority))))
    (kom-show-creating-software (toggle (on off)))
    (kom-text-footer-format (choice ((const (default-format nil))
                                     (string nil :tag custom-format))))
    (kom-long-lines (toggle (on off)))
    (kom-postpone-default (number))
    (kom-allow-incompleteness (toggle (on off)))
    (kom-smileys (toggle (on off)))
    (kom-ignore-message-senders (repeat (person nil :tag name)))
    (kom-ignore-message-recipients (repeat (person nil 
                                                   :tag name
                                                   :lyskom-predicate (pers conf))))
    (kom-text-header-dash-length (number))
    (kom-text-footer-dash-length (number))
    (kom-show-personal-message-date (toggle (on off)))
    (kom-mercial (string))
    (kom-w3-simplify-body (toggle (on off)))
    (kom-format-html-authors 
     (repeat (cons ((choice ((const (other-persons t))
			     (const (me lyskom-pers-no))
			     (const (morons kom-morons))
			     (const (friends kom-friends))
			     (const (pers-list-1 kom-person-list-1))
			     (const (pers-list-2 kom-person-list-2))
			     (const (pers-list-3 kom-person-list-3))
			     (const (pers-list-4 kom-person-list-4))
			     (symbol nil :tag other-list)
			     (person nil :tag name   
					 :format "%[[*]%] %v"
					 :lyskom-predicate (pers))
			     (repeat (person nil :tag name) 
				     :indent 12
				     :tag conflist
				     :lyskom-predicate (pers)
				     :menu-tag conflist)
			     )
			    :tag some-persons
			    :format "%[%t%] %v\n"
			    )
		    (toggle (yes no) :tag format-html))
		   )
	     :tag detailed-setting
	     ))
    (kom-format-show-images (toggle (on off)))
    (kom-bury-buffers (toggle (on off)))
    (kom-ansaphone-replies (ansaphone))
    (kom-complete-numbers-before-names (toggle (on off)))
    (kom-keep-alive-interval (number))
    (kom-lost-session-notification (choice ((const (off nil))
					    (const (session-lost-all-buffers all-buffers))
					    (const (session-lost-beep beep)))))
    (kom-text-no-prompts (repeat (cons ((command nil :tag command :format "%[%t%]: %v\n")
                                        (toggle (yes no)  :tag prompt-for-text-no :format "%[%t%]: %v")
                                        ))))
    (kom-saved-file-name (file))
    (kom-follow-attachments (toggle (yes no)))
    (kom-show-unread-in-frame-title (toggle (yes no)))

    (kom-unread-mode-line-type (choice ((const (simple-unread-mode-line nil))
					(const (explicit-unread-mode-line t)))))

    (kom-confirm-add-recipients (toggle (yes no)))
    (kom-trim-buffer-minimum (number))
    (kom-dont-check-commented-authors (repeat (person nil :tag name)))
    (kom-print-relative-dates (toggle (yes no)))
    (kom-show-mx-date-as-local (toggle (yes no)))
    (kom-print-seconds-in-time-strings (toggle (yes no)))
    (kom-extended-status-information (choice ((const (yes t))
                                              (repeat (cons ((choice ((const (extended-conf-info server))
                                                                      (const (extended-pers-info pers))
                                                                      (const (extended-server-info server))
                                                                      (const (extended-read-faq-info read-faq))
                                                                      (const (extended-statistics-info raw-server-stats))
                                                                      (const (extended-all-other-info t))
                                                                      )
                                                                     :tag extended-info-type
                                                                     :format "%[%t%] %v\n")
                                                             (toggle (yes no) :tag extended-info-show)))
                                                      :tag specific-extended-info 
                                                      :menu-tag specific-extended-info))))
    (kom-edit-hide-add-button (noggle (yes no)))
    (kom-keyboard-menu-immediate-selection (noggle (yes no)))
    (kom-url-transformation-rules (repeat (cons ((string nil :tag url-transform-regexp :format "%[%t%] %v\n")
                                                 (string nil :tag url-transform-newtext)))))
    (kom-text-links (repeat (cons ((choice ((person nil :tag link-specific-rcpt :lyskom-predicate (pers conf))
                                            (const (all-conferences t)))
                                           :tag link-pattern-for
                                           :format "%[%t%]: %v")
                                   (repeat (list ((string nil :tag link-pattern :format "%[%t%]: %v\n")
                                                  (string nil :tag link-replace :format "%[%t%]: %v\n")
                                                  (number (0 9) :tag link-highlight-match :format "%[%t%]: %v\n")
                                                  (toggle (yes no) :tag link-fold-case :format "%[%t%]: %v"))
                                                 ) :indent 12 :tag "")
                                   ))))
    (kom-url-face (face))
    (kom-text-no-face (face))
    (kom-active-face (face))
    (kom-active-highlight-face (face))
    (kom-active-strikethrough-face (face))
    (kom-highlight-face (face))
    (kom-me-face (face))
    (kom-friends-face (face))
    (kom-morons-face (face))
    (kom-subject-face (face))
    (kom-presence-face (face))
    (kom-first-line-face (face))
    (kom-warning-face (face))
    (kom-mark-face (face))
    (kom-dim-face (face))
    (kom-text-body-face (face t))
    (kom-dashed-lines-face (face t))
    (kom-async-text-body-face (face t))
    (kom-async-dashed-lines-face (face t))
    (kom-show-sync-messages (toggle (yes no)))
    (kom-netscape-variant (choice ((const (default-netscape nil))
                                   (const (new-window-netscape new-window))
                                   (const (new-tab-netscape new-tab)))))
    (kom-auto-confirm-new-conferences (toggle (yes no)))
    (kom-text-properties (toggle (yes no)))
    (kom-fontify-text (toggle (yes no)))
))

(defvar lyskom-widget-functions 
  '((toggle . lyskom-toggle-widget)
    (noggle . lyskom-toggle-widget-inverse)
    (ding   . lyskom-ding-widget)
    (choice . lyskom-choice-widget)
    (string . lyskom-string-widget)
    (symbol . lyskom-symbol-widget)
    (number . lyskom-number-widget)
    (const .  lyskom-item-widget)
    (repeat . lyskom-repeat-widget)
    (cons . lyskom-cons-widget)
    (list . lyskom-list-widget)
    (face . lyskom-face-widget)
    (nameday . lyskom-nameday-widget)
    (kbd-macro . lyskom-kbd-macro-widget)
    (url-viewer . lyskom-url-viewer-widget)
    (ispell-dictionary . lyskom-ispell-dictionary-widget)
    (open-window . lyskom-open-window-widget)
    (command . lyskom-command-widget)
    (person . lyskom-person-widget)
    (language-choice . lyskom-language-widget)
    (file . lyskom-file-widget)
    (ext-command . lyskom-external-command-widget)
    (ansaphone . lyskom-ansaphone-reply-widget)
    (mark-association . lyskom-mark-association-widget)
))

(defun lyskom-make-menu-tag (str)
  "Make a menu tag from the string STR."
  (if (string-match "\\(.*\\):\\s-*" str)
      (match-string 1 str)
    str))

(defun lyskom-create-widget (variable)
  (let* ((el (assq variable lyskom-custom-variables))
         (dummy (or el (error "Unknown variable: %S" variable)))
         (spec (lyskom-widget-convert-specification (car (cdr el)) variable))
         (tag-sym (intern (concat (symbol-name variable) "-tag")))
         (doc-sym (intern (concat (symbol-name variable) "-doc")))
         (help-sym (intern (concat (symbol-name variable) "-help")))
         (value (save-current-buffer (set-buffer lyskom-buffer)
                                     (symbol-value variable)))
         (storage-widget nil))

    (lyskom-ignore value help-sym dummy)       ; Are they ever used?

    (setq storage-widget
	  (prog1
	      (widget-create 'checkbox
			     ':value (and (not (memq variable kom-dont-read-saved-variables))
					  (or (memq variable lyskom-elisp-variables)
					      (lyskom-flag-global-variable-from-elisp variable)))
			     ':args (list variable)
			     ':format "%[%v%]"
			     ':help-echo (lyskom-custom-string 'variable-type-help))
	    (widget-insert " ")))

    (setq spec 
          (cons (car spec)
                (append
                 (list ':tag
                       (lyskom-custom-string tag-sym)
                       ':menu-tag
                       (lyskom-make-menu-tag (lyskom-custom-string tag-sym))
                       ':value
                       (lyskom-custom-get-value variable)
                       ':help-echo
                       (lyskom-format 
                        (lyskom-custom-string 'default-help-echo)
                        (symbol-name variable))
                       ':lyskom-storage-widget storage-widget
                       )
                 (cdr spec))))

    (let ((widget (apply 'widget-create spec)))
      (condition-case nil
          (progn
            (if (string= "" (lyskom-custom-string doc-sym))
                (progn (widget-insert (lyskom-format " %#1@(%#2s)" 
                                                     `(face ,kom-dim-face)
                                                     (symbol-name variable)))
                       (widget-insert "\n"))
              (widget-insert "  ")
              (widget-create 'lyskom-widget-help
                             ':value (lyskom-default-value 'kom-customize-format)
                             ':help-echo
                             (if (eq (lyskom-default-value 'kom-customize-format)
                                     'long)
                                 (lyskom-custom-string 'hide-doc)
                               (lyskom-custom-string 'show-doc))
                             ':args
                             (list (list 'long
                                         "\n%s\n\n"
                                         (lyskom-custom-string doc-sym)
                                         (lyskom-custom-string 'hide-doc)
                                         "-")
                                   (list 'short 
                                         "%s"
                                         ""
                                         (lyskom-custom-string 'show-doc)
                                         "?"))
                             ':format (lyskom-format "%%[[%%T]%%] %[%#1@(%#2s)%]\n%%D"
                                                     `(face ,kom-dim-face)
                                                     (symbol-name variable)))))
        (error (widget-insert "\n")))
        widget)))


(defun lyskom-widget-convert-specification (spec var)
  "Convert a LysKOM widget specification to something widget-create accepts"
  (let ((convertfn (assq (car spec) lyskom-widget-functions)))
    (if (null convertfn)
        (error "Unknown widget type: %S" spec))
    (nconc (funcall (cdr convertfn)
                    (car spec)
                    (car (cdr spec))
                    (cdr (cdr spec))
                    var)
           (lyskom-widget-convert-props spec))))

(defun lyskom-widget-convert-props (spec)
  "Convert widget properties to a format that widget-create likes"
  (let ((propl (nthcdr 2 spec))
        (result))
    (if (/= 0 (% (length propl) 2))
        (error "Widget property list has odd length: %S" spec))
    (while propl
      (setq result (cons (car propl) result))
      (setq propl (cdr propl))
      (setq result
            (cons (cond ((symbolp (car propl))
                         (cond ((eq (car propl) t) t)
                               ((eq (car propl) nil) nil)
                               (t (lyskom-custom-string (car propl)))))
                        ((and (consp (car propl))
                              (eq 'quote (car (car propl))))
                         (car (cdr (car propl))))
                        (t (car propl)))
                  result))
      (setq propl (cdr propl)))
    (nreverse result)))


(defun lyskom-build-simple-widget-spec (type defaults propl)
  (let (new-props)
    (while defaults
      (unless (plist-member propl (car defaults))
        (setq new-props (cons (car defaults)
                              (cons (car (cdr defaults)) new-props))))
      (setq defaults (cdr (cdr defaults))))
    (cons type new-props)))


(defun lyskom-file-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec 'file
                                   '(:format "%[%t%] %v" :size 1)
                                   propl))

(defun lyskom-person-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec 'lyskom-name nil propl))

(defun lyskom-mark-association-widget (type &optional args propl ar)
  (lyskom-build-simple-widget-spec 'lyskom-mark-association nil propl))

(defun lyskom-command-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec 'lyskom-command nil propl))

(defun lyskom-kbd-macro-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec 'lyskom-kbd-macro
                                   (list ':macro-buffer lyskom-buffer)
                                   propl))

(defun lyskom-item-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec 'item
                                   (list ':format "%t"
                                         ':tag (lyskom-custom-string 
                                                (elt args 0))
                                         ':value (elt args 1))
                                   propl))

(defun lyskom-language-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'menu-choice
   (list ':format "%[%t%] %v"
         ':case-fold t
         ':tag (lyskom-custom-string 'language)
         ':args
         (mapcar
          (lambda (x)
            (list 'item
                  ':tag (lyskom-language-name (car x))
                  ':format "%t"
                  ':value (elt x 0)))
          lyskom-languages))
   propl))

(defun lyskom-nameday-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'menu-choice
   (list ':format "%[%t%] %v"
         ':case-fold t
         ':args
         (mapcar
          (lambda (x)
            (list 'item
                  ':tag (nameday-data->name (cdr x))
                  ':format "%t"
                  ':value (nameday-data->code (cdr x))))
          lyskom-namedays))
   propl))

(defvar ispell-dictionary-alist)
(defun lyskom-ispell-dictionary-widget (type &optional args propl var)
  (let ((tmp-dictionary-alist nil))
    (condition-case nil 
        (progn (require 'ispell)
               (setq tmp-dictionary-alist ispell-dictionary-alist))
      (error (when (null ispell-dictionary-alist)
               (setq tmp-dictionary-alist '("american" "brasiliano"
                                            "british" "castellano"
                                            "castellano8" "czech" 
                                            "dansk" "deutsch"
                                            "deutsch8" "english" 
                                            "esperanto" "esperanto-tex"
                                            "francais7" "francais" 
                                            "francais-tex" "nederlands"
                                            "nederlands8" "norsk"
                                            "norsk7-tex" "polish"
                                            "russian" "svenska")))))
    (lyskom-build-simple-widget-spec 
     'menu-choice
     (list ':format "%[%t%] %v"
           ':case-fold nil
           ':args
           (cons (list 'item
                       ':tag "ispell-dictionary"
                       ':format "%t"
                       ':value nil)
                 (delq nil
                       (mapcar 
                        (lambda (x)
                          (and (car x)
                               (list 'item
                                     ':tag (car x)
                                     ':format "%t"
                                     ':value (car x))))
                        tmp-dictionary-alist))))
     propl)))

(defun lyskom-face-widget (type &optional args propl var)
  (let ((wargs (append (mapcar (lambda (f)
                                 (list 'item 
                                       :tag (symbol-name f)
                                       :value f
                                       :format "%t"))
                               (or
                                (and (listp args) args)
                                (cdr (assq var lyskom-predefined-faces))))
                       (list (list 'symbol
                                   ':size 30
                                   ':tag (lyskom-custom-string 
                                          'other-face))))))
    (when (eq args t)
      (setq wargs 
            (append (list (list 'item
                                :tag (lyskom-custom-string 'generated-face)
                                :value nil
                                :format "%t"))
                    wargs)))
    (if wargs
        (lyskom-build-simple-widget-spec 'menu-choice
                                         (list ':format "%[%t%] %v"
                                               ':args wargs)
                                         propl)
      (lyskom-build-simple-widget-spec 'symbol
                                       (list ':format "%[%t%] %v"
                                             ':size 30
                                             ':args wargs)
                                       propl))))

(defun lyskom-url-viewer-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'menu-choice 
   (list ':format "%[%v%]"
         ':case-fold t
         ':help-echo (lyskom-custom-string 'select-url-viewer)
         ':args
         (list (list 'item
                     ':tag (lyskom-custom-string 'no-viewer)
                     ':format "%t"
                     ':value nil)
               (list 'item 
                     ':tag (lyskom-custom-string 'default-viewer)
                     ':format "%t"
                     ':value "default")
               (list 'item
                     ':tag (lyskom-custom-string 'windows-viewer)
                     ':format "%t"
                     ':value "windows")
               (list 'item
                     ':tag (lyskom-custom-string 'netscape-viewer)
                     ':format "%t"
                     ':value "netscape")
               (list 'item
                     ':tag (lyskom-custom-string 'emacs-w3-viewer)
                     ':format "%t"
                     ':value "w3")
               (list 'item 
                     ':tag (lyskom-custom-string 'emacs-general-viewer)
                     ':format "%t"
                     ':value "emacs")
               (list 'item
                     ':tag (lyskom-custom-string 'emacs-dired-viewer)
                     ':format "%t"
                     ':value "dired")
               (list 'item 
                     ':tag (lyskom-custom-string 'emacs-mail-viewer)
                     ':format "%t"
                     ':value "mail-mode")
               (list 'item
                     ':tag (lyskom-custom-string 'emacs-telnet-viewer)
                     ':format "%t"
                     ':value "telnet-mode")
               (list 'item
                     ':tag (lyskom-custom-string 'mosaic-viewer)
                     ':format "%t"
                     ':value "mosaic")
               (list 'item
                     ':tag (lyskom-custom-string 'lynx-viewer)
                     ':format "%t"
                     ':value "lynx")
	       (list 'item
                     ':tag (lyskom-custom-string 'galeon-viewer)
                     ':format "%t"
                     ':value "galeon")))
   propl))

(defun lyskom-open-window-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'menu-choice
   (list ':case-fold t
         ':format "%[%t%] %v"
         ':args
         (list (list 'item
                     ':tag (lyskom-custom-string 'other-window)
                     ':format "%t"
                     ':value 'other)
               (list 'item 
                     ':tag (lyskom-custom-string 'other-frame)
                     ':format "%t"
                     ':value 'other-frame)
               (list 'item 
                     ':tag (lyskom-custom-string 'new-frame)
                     ':format "%t"
                     ':value 'new-frame)
               (list 'item
                     ':tag (lyskom-custom-string 'lyskom-window)
                     ':format "%t"
                     ':value nil)
               (list 'editable-field
                     ':tag (lyskom-custom-string 'window-on-buffer)
                     ':format "%[%t%]: `%v'"
                     ':value ""
                     ':size 0)))
   propl))

(defun lyskom-ding-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'menu-choice
   (list  ':case-fold t
          ':format "%[%t%] %v"
          ':args
          (list (list 'item 
                      ':tag (lyskom-custom-string 'turned-off)
                      ':value 0
                      ':format "%t"
                      ':match '(lambda (w v) (eq v 0)))
                (list 'lyskom-number
                      ':tag (lyskom-custom-string 'number-of-times)
                      ':help-echo (lyskom-custom-string 'select-number)
                      ':value 1
                      ':format "%[%t%]: (%v)"
                      ':size 0
                      ':min-value 1
                      ':max-value 255)
                (list 'lyskom-string
                      ':tag (lyskom-custom-string 'sound-file)
                      ':help-echo (lyskom-custom-string 'select-audio-file)
                      ':size 0)
                (list 'editable-list
                      ':format "%[%t%]\n%v%i"
                      ':tag (lyskom-custom-string 'specific-spec)
                      ':menu-tag (lyskom-custom-string 'specific-spec)
                      ':args
                      `((cons :format "%v" 
                              :value (1 0)
                              :args
                              ((menu-choice
                                :case-fold t
                                :format "%[%t%]: %v\n"
                                :tag ,(lyskom-custom-string 'conf-or-person)
                                :args
                                ((lyskom-name :lyskom-predicate (pers conf)
                                              :tag ,(lyskom-custom-string 'conf-or-person))
                                 (item :tag ,(lyskom-custom-string 'other-persons)
                                       :value t
                                       :format "%t\n")))
                               (menu-choice
                                :case-fold t
                                :format "%[%t%]: %v\n"
                                :tag ,(lyskom-custom-string 'ding)
                                :args
                                ((item :tag ,(lyskom-custom-string 'turned-off)
                                       :value nil
                                       :format "%t"
                                       :match (lambda (w v) (or (null v) (eq v 0))))
                                 (lyskom-number :tag ,(lyskom-custom-string 'number-of-times)
                                                :help-echo ,(lyskom-custom-string 'select-number)
                                                :value 1
                                                :format "%[%t%]: (%v)"
                                                :size 0
                                                :min-value 1
                                                :max-value 255)
                                 (lyskom-string :tag ,(lyskom-custom-string 'sound-file)
                                                :help-echo ,(lyskom-custom-string 'select-audio-file)
                                                :size 0))))))
                      )
                ))
   propl))

(defun lyskom-toggle-widget-inverse (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'menu-choice
   (list ':case-fold t
         ':format "%[%t%] %v"
         ':args
         (list (list 'item ':tag (lyskom-custom-string (elt args 0))
                     ':value nil
                     ':format "%t")
               (list 'item 
                     ':tag (lyskom-custom-string (elt args 1))
                     ':value t
                     ':match '(lambda (w v) v)
                     ':format "%t")))
   propl))

(defun lyskom-toggle-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'menu-choice
   (list ':case-fold t
         ':format "%[%t%] %v"
         ':args
         (list (list 'item
                     ':tag (lyskom-custom-string (elt args 0))
                     ':value t
                     ':format "%t"
                     ':match '(lambda (w v) v))
               (list 'item ':tag (lyskom-custom-string (elt args 1))
                     ':value nil
                     ':format "%t")))
   propl))


(defun lyskom-repeat-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'editable-list 
   (list ':format "%[%t%]\n%v%i"
	 ':entry-format "%i %d %v\n"
         ':args
         (list (lyskom-widget-convert-specification args var)))
   propl))

(defun lyskom-cons-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'cons
   (list ':format "%v"
         ':tag ""
         ':args (list (lyskom-widget-convert-specification (elt args 0) var)
                      (lyskom-widget-convert-specification (elt args 1) var)))
   propl))

(defun lyskom-list-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'list
   (list ':format "%v"
         ':tag ""
         ':args (mapcar (lambda (x) (lyskom-widget-convert-specification x var)) args))
   propl))

(defun lyskom-choice-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'menu-choice
   (list ':case-fold t
         ':format "%[%t%] %v"
         ':args
         (mapcar (lambda (x) (lyskom-widget-convert-specification x var)) args))
   propl))


(defun lyskom-string-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'lyskom-string 
   (list ':size 0 
         ':format "%[%t%] `%v'")
   propl))

(defun lyskom-symbol-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'symbol 
   (list ':size 0 
         ':format "%[%t%] `%v'")
   propl))


(defun lyskom-number-widget (type &optional args propl var)
  (if args
      (lyskom-build-simple-widget-spec
       'lyskom-number
       (list ':min-value (elt args 0)
             ':max-value (elt args 1)
	     ':value 0
             ':size 0)
       propl)
    (lyskom-build-simple-widget-spec
     'lyskom-number
     (list ':size 0 ':value 0)
     propl)))

;;;
;;; The ansaphone reply widget (whew!)

(defun lyskom-ansaphone-reply-widget (type &optional args propl var)
  (lyskom-build-simple-widget-spec
   'editable-list
   (list ':format "%t:\n%v%i\n"
         ':args
         `((group
            :args ((menu-choice :tag ,(lyskom-custom-string 'ar-message-type)
                                :format "%[%t%]: %v"
                                :args
                                ((item :value personal :tag ,(lyskom-custom-string 'ar-personal))
                                 (item :value group :tag ,(lyskom-custom-string 'ar-group))
                                 (item :value common :tag ,(lyskom-custom-string 'ar-alarm))
                                 (item :value nil :tag ,(lyskom-custom-string 'ar-any-type))))
                   (menu-choice :tag ,(lyskom-custom-string 'ar-sender)
                                :args
                                ((item :value nil :tag ,(lyskom-custom-string 'ar-any-sender))
                                 (editable-list :tag ,(lyskom-custom-string 'ar-specified-sender)
                                                :menu-tag ,(lyskom-custom-string 'ar-specified-sender)
                                                :format "%t:\n%v%i\n"
                                                :indent 14
                                                :args
                                                ((lyskom-name 
                                                  :format "%[%t%]: %v"
                                                  :tag ,(lyskom-custom-string 'ar-person))))))
                   (menu-choice :tag ,(lyskom-custom-string 'ar-recipient)
                                :args
                                ((item :value nil :tag ,(lyskom-custom-string 'ar-any-recipient))
                                 (editable-list :tag ,(lyskom-custom-string 'ar-specified-recipient)
                                                :menu-tag ,(lyskom-custom-string 'ar-specified-recipient)
                                                :format "%t:\n%v%i\n"
                                                :indent 14
                                                :args
                                                ((lyskom-name :tag ,(lyskom-custom-string 'ar-pers-or-conf)
                                                              :format "%[%t%]: %v"
                                                              :lyskom-predicate (pers conf))))))
                   (menu-choice :tag ,(lyskom-custom-string 'ar-message-text)
                                :args
                                ((item :value nil :tag ,(lyskom-custom-string 'ar-any-message))
                                 (lyskom-string :tag ,(lyskom-custom-string 'ar-matching-regexp)
                                                :size 0
                                                :format "%[%t%] `%v'\n")))
                   (menu-choice :tag ,(lyskom-custom-string 'ar-reply)
                                :args
                                ((item :value nil :tag ,(lyskom-custom-string 'ar-no-reply))
                                 (lyskom-string :tag ,(lyskom-custom-string 'ar-reply-text)
                                                :size 0
                                                :format "%[%t%] `%v'\n")))))))
   propl))


;;; ======================================================================
;;; Generic LysKOM widget functions
;;;

(defun lyskom-widget-value-get (widget)
  (widget-get widget ':value))

(defun lyskom-widget-value-delete (widget)
  (set-marker (widget-get widget ':value-from) nil)
  (set-marker (widget-get widget ':value-to) nil))
  

(defun lyskom-widget-value-to-external (widget value)
  value)

(defun lyskom-widget-value-to-internal (widget value)
  value)

(defun lyskom-widget-invalid-value (widget)
  (widget-put widget ':error 
              (lyskom-format (lyskom-custom-string 'invalid-value)
                             (widget-value widget)))
  widget)


;;;
;;; The Person Widget
;;;

(defun lyskom-widget-name-action (widget &optional event)
  (widget-value-set widget
                    (save-current-buffer
                      (set-buffer lyskom-buffer)
                      (lyskom-read-conf-no
                       (lyskom-custom-string
                        (cond ((widget-get widget ':lyskom-prompt)
                               (widget-get widget ':lyskom-prompt))
                              ((and 
                                (memq 'pers (widget-get widget
                                                        ':lyskom-predicate))
                                (memq 'conf (widget-get widget
                                                        ':lyskom-predicate)))
                               'which-conf-or-person)
                              ((memq 'pers (widget-get widget
                                                       ':lyskom-predicate))
                               'which-person)
                              ((memq 'conf (widget-get widget
                                                       ':lyskom-predicate))
                               'which-conf)
                              (t 'which-name)))
                       (widget-get widget ':lyskom-predicate)
                       nil "" t)))
  (widget-setup))
               
(defun lyskom-widget-name-value-create (widget)
  (let* ((size (widget-get widget ':size))
         (value (widget-get widget ':value))
         (from (point))
         (string (save-current-buffer
                   (set-buffer lyskom-buffer)
                   (cond ((or (null value) (eq 0 value)) "")
                         (t (or (conf-stat->name
                                 (blocking-do 'get-conf-stat value))
                                (lyskom-format
                                 (lyskom-custom-string 'some-person)
                                 value)))))))
    (if (null size)
        (insert string)
      (insert string)
      (if (< (lyskom-string-width value) size)
          (insert-char ?\  (- size (lyskom-string-width value)))))

    (widget-put widget ':value-from (copy-marker from))
    (widget-put widget ':value-to (copy-marker (point)))
    (set-marker-insertion-type (widget-get widget ':value-to) nil)
    (insert " ")))

(defun lyskom-widget-name-match (widget value)
  (and (numberp value)
       (>= value 0)))

(defun lyskom-widget-name-validate (widget)
  (let ((value (widget-value widget)))
    (if (and (numberp value)
             (>= value 0))
        nil
      (lyskom-widget-invalid-value widget))))

(define-widget 'lyskom-name 'default
  "A LysKOM person"
  ':format "%[[*]%] %v"
  ':default-help-echo 'change-this-name
  ':value 0
  ':lyskom-predicate '(pers)
  ':action 'lyskom-widget-name-action
  ':value-create 'lyskom-widget-name-value-create
  ':value-delete 'lyskom-widget-value-delete
  ':value-get 'lyskom-widget-value-get
  ':value-to-external 'lyskom-widget-value-to-external
  ':value-to-internal 'lyskom-widget-value-to-internal
  ':match 'lyskom-widget-name-match
  ':validate 'lyskom-widget-name-validate
)

(define-widget 'lyskom-name-list 'editable-list
  "A list of LysKOM commands"
  ':entry-format "%i %d %v"
  ':args '(lyskom-name))


;;;
;;; The Command Widget
;;;

(defun lyskom-widget-command-action (widget &optional event)
  (widget-value-set widget
		    (save-current-buffer
		      (set-buffer lyskom-buffer)
		      (lyskom-read-extended-command nil (concat 
                                                         (widget-get widget ':tag)
                                                         ": "))))
  (widget-setup))
               
(defun lyskom-widget-command-value-create (widget)
  (let* ((size (widget-get widget ':size))
         (value (widget-get widget ':value))
         (from (point))
         (string (save-current-buffer
                   (set-buffer lyskom-buffer)
                   (cond ((null value) "")
                         (t (or (lyskom-try-get-string value 'lyskom-command)
                                (lyskom-format
                                 (lyskom-custom-string 'unknown-command)
                                 (symbol-name value))))))))
    (if (null size)
        (insert string)
      (insert string)
      (if (< (lyskom-string-width value) size)
          (insert-char ?\  (- size (lyskom-string-width value)))))

    (widget-put widget ':value-from (copy-marker from))
    (widget-put widget ':value-to (copy-marker (point)))
    (set-marker-insertion-type (widget-get widget ':value-to) nil)
    (insert " ")))

(defun lyskom-widget-command-match (widget value)
  (symbolp value))

(defun lyskom-widget-command-validate (widget)
  (if (symbolp (widget-value widget))
      nil
    (lyskom-widget-invalid-value widget)))
  

(define-widget 'lyskom-command 'default
  "A LysKOM command"
  ':format "%[%t%] %v"
  ':default-help-echo 'select-command
  ':value 'kom-display-time
  ':action 'lyskom-widget-command-action
  ':value-create 'lyskom-widget-command-value-create
  ':value-delete 'lyskom-widget-value-delete
  ':value-get 'lyskom-widget-value-get
  ':value-to-external 'lyskom-widget-value-to-external
  ':value-to-internal 'lyskom-widget-value-to-internal
  ':match 'lyskom-widget-command-match
  ':validate 'lyskom-widget-command-validate
)

(define-widget 'lyskom-command-list 'editable-list
  "A list of LysKOM commands"
  ':entry-format "%i %d %v"
  ':args '(lyskom-command))



;;;
;;; A new String widget
;;;

(defun lyskom-widget-string-action (widget &optional event)
  (let ((tmp (lyskom-read-from-minibuffer 
              (format "%s " (widget-get widget ':tag))
              (widget-value widget))))
    (widget-value-set widget tmp)
    (widget-setup)))


(define-widget 'lyskom-string 'editable-field
  "A string"
  ':format "%[%t%] %v"
  ':action 'lyskom-widget-string-action)

;;;
;;; A Number widget
;;;

(defun lyskom-widget-number-action (widget &optional event)
  (let ((min (widget-get widget ':min-value))
        (max (widget-get widget ':max-value)))
    (widget-value-set
     widget
     (if (and min max)
         (lyskom-read-num-range min max
                                (widget-get widget ':tag)
                                t
                                (widget-value widget))
       (lyskom-read-number (widget-get widget ':tag)
                           (widget-value widget))))
    (widget-setup)))

(defun lyskom-widget-number-value-to-external (widget value)
  (cond ((stringp value) (lyskom-string-to-number value))
	((numberp value) value)
	((null value) 0)
	(t (error "unable to convert settings value"))))

(defun lyskom-widget-number-value-to-internal (widget value)
  (cond ((stringp value) value)
	((numberp value) (format "% 4d" value))
	((null value) "")
	(t (format "%S" value))))

(defun lyskom-widget-number-validate (widget)
  (if (numberp (widget-value widget))
      nil
    (lyskom-widget-invalid-value widget)))

(defun lyskom-widget-number-match (widget value)
  (if (and (widget-get widget ':max-value)
           (widget-get widget ':min-value))
      (and (numberp value)
           (>= value (widget-get widget ':min-value))
           (<= value (widget-get widget ':max-value)))
    (numberp value)))


(define-widget 'lyskom-number 'editable-field
  "A number"
  ':format "%[%t%] %v"
  ':action 'lyskom-widget-number-action
  ':value-to-external 'lyskom-widget-number-value-to-external
  ':value-to-internal 'lyskom-widget-number-value-to-internal
  ':match 'lyskom-widget-number-match
  ':validate 'lyskom-widget-number-validate)


;;;
;;; Keyboard macro widget
;;;

(defun lyskom-widget-kbd-macro-match (widget value)
  (or (stringp value)
      (vectorp value)))

(defun lyskom-widget-kbd-macro-validate (widget)
  (if (lyskom-widget-kbd-macro-match widget (widget-value widget))
      nil
    (lyskom-widget-invalid-value widget)))

(defun lyskom-widget-kbd-macro-value-create (widget)
  (let* ((size (widget-get widget ':size))
         (value (widget-get widget ':value))
         (from (point))
         (string (mapconcat 'single-key-description (append value nil) " ")))
    (if (null size)
        (insert string)
      (insert string)
      (if (< (lyskom-string-width string) size)
          (insert-char ?\  (- size (lyskom-string-width string)))))

    (widget-put widget ':value-from (copy-marker from))
    (widget-put widget ':value-to (copy-marker (point)))
    (set-marker-insertion-type (widget-get widget ':value-to) nil)
    (insert " ")))

;;;
;;; This is a truly disgusting piece of work. In Gnu Emacs it's not
;;; possible to simply read a keyboard macro in a recursive command
;;; loop and have the command loop end when macro definition time is
;;; up, so we start a macro definition, make the normal end-kbd-macro
;;; keys just exit the recursive command loop, go recursive and when
;;; the recursive loop is over, any which way, we see if a macro was
;;; defined and reset the keyboard bindings.
;;;

(defun lyskom-widget-end-kbd-macro ()
  (interactive)
  (exit-recursive-edit))

(defun lyskom-widget-kbd-macro-action (widget &optional event)
  (let ((tmp last-kbd-macro)
        (value nil))
    (save-window-excursion
      (unwind-protect
          (progn
            (mapc (function
                     (lambda (k)
                       (define-key global-map k 'lyskom-widget-end-kbd-macro)))
                    (where-is-internal 'end-kbd-macro))
            (if (widget-get widget ':macro-buffer)
                (pop-to-buffer (widget-get widget ':macro-buffer)))
            (start-kbd-macro nil)
            (recursive-edit))
        (mapc (function
                 (lambda (k)
                   (define-key global-map k 'end-kbd-macro)))
                (where-is-internal 'lyskom-widget-end-kbd-macro))
        (condition-case e
            (end-kbd-macro)
          (error (setq last-kbd-macro nil)))))
    (setq value
          (prog1 last-kbd-macro
            (setq last-kbd-macro tmp)))
    (if value
        (progn
          (widget-value-set widget value)
          (widget-setup)))))


(define-widget 'lyskom-kbd-macro 'default
  "A Keyboard Macro"
  ':action 'lyskom-widget-kbd-macro-action
  ':value ""
  ':format "%[%t%] %v"
  ':validate 'lyskom-widget-kbd-macro-validate
  ':value-create 'lyskom-widget-kbd-macro-value-create
  ':value-delete 'lyskom-widget-value-delete
  ':value-get 'lyskom-widget-value-get
  ':value-to-external 'lyskom-widget-value-to-external
  ':value-to-internal 'lyskom-widget-value-to-internal
  ':match 'lyskom-widget-kbd-macro-match)


;;;
;;; Mark association widget
;;;

(defun lyskom-widget-mark-association-action (widget &optional event)
  (widget-value-set
   widget
   (cons
    (lyskom-read-string (widget-get widget ':mark-key-prompt)
                        (car (widget-value widget)))
    (lyskom-read-num-range 0
                           255
                           (widget-get widget ':mark-value-prompt)
                           t
                           (cdr (widget-value widget)))))
  (widget-setup))

(defun lyskom-widget-mark-association-match (widget value)
  (and (consp value)
       (let ((s (car value))
             (i (cdr value)))
         (and (stringp s)
              (> (length s) 0)
              (integerp i)
              (>= i 0)
              (<= i 255)))))

(defun lyskom-widget-mark-association-validate (widget)
  (let ((value (widget-value widget)))
    (if (lyskom-widget-mark-association-match widget value)
        nil
      (lyskom-widget-invalid-value widget))))

(defun lyskom-widget-mark-association-value-create (widget)
  (let ((from (point))
        (value (widget-value widget)))
    (insert (format "%s <--> %s"
                    (car value)
                    (if (and (string= (car value) "...")
                             (= (cdr value) 0))
                        "..."
                      (cdr value))))
    (widget-put widget ':value-from (copy-marker from))
    (widget-put widget ':value-to (copy-marker (point)))))

(define-widget 'lyskom-mark-association 'default
  "A mark association."
  :tag "Mark association"
  :format "%[%t%] %v"
  :value '("..." . 0)
  :default-help-echo 'change-this-name
  :match 'lyskom-widget-mark-association-match
  :validate 'lyskom-widget-mark-association-validate
  :action 'lyskom-widget-mark-association-action
  :value-create 'lyskom-widget-mark-association-value-create
  :value-delete 'lyskom-widget-value-delete
  :value-get 'lyskom-widget-value-get
  :value-to-external 'lyskom-widget-value-to-external
  :value-to-internal 'lyskom-widget-value-to-internal)

;;;
;;; Help widget
;;;

(defun lyskom-widget-help-action (widget &optional event)
  (let* ((value (widget-get widget ':value))
         (spec (assq value (widget-get widget ':args)))
         (syms (car (cdr (memq spec (widget-get widget ':args))))))
    (if (null syms)
        (setq syms (car (widget-get widget ':args))))
    (widget-put widget ':help-echo (elt syms 3))
    (widget-value-set widget (car syms))
    (widget-setup)))


(defun lyskom-widget-help-format-handler (widget escape)
  (let* ((value (widget-get widget ':value))
         (spec  (assq value (widget-get widget ':args))))
    (cond ((eq escape ?D)
           (if (widget-get widget ':indent)
               (insert-char ?  (widget-get widget ':indent)))
           (insert (format (elt spec 1) (elt spec 2))))
          ((eq escape ?T)
           (if (widget-get widget ':indent)
               (insert-char ?  (widget-get widget ':indent)))
           (insert (elt spec 4)))
          (t (widget-default-format-handler widget escape)))))


(define-widget 'lyskom-widget-help 'push-button
  "A help widget"
  ':format "%[[%t]%] %D"
  ':action 'lyskom-widget-help-action
  ':format-handler 'lyskom-widget-help-format-handler)
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: talkback.el
;;;;
;;;; Functions for handing bug reports
;;;;


(defvar kom-bug-report-address "kom@lysator.liu.se")

(defvar lyskom-bug-report-special-vars 
  '((lyskom-conf-cache . (lambda (n v) (format "%d items" (length v))))
    (lyskom-uconf-cache . (lambda (n v) (format "%d items" (length v))))
    (lyskom-text-cache . (lambda (n v) (format "%d items" (length v))))
    (lyskom-text-mass-cache . (lambda (n v) (format "%d items" (length v))))
    (lyskom-default-password . "********")))


(defun lyskom-save-backtrace (&rest data)
  "Save the current backtrace"
  (lyskom-message "Saving debugging information...")
  (setq lyskom-backtrace-list (cons (list (current-time-string)
                                          (let* ((buffer (generate-new-buffer " *Backtrace*"))
                                                 (standard-output buffer))
                                            (backtrace)
                                            (prog1 (save-current-buffer
                                                     (set-buffer buffer)
                                                     (buffer-string))
                                              (kill-buffer buffer)))
                                          data)
                                    lyskom-backtrace-list))
  (lyskom-message "Saving debugging information...done"))

(defun lyskom-bug-insert-recent-messages (n)
  (let ((message-buf (get-buffer (lyskom-xemacs-or-gnu " *Message-Log*"
                                                       "*Messages*"))))
    (insert "\n\nRecent messages:\n")
    (if message-buf
        (insert-buffer-substring message-buf
                                 (save-current-buffer
                                   (set-buffer message-buf)
                                   (save-excursion
                                     (goto-char (point-max))
                                     (forward-line -10)
                                     (point)))
                                  (save-current-buffer
                                    (set-buffer message-buf)
                                    (point-max))))))


(defun lyskom-insert-bug-report ()
  (insert 
   (save-current-buffer
     (set-buffer lyskom-buffer)
     (let ((v lyskom-server-version-info))
       (format "\
This bug report will be sent to the LysKOM development team,
not to your local site managers.

Please write in english or swedish, because the LysKOM maintainers (if
there are any) are too lazy to learn other languages just so they can
read your bug reports.

Client time:    %s
Emacs version:  %s (%s)
Client version: %s (%s)
LysKOM server:  %s:%S
Server version: %s %s %S
Current person: %S (%s)
Command line:   %S

Please describe the precise symptoms of the bug and exactly what
actions triggered the bug. Be as detailed as possible. Do not try to
make any conclusions of your own.

"
               (current-time-string)
               (emacs-version)
               (if enable-multibyte-characters "MULE" "no MULE")
               lyskom-clientversion
               (if lyskom-mule-compiled "MULE" "no MULE")
               lyskom-server-name
               lyskom-server-port
               (if v (version-info->server-software v) "unknown")
               (if v (version-info->software-version v) "unknown")
               (if v (version-info->protocol-version v) "unknown")
               lyskom-pers-no
               (condition-case nil
                   (uconf-stat->name (cache-get-uconf-stat lyskom-pers-no))
                 (error ""))
               command-line-args
               ))))
  (let ((saved-place (point)))
    (insert "\n\n")
    (lyskom-bug-insert-recent-messages 20)
    (insert "\n\nSaved backtraces:\n\n")

    (mapc (lambda (el)
              (insert (elt el 0))        ; Time
              (insert "\n")
              (insert (elt el 1))        ; Backtrace
              (insert "\n")
              (mapcar (lambda (data)
                        (insert "Data: ")
                        (prin1 data)
                        (insert "\n"))
                      (elt el 2)))
            lyskom-backtrace-list)

    (insert "\n\nAll variables:\n\n")
   
    (mapatoms 
     (lambda (var)
       (when (and (save-current-buffer (set-buffer lyskom-buffer) (boundp var))
                  (or (string-match "^\\(lys\\)?kom" (symbol-name var))))
         (message "Collecting system information...%s" var)
         (insert (format "(%s " var))
         (cond ((assq var lyskom-bug-report-special-vars)
                (let ((fn (cdr (assq var lyskom-bug-report-special-vars))))
                  (cond ((functionp fn) (lyskom-bug-report-insert-value (funcall fn var (lyskom-default-value var))))
                        (t (lyskom-bug-report-insert-value fn)))))
               (t (lyskom-bug-report-insert-value (lyskom-default-value var))))
         (insert ")\n"))))

    (goto-char saved-place)
    ))
                                 
(defun lyskom-bug-report-insert-value (v)
  (cond ((eq t v) (insert "t"))
        ((eq nil v) (insert "nil"))
        ((symbolp v) (insert "'") (princ v))
        (t (prin1 v)))) 
                          
(defun kom-bug-report (topic)
  "Report a LysKOM bug"
  (interactive "sBug Subject: ")
  (let ((buf lyskom-buffer))
    (unless buf
      (error "Switch to a LysKOM buffer to report bugs."))


    (when (lyskom-xemacs-or-gnu
           (progn (compose-mail kom-bug-report-address topic) t)
           (mail nil kom-bug-report-address topic))
      (goto-char (point-min))
      (re-search-forward (concat "^" (regexp-quote mail-header-separator) "\n\
"))

      (let ((standard-output (current-buffer))) 
        (message "Collecting system information...")
        (make-local-variable 'lyskom-buffer)
        (setq lyskom-buffer buf)
        (lyskom-insert-bug-report)
        (message "Collecting system information...done")))))


  
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: faqs.el
;;;;
;;;; This file contains the code for FAQ handling
;;;;

(defun lyskom-register-read-faq (conf-no text-no)
  (unless conf-no (setq conf-no 0))
  (unless (lyskom-faq-is-read conf-no text-no)
    (setq lyskom-read-faqs (cons (cons conf-no text-no) lyskom-read-faqs))
    (initiate-modify-conf-info 
     'background
     nil
     lyskom-pers-no
     nil
     (list (lyskom-create-aux-item
            0
            10000
            nil
            nil
            (lyskom-create-aux-item-flags nil nil t nil 
                                          nil nil nil nil)
            0
            (format "%d %d" conf-no text-no))))))

(defun lyskom-faq-is-read (conf-no text-no)
  "Return non-nil if lyskom-pers-no has read CONF-NOs FAQ TEXT-NO."
  (member (cons (or conf-no 0) text-no) lyskom-read-faqs))

(defun lyskom-update-read-faqs ()
  "Update the list of read FAQs from the server."
  (setq lyskom-read-faqs nil)
  (let* ((conf-stat (blocking-do 'get-conf-stat lyskom-pers-no))
         (aux-list (and conf-stat (lyskom-get-aux-item
                                   (conf-stat->aux-items conf-stat) 
                                   10000))))
    (lyskom-traverse item aux-list
      (when (string-match "^\\([0-9]+\\) \\([0-9]+\\)" (aux-item->data item))
        (setq lyskom-read-faqs
              (cons (cons (lyskom-string-to-number (match-string 1 (aux-item->data item)))
                          (lyskom-string-to-number (match-string 2 (aux-item->data item))))
                    lyskom-read-faqs))))))

;;; ======================================================================
;;; FAQ Management
;;;

(def-kom-command kom-add-faq (&optional conf-no text-no)
  "Make an existing text a FAQ of a conference. To write a new FAQ for
a conference that doesn't have one, or change an existing FAQ, use
`kom-change-conf-faq'.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-conf-no 'conf-to-add-faq
                                          '(conf pers) nil nil t)
                     (lyskom-read-text-no-prefix-arg 'text-to-add-as-faq)))
  (lyskom-add-faq conf-no text-no))



(def-kom-command kom-add-server-faq (&optional text-no)
  "Make an existing text a FAQ of the server. To write a new FAQ for
a conference that doesn't have one, or change an existing FAQ, use
`kom-change-server-faq'.

This command accepts text number prefix arguments \(see
`lyskom-read-text-no-prefix-arg')."
  (interactive (list (lyskom-read-text-no-prefix-arg 'text-to-add-as-faq)))
  (lyskom-add-faq nil text-no))


(defun lyskom-add-faq (conf-no text-no)
  "Add a FAQ to a conference or the server.
Add to the server if CONF-NO is nil, otherwise add to conference CONF-NO.
The text to add is passed in TEXT-NO"
  (let ((text (blocking-do 'get-text-stat text-no)))
    (if (null text)
        (lyskom-format-insert 'no-such-text-no text-no)
      (lyskom-format-insert 'adding-faq text-no conf-no)
      (cache-del-text-stat text-no)
      (when conf-no
        (cache-del-conf-stat conf-no))
      (let ((aux-item (lyskom-create-aux-item 
                       0 14 0 0 
                       (lyskom-create-aux-item-flags nil nil nil nil
                                                     nil nil nil nil)
                       0
                       (int-to-string text-no))))
        (lyskom-report-command-answer
         (if conf-no
             (blocking-do 'modify-conf-info
                          conf-no
                          nil
                          (list aux-item))
           (blocking-do 'modify-server-info
                        nil
                        (list aux-item))))))))

(def-kom-command kom-del-server-faq ()
  "Remove a FAQ from the server. You need administrative rights to
do this. To add a FAQ, use `kom-add-server-faq'."
  (interactive)
  (lyskom-del-faq nil))


(def-kom-command kom-del-faq ()
  "Remove a FAQ from a conference. To add a FAQ, use
`kom-add-faq'."
  (interactive)
  (let* ((conf-stat (lyskom-read-conf-stat 'conf-to-del-faq
                                           '(conf pers) nil nil t)))
    (lyskom-del-faq conf-stat)))

(defun lyskom-del-faq (conf-stat)
  (let ((faq-list 
         (delq nil
               (mapcar (lambda (aux)
                         (and (lyskom-string-to-int (aux-item->data aux))
                              (cons (aux-item->data aux)
                                    (aux-item->aux-no aux))))
                       (lyskom-get-aux-item
                        (if (null conf-stat)
                            (server-info->aux-item-list
                             (blocking-do 'get-server-info))
                          (conf-stat->aux-items conf-stat))
                        14))))
        (text-no nil))

    (cond
     ((null faq-list) 
      (lyskom-format-insert 'conf-has-no-faq conf-stat))
     (t (setq text-no
              (if (eq 1 (length faq-list))
                  (car (car faq-list))
                (lyskom-completing-read (lyskom-get-string 'text-to-del-as-faq)
                                        (lyskom-maybe-frob-completion-table 
                                         faq-list)
                                        nil t)))
        (when text-no
          (lyskom-format-insert 'deleting-faq 
                                (lyskom-string-to-number text-no)
                                conf-stat)
          (cache-del-text-stat (lyskom-string-to-number text-no))
          (when conf-stat
            (cache-del-conf-stat (conf-stat->conf-no conf-stat)))
          (lyskom-report-command-answer
           (if conf-stat
               (blocking-do 'modify-conf-info
                            (conf-stat->conf-no conf-stat)
                            (list (cdr (lyskom-string-assoc text-no faq-list)))
                            nil)
             (blocking-do 'modify-server-info
                          (list (cdr (lyskom-string-assoc text-no faq-list)))
                          nil))))))))

(def-kom-command kom-review-server-faq ()
  "View the FAQs for the server."
  (interactive)
  (lyskom-review-faq nil (server-info->aux-item-list
                          (blocking-do 'get-server-info))))

(def-kom-command kom-unread-server-faq ()
  "Mark the FAQs for the server as unread."
  (interactive)
  (lyskom-unread-faq nil (server-info->aux-item-list
                          (blocking-do 'get-server-info))))

(def-kom-command kom-review-faq (&optional conf-no)
  "View the FAQs for a particular conference."
  (interactive 
   (list (lyskom-read-conf-no 'view-which-faq '(conf pers) t nil t)))
  (if (zerop conf-no)
      (lyskom-review-faq nil (server-info->aux-item-list
                              (blocking-do 'get-server-info)))
    (let ((conf-stat (blocking-do 'get-conf-stat conf-no)))
      (if conf-stat
          (lyskom-review-faq conf-stat (conf-stat->aux-items conf-stat))
        (lyskom-format-insert 'conf-no-does-not-exist-r conf-no)))))

(def-kom-command kom-unread-faq (&optional conf-no)
  "Marks the FAQs for a particular conference as unread."
  (interactive 
   (list (lyskom-read-conf-no 'unread-which-faq '(conf pers) t nil t)))
  (if (zerop conf-no)
      (lyskom-unread-faq nil (server-info->aux-item-list
                              (blocking-do 'get-server-info)))
    (let ((conf-stat (blocking-do 'get-conf-stat conf-no)))
      (if conf-stat
          (lyskom-unread-faq conf-stat (conf-stat->aux-items conf-stat))
        (lyskom-format-insert 'conf-no-does-not-exist-r conf-no)))))


(defun lyskom-unread-faq (conf-stat aux-list)
  (let ((faq-list (delq nil 
                        (mapcar (lambda (aux)
                                  (lyskom-string-to-int (aux-item->data aux)))
                                (lyskom-get-aux-item aux-list 14)))))
    (if (null faq-list) 
        (lyskom-format-insert 'conf-has-no-faq conf-stat)
      (lyskom-traverse text-no faq-list
        (lyskom-format-insert 'marking-text-unread text-no)
        (lyskom-report-command-answer 
         (lyskom-mark-unread text-no))))))

(defun lyskom-review-faq (conf-stat aux-list)
  (let ((faq-list (delq nil
                        (mapcar (lambda (aux)
                                  (lyskom-string-to-int (aux-item->data aux)))
                                (lyskom-get-aux-item aux-list 14)))))
    (cond 
     ((null faq-list) 
      (lyskom-format-insert 'conf-has-no-faq conf-stat))
     ((eq 1 (length faq-list))
      (lyskom-format-insert 'review-faq-for-r conf-stat)
      (lyskom-view-text (car faq-list) 
                        nil             ;mark-as-read
                        nil             ;follow-comments
                        nil             ;conf-stat
                        nil             ;priority
                        nil             ;build-review-tree
                        t               ;flat-review
                        ))
     (t 
      (lyskom-format-insert 'review-faq-for-r conf-stat)
      (lyskom-do-review-faq faq-list)
      ))))

(defun lyskom-do-review-faq (faq-list &optional review-tree)
  (read-list-enter-read-info
   (lyskom-create-read-info (if review-tree 'REVIEW-FAQ-TREE 'REVIEW-FAQ)
                            nil
                            (lyskom-review-get-priority)
                            (lyskom-create-text-list faq-list)
                            nil t)
   lyskom-reading-list t))

(def-kom-command kom-change-server-faq ()
  "Change a FAQ for the server. If the server doesn't have a FAQ, 
create a new one. You need administrative rights to do this."
  (interactive)
  (lyskom-change-faq nil (lyskom-get-aux-item (server-info->aux-item-list
                                               (blocking-do 'get-server-info))
                                              14)))

(def-kom-command kom-change-conf-faq ()
  "Change a FAQ fo a conference. If the conference doesn't have a FAQ,
create a new FAQ."
  (interactive)
  (let* ((conf-no (lyskom-read-conf-no 'what-to-change-faq-you
                                       '(conf pers) nil nil t))
         (conf-stat (when conf-no       ; Need this to make sure the conf-stat is up-to-date!
                      (cache-del-conf-stat conf-no)
                      (blocking-do 'get-conf-stat conf-no))))
    (if conf-stat
        (lyskom-change-faq conf-stat (lyskom-get-aux-item 
                                      (conf-stat->aux-items conf-stat)
                                      14))
      (lyskom-insert (lyskom-get-string 'conf-does-not-exist)))))


(defun  lyskom-change-faq (conf-stat aux-list)
  "Change a FAQ for a conference."
  ;; Get a list of FAQ texts and corresponding aux-item-numbers
  ;; Get the FAQ to change
  (let* ((faq-list
          (delq nil 
                (mapcar (lambda (x)
                          (and (lyskom-string-to-int (aux-item->data x))
                               (cons (aux-item->data x)
                                     (aux-item->aux-no x))))
                        aux-list)))
         (text-no-aux (cond ((= (length faq-list) 1) (car faq-list))
                            ((null faq-list) nil)
                            (t (lyskom-string-assoc
                                (lyskom-completing-read 
                                 (lyskom-get-string 'text-to-change-as-faq)
                                 (lyskom-maybe-frob-completion-table 
                                  faq-list)
                                 nil t)
                                faq-list))))
         (text-no (and text-no-aux (lyskom-string-to-number (car text-no-aux))))
         (aux-no (and text-no-aux (cdr text-no-aux))))

    (cond 

     ;; If conf-stat is null we are changing the FAQ for the server.
     ;; Don't do this unless we are running enabled and have the right
     ;; privileges.
     ((and (null conf-stat)
           (or (not lyskom-is-administrator)
               (not (privs->admin (pers-stat->privileges
                                   (blocking-do 'get-pers-stat
                                                lyskom-pers-no))))))
      (lyskom-format-insert 'not-supervisor-for-server))

     ;; If we have a conf-stat and are not administrator and not
     ;; supervisor for the conf, then we are not allowed to change
     ;; the FAQ.
     ((and conf-stat 
           (not lyskom-is-administrator)
           (not (lyskom-is-supervisor (conf-stat->conf-no conf-stat)
                                      lyskom-pers-no)))
      (lyskom-format-insert 'not-supervisor-for conf-stat))

     ;; OK, it looks like we are allowed to change the FAQ.
     (t (let ((text-stat nil)
              (text-mass nil))

          ;; Get the old text (if there is one)
          (blocking-do-multiple ((ts (get-text-stat text-no))
                                 (tm (get-text text-no)))
            (setq text-stat ts text-mass tm))

          (let* ((str (and text-mass (text->decoded-text-mass text-mass
                                                              text-stat)))
                 (subject (if (and str (string-match "\n" str))
                              (substring str 0 (match-beginning 0))
                            ""
                            ))
                 (body (if (and str (string-match "\n" str))
                           (substring str (match-end 0))
                         (or str ""))))
            (lyskom-dispatch-edit-text
             lyskom-proc
             (apply 'lyskom-create-misc-list
                    (if (and text-stat text-mass)
                        (append (lyskom-get-recipients-from-misc-list
                                 (text-stat->misc-info-list text-stat))
                                (list 'COMM-TO (text-stat->text-no text-stat)))
                      (list 'RECPT (conf-stat->conf-no conf-stat))))
             subject
             body
             'lyskom-change-faq-2
             conf-stat
             (and text-stat (text-stat->text-no text-stat))
             aux-no)))))))


(defun lyskom-change-faq-2 (text-no conf-stat old-text-no old-aux-no)
  (let ((aux-item (lyskom-create-aux-item 
                   0 14 0 0
                   (lyskom-create-aux-item-flags nil nil nil nil
                                                 nil nil nil nil)
                   0
                   (int-to-string text-no))))
    (if conf-stat
        (progn
          (cache-del-conf-stat (conf-stat->conf-no conf-stat))
          (initiate-modify-conf-info 'background
                                     'lyskom-change-faq-3
                                     (conf-stat->conf-no conf-stat)
                                     (when old-aux-no (list old-aux-no))
                                     (list aux-item)
                                     conf-stat
                                     old-text-no
                                     text-no))
      (initiate-modify-server-info 'background
                                   'lyskom-change-faq-3
                                   (when old-aux-no (list old-aux-no))
                                   (list aux-item)
                                   conf-stat
                                   old-text-no
                                   text-no))))

(defun lyskom-change-faq-3 (retval conf-stat old-text-no text-no)
  (cache-del-text-stat text-no)
  (if retval
      (lyskom-format-insert-before-prompt 
       (if old-text-no 'changed-faq-for-conf-done 'set-faq-for-conf-done)
       conf-stat
       old-text-no
       text-no)
    (lyskom-format-insert-before-prompt 
     (if old-text-no 'changed-faq-for-conf-failed 'set-faq-for-conf-failed)
     conf-stat 
     old-text-no text-no
     (lyskom-current-error))))



;;; ================================================================
;;; Special have-you-read-the-FAQ-checking stuff

(defun lyskom-startup-check-faqs ()
  (let ((faq-list (lyskom-get-unread-faqs nil)))
    (when faq-list
      (lyskom-format-insert 'server-has-new-faq (length faq-list))
      (lyskom-present-unread-faqs nil faq-list))))

(defun lyskom-get-unread-faqs (conf-stat)
  "Return a list of unread FAQs for conf CONF-STAT."
  (let* ((conf-no (if conf-stat (conf-stat->conf-no conf-stat) 0))
         (aux-list (if conf-stat
                       (conf-stat->aux-items conf-stat)
                     (server-info->aux-item-list lyskom-server-info)))
         (faq-list
          (lyskom-filter-list 
           (lambda (faq)
             (not (lyskom-faq-is-read conf-no faq)))
           (delq nil
                 (mapcar (lambda (aux)
                           (lyskom-string-to-int (aux-item->data aux)))
                         (lyskom-get-aux-item aux-list 14))))))

    ;; Filter out FAQs that don't exist
    (let ((collector (make-collector)))
      (lyskom-traverse faq faq-list
        (initiate-get-text-stat 'background 
                                (lambda (text-stat faq conf-no collector)
                                  (if text-stat
                                      (collector-push faq collector)
                                    (lyskom-register-read-faq conf-no faq)))
                                faq
                                faq
                                conf-no
                                collector))
      (lyskom-wait-queue 'background)
      (setq faq-list (nreverse (collector->value collector))))

    faq-list))

(defun lyskom-present-unread-faqs (conf-stat faq-list)
  "Present a list of unread FAQs in an appropriate manner."
  (when faq-list
    (when kom-auto-list-faqs
      (lyskom-format-insert 'unread-faqs-header (length faq-list) 
                            (and conf-stat (conf-stat->conf-no conf-stat)))
      (lyskom-list-text-summary faq-list nil))
    (when kom-auto-review-faqs
      (lyskom-do-review-faq faq-list t))))


(def-kom-command kom-list-faqs (conf-stat)
  "List all FAQs for a conference."
  (interactive (list (lyskom-read-conf-stat 'conf-to-list-faqs
                                            '(conf) nil nil t)))
  (lyskom-do-list-faqs conf-stat
                       (lyskom-get-aux-item 
                        (conf-stat->aux-items conf-stat)
                        14)))

(def-kom-command kom-list-server-faqs ()
  "List all FAQs for the server."
  (interactive)
  (lyskom-do-list-faqs
   nil
   (lyskom-get-aux-item (server-info->aux-item-list
                         (blocking-do 'get-server-info)) 14)))


(defun lyskom-do-list-faqs (conf-stat faq-list)
  (setq faq-list (delq nil 
                       (mapcar (lambda (aux)
                                 (lyskom-string-to-int (aux-item->data aux)))
                               faq-list)))
  (cond (faq-list
         (lyskom-format-insert 'all-faqs-header 
                               (and conf-stat
                                    (conf-stat->conf-no conf-stat)))
         (lyskom-list-text-summary faq-list nil))
        (t (lyskom-format-insert 'conf-has-no-faq conf-stat))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: user-hooks.el
;;;;
;;;; This file contains hook functions that users might be interested in.
;;;;


(def-kom-var kom-list-summary-hook-conf-list nil
  "*This variable controls the behavior of kom-list-summary-hook.
When kom-list-summary-hook is used in kom-after-change-conf-hook, 
this variable specifies which conferences to automatically to list
summary for. It is a list of conference numbers"
  server)

(def-kom-var kom-read-last-text-hook-conf-list nil
  "*This variable controls the behavior of kom-read-last-text-hook.
When kom-read-last-text-hook is used in kom-change-conf-hook, 
this variable specifies which conferences the hook applies to. Each
element is either a conference number of a cons cell consisting of
a conference number and an integer.

If a conference number appears in the list alone, the most recent
text in that conference will be marked as unread upon entering the 
conference.

If a cons cell (CONF-NO . NUMBER) is in the list, the NUMBER most 
recent texts (approximately) will be marked as unread on entering
conference CONF-NO."
  server)

(defun kom-list-summary-hook (from-conf to-conf)
  "Automatically list text summary when entering a conference. This
function can be added to kom-after-change-conf-hook. Which conferences to
list subjects in is controlled by kom-list-summary-hook-conf-list"
  (when (or (eq kom-list-summary-hook-conf-list t)
            (memq to-conf kom-list-summary-hook-conf-list))
    (lyskom-list-summary to-conf)))

(defun kom-read-last-text-hook (from-conf to-conf)
  "Automatically read only the most recent N texts in a conference.
This function can be added to kom-change-conf-hook. Which conferences
to perform this in is controlled by kom-read-last-text-hook-conf-list.

Not that even unread texts may be read again when usign this function."
  (let* ((spec (or (and (memq to-conf kom-read-last-text-hook-conf-list) t)
                   (assq to-conf kom-read-last-text-hook-conf-list)))
         (arg (and spec (if (consp spec) (cdr spec) 1))))
    (when (and arg (blocking-do 'set-unread to-conf arg))
      (let ((membership (blocking-do 'query-read-texts lyskom-pers-no 
                                     to-conf t 0))
            (read-info nil))
        (lyskom-replace-membership membership)
        (read-list-delete-read-info to-conf lyskom-to-do-list)
        (read-list-delete-read-info to-conf lyskom-reading-list)

        (setq read-info
              (lyskom-create-read-info 'CONF
                                       (blocking-do 'get-conf-stat to-conf)
                                       (lyskom-get-current-priority)
                                       (listify-vector
                                        (map->text-nos 
                                         (blocking-do 'get-map 
                                                      to-conf
                                                      (membership->last-text-read membership)
                                                      lyskom-max-int)))))
        (read-list-enter-first read-info lyskom-to-do-list)
        (read-list-enter-first read-info lyskom-reading-list)))))
;;;;; -*-coding: iso-8859-1;-*-
;;;;;
;;;;; Copyright (C) 1991-2002  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM Emacs LISP client.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
;;;;; the Free Software Foundation; either version 2, or (at your option) 
;;;;; any later version.
;;;;; 
;;;;; LysKOM is distributed in the hope that it will be useful, but WITHOUT
;;;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;;;;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;;; for more details.
;;;;; 
;;;;; You should have received a copy of the GNU General Public License
;;;;; along with LysKOM; see the file COPYING.  If not, write to
;;;;; Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
;;;;; or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
;;;;; MA 02139, USA.
;;;;;
;;;;; Please mail bug reports to bug-lyskom@lysator.liu.se. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
;;;; File: lyskom-rest.el
;;;;
;;;; This is the rest of what was once the entire source code of the
;;;; client. Now most of the source resides in other files. Eventually
;;;; the functions in here will be distributed out to other files.
;;;; Below follows the original header of the LysKOM elisp client:
;;;;
;;;; ================================================================
;;;;
;;;; Simple LysKOM elisp client.
;;;;
;;;; Originally written by Per Cederqvist.
;;;;
;;;; Heavily hacked by:
;;;;    Thomas Bellman
;;;;    Linus Tolke
;;;;    Inge Wallin
;;;;    David K}gedal
;;;;    David Byers
;;;;    Hans Eric Svensson
;;;;   and others.
;;;;
;;;; Some ideas stolen from lpmud.el written by Lars Willf|r and Thomas Bellman
;;;;
;;;; Conventions:
;;;;
;;;;    All functions, variables etc follow one of these conventions:
;;;;		lyskom-		general functions.
;;;;		cache-		cache-routines.
;;;;		initiate-	call a service. (Might look up in cache)
;;;;		kom-		user kommands.
;;;;		lyskom-parse-	parse (part of) a reply from the server.
;;;;				these are found in parse.el
;;;;		lyskom-edit-	Things related to the editing of texts.
;;;;		{lyskom,kom}-edit-options-
;;;;				things related to editing options
;;;;				(found in flags.el)
;;;;		{lyskom,kom}-prioritize-
;;;;				things related to the prioritize
;;;;				buffer. The file is prioritize.el
;;;;		{lyskom,kom}-{completing-read,completing,complete}-
;;;;				things related to
;;;;				lyskom-completing-read. Found in
;;;;				completing-read.el 
;;;;		lyskom-*-hook   hooks.
;;;;
;;;;    Three plus-signs (+++) in the code marks a place where more
;;;;    work is needed.
;;;;

(eval-when-compile
  (require 'lyskom-command "command")
  (require 'lyskom-menus "menus")
  (require 'lyskom-clienttypes "clienttypes.el"))


;;;; ================================================================
;;;;                             New errors.


(put 'lyskom-protocol-error 'error-conditions
     '(error lyskom-error lyskom-protocol-error))
(put 'lyskom-protocol-error 'error-message
     "LysKOM protocol error")

(put 'lyskom-internal-error 'error-conditions
     '(error lyskom-error lyskom-internal-error))
(put 'lyskom-internal-error 'error-message
     "Internal LysKOM error")

(put 'lyskom-format-error 'error-conditions
    '(error lyskom-error lyskom-format-error))
(put 'lyskom-internal-error 'error-message
     "Internal LysKOM format error")

(put 'lyskom-error 'error-conditions
     '(error))
(put 'lyskom-error 'error-message
     "LysKOM error")


;;; ================================================================
;;;             Global variables
;;;

(defvar lyskom-unread-mode-line nil)
(defvar lyskom-unread-title-format nil)

;;; ================================================================
;;;             Error reporting from a number of commands.
;;;

;;; Author: Inge Wallin



(defun lyskom-get-error-text (errno &optional error-descriptions)
  "Get a string which is the error ERRNO in plain text."
  (let ((custom-error
         (or (cdr (assq errno error-descriptions))
             (cdr (assq (car (rassq errno lyskom-error-codes))
                        error-descriptions)))))
    (if custom-error
        (lyskom-get-string custom-error)
      (or
       (lyskom-try-get-string (intern (concat "error-"
                                              (number-to-string errno)))
                              'lyskom-error-texts)
       (lyskom-format 'error-not-found errno)))))

(defun lyskom-report-command-answer (answer &optional errno error-descriptions)
  "Handles a void return from call to the server."
  (if answer 
      (lyskom-insert-string 'done)
    (lyskom-insert-string 'nope)
    (lyskom-insert-error errno nil error-descriptions))
  answer)

(defun lyskom-insert-error (&optional errno err-stat error-descriptions)
  "Insert an error message describing ERRNO and ERR-STAT.
If ERRNO and ERR-STAT are not supplied, use lyskom-errno and
lyskom-err-stat instead.  If only ERRNO is supplied, use the empty
string for ERR-STAT.

If ERROR-DESCRIPTIONS is supplied, it should be an alist with error numbers
as the cars and error descriptions (symbols) as the cdr. The symbol will
be used to get a description of the corresponding error."
  (lyskom-format-insert 'error-code
                        (lyskom-get-error-text (or errno lyskom-errno)
                                               error-descriptions)
                        (or errno lyskom-errno)
                        (or err-stat (if errno "" lyskom-err-stat))))

(defun lyskom-current-error ()
  "Return a string describing the current error"
  (lyskom-format 'error-code
		 (lyskom-get-error-text lyskom-errno)
		 lyskom-errno
		 lyskom-err-stat))


;;; ----------------------------------------------------------------
;;; Author: Aronsson

(defun lyskom-tell-internat (key)
  "Same as lyskom-tell-server, but use a key to a list of phrases."
  ;; The server at Lysator produces a lot of network traffic. The
  ;; solution should to be less verbose. This is very interesting
  ;; anymore, anyway.
  (lyskom-tell-server (lyskom-tell-string key)))



;;; Resume operation after a crash.

(defun kom-recover (&optional refetch)
  "Try to recover from an error.
If the optional argument REFETCH is non-nil or the funcion is called
interactively, all caches are cleared and the membership is re-read."
  (interactive (list t))
  (lyskom-init-parse lyskom-buffer)
  (setq lyskom-call-data nil)
  (setq lyskom-pending-calls nil)
  (setq lyskom-output-queues (make-vector 10 nil))
  (let ((i 0))
    (while (< i 10)
      (aset lyskom-output-queues i (lyskom-queue-create))
      (++ i)))
  (setq lyskom-number-of-pending-calls 0)
  (setq lyskom-is-parsing nil)
  (when refetch
    (clear-all-caches)
    (lyskom-refetch))
  (lyskom-tell-internat 'kom-tell-recover)
  (lyskom-end-of-command))


;;; ================================================================
;;;                      Run default command.


;;; Author: Tommy Persson

(defun kom-page-next-command ()
  "Scroll the bottom to the top and run the next command if it should be run"
  (interactive)
  (if (lyskom-pos-visible-in-window-p (point-max))
      (progn
	(goto-char (point-max))
	(recenter 0)
        (sit-for 0)                     ; FIXME: emacs-21.0.94 workaround
	(lyskom-next-command))
    (recenter 0)
    (sit-for 0)                     ; FIXME: emacs-21.0.94 workaround
    (move-to-window-line -1)
    (lyskom-set-last-viewed)))


;;; Author: Lars Willf|r

(defun kom-next-command ()
  "Run next command or scroll one page."
  (interactive)
  (if (lyskom-pos-visible-in-window-p (point-max))
      (lyskom-next-command)
    (move-to-window-line -1)
    (lyskom-set-last-viewed)
    (lyskom-scroll)
    (if (< (window-start) lyskom-last-viewed)
        (lyskom-xemacs-or-gnu
         (let ((overlay (make-extent 
                         lyskom-last-viewed
                         (save-excursion (goto-char lyskom-last-viewed)
                                         (end-of-line)
                                         (forward-char 1)
                                         (point)))))
           (set-extent-priority overlay 1000)
           (set-extent-face overlay kom-mark-face)
           (add-timeout 2 
                        'delete-extent
                        overlay))
         (let ((overlay (make-overlay 
                         lyskom-last-viewed
                         (save-excursion (goto-char lyskom-last-viewed)
                                         (end-of-line)
                                         (forward-char 1)
                                         (point)))))
           (overlay-put overlay 'face kom-mark-face)
           (overlay-put overlay 'priority 1000)
           (run-at-time 2 nil
                        'delete-overlay
                        overlay))))))

  
(defun kom-line-next-command ()
  "Run next command or scroll one line."
  (interactive)
  (if (lyskom-pos-visible-in-window-p (point-max))
      (lyskom-next-command)
    (move-to-window-line 1)
    (lyskom-set-last-viewed)
    (lyskom-scroll)))


;;; Author: Per Cederqvist

(defun lyskom-next-command ()
  "Run next command."
  (let ((saved-buffer (current-buffer)))
    (unwind-protect 
        (progn
          (setq lyskom-doing-default-command t)
          (cond
           ((eq lyskom-command-to-do 'next-pri-text)
            (lyskom-view-priority-text))
           ((eq lyskom-command-to-do 'next-text)
            (kom-view-next-text))
           ((eq lyskom-command-to-do 'reedit-text)
            (kom-re-edit-next-text))
           ((eq lyskom-command-to-do 'next-pri-session)
            (kom-go-to-pri-session))
           ((eq lyskom-command-to-do 'next-conf)
            (kom-go-to-next-conf))
           ((eq lyskom-command-to-do 'next-pri-conf)
            (lyskom-go-to-pri-conf))
           ((eq lyskom-command-to-do 'when-done)
            (let ((command (lyskom-what-to-do-when-done)))
              (cond
               ((or (stringp command)
                    (vectorp command))
                (execute-kbd-macro command))
               ((commandp command) (call-interactively command))
               (t (lyskom-start-of-command nil) (lyskom-end-of-command)))))
           ((eq lyskom-command-to-do 'unknown)
            (lyskom-insert
             (lyskom-get-string 'wait-for-server)))
           (t (signal 'lyskom-internal-error '(kom-next-command)))))
      (condition-case nil
          (save-current-buffer
            (set-buffer saved-buffer)
            (setq lyskom-doing-default-command nil))
        (error nil)))))


;;; ================================================================
;;;                              View text.


(defun kom-initial-digit-view ()
  (interactive)
  (setq unread-command-events (cons last-command-event
                                    unread-command-events))
  (call-interactively 'kom-view nil))


(defun kom-view (text-no)
  "View text number TEXT-NO."
  (interactive "P")
  (unwind-protect
      (progn
	(let ((kom-page-before-command nil))
	  (lyskom-start-of-command 'kom-view)
	  (lyskom-tell-internat 'kom-tell-review))
	(unless (and (null current-prefix-arg)
		     (lyskom-plusp text-no))
	  (let ((current-prefix-arg text-no))
	    (setq text-no (lyskom-read-text-no-prefix-arg 'review-text-q))))

        (cond (text-no
               (when (or (not (listp kom-page-before-command))
                         (memq 'kom-view kom-page-before-command))
                 (recenter 1)
                 (sit-for 0)                     ; FIXME: emacs-21.0.94 workaround
                 )
               (lyskom-tell-internat 'kom-tell-review)
               (lyskom-format-insert 'review-text-no text-no)
               (unless kom-review-uses-cache
                 (cache-del-text-stat text-no))
               (lyskom-view-text text-no))
              (t (lyskom-insert 'confusion-what-to-view))))
    (lyskom-end-of-command)))

(defun kom-initial-digit-unread ()
  (interactive)
  (setq unread-command-events (cons last-command-event
                                    unread-command-events))
  (call-interactively 'kom-mark-unread nil))

(def-kom-command kom-mark-unread (text-no)
  "Make a single text unread. This command is functional only on servers
that support making texts unread."
  (interactive (list (lyskom-read-text-no-prefix-arg 'unread-text-q)))
  (lyskom-format-insert 'marking-text-unread text-no)
  (lyskom-report-command-answer (lyskom-mark-unread text-no)))

(defun lyskom-mark-unread (text-no)
  "Mark text TEXT-NO in all conferences.
Returns non-nil if successful."
  (let ((text-stat (blocking-do 'get-text-stat text-no))
        (marked-unread nil))
    (and text-stat
         (let ((result
                (lyskom-traverse misc-info (text-stat->misc-info-list text-stat)
                  (when (memq (misc-info->type misc-info) lyskom-recpt-types-list)
                    (let* ((mship (lyskom-get-membership (misc-info->recipient-no misc-info)))
                           (recipient (and mship (blocking-do 'get-conf-stat (misc-info->recipient-no misc-info)))))
                      (when (and mship recipient)
                        (if (not (blocking-do 'mark-as-unread
                                              (misc-info->recipient-no misc-info)
                                              (misc-info->local-no misc-info)))
                            (lyskom-traverse-break 'error)
                          (when (lyskom-visible-membership mship)
                            (setq marked-unread t)
                            (unless (read-list-enter-text text-no 
                                                          recipient
                                                          lyskom-to-do-list)
                              (let ((info (lyskom-create-read-info
                                           'CONF
                                           recipient
                                           (membership->priority mship)
                                           (lyskom-create-text-list (list text-no)))))
                                (read-list-enter-read-info info lyskom-to-do-list)
                                (if (= lyskom-current-conf (conf-stat->conf-no recipient))
                                    (read-list-enter-read-info info lyskom-reading-list))))))))))))
           (cond (result nil)
                 (marked-unread t)
                 (t (setq lyskom-errno -1)
                    nil))))))



;;;; ================================================================
;;;;                          Re-edit text

(def-kom-command kom-re-edit-next-text ()
  "Display a buffer containing a failed text submission. This pseudo
command is only invoked from the prompt after submission of a text
failed."
  (interactive)
  (let ((el (read-list->first lyskom-reading-list)))
    (set-read-list-del-first lyskom-reading-list)
    (if (buffer-live-p (read-info->misc el))
        (lyskom-display-buffer (read-info->misc el))
      (lyskom-format-insert 'text-buffer-missing))))


;;;; ================================================================
;;;;                        Go to pri session

(def-kom-command kom-go-to-pri-session ()
  "Go to a prioritized session with unreads. This pseudo command is
generally invoked from the prompt when there is a session with unread
texts that has a higher priority than the current session.

See `kom-server-priority' and `kom-server-priority-breaks' for
settings to control session priorities."
  (interactive)
  (let ((session (lyskom-get-prioritized-session)))
    (if (or (null session)
            (eq session (current-buffer))
            (not (buffer-live-p session)))
        (lyskom-insert 'no-such-kom-session)
      (lyskom-switch-to-kom-buffer session))))


;;;; ================================================================
;;;;                         View next text.


(defun kom-view-next-new-text ()
  "Display next new text (putting all review constructs at the end of this conf)."
  (interactive)
  (let ((len (read-list-length lyskom-reading-list))
	(finished nil))
    (while (and (not finished)
		(> len 1))
      (let ((type (read-info->type (read-list->first lyskom-reading-list))))
	(cond 
	 ((memq type lyskom-review-types-list)
	  (read-list-rotate lyskom-reading-list))
	 ((memq type lyskom-comment-types-list)
	  (set-read-list-del-first lyskom-reading-list))
	 (t 
	  (setq finished t))))
      (-- len)))
  (kom-view-next-text))


;;; Modified to handle filters

;; This is horribly ugly. It acts like a user command, but it isn't.
(defun lyskom-view-priority-text ()
  "Display the first text from the next conference on the lyskom-to-do-list.

Cannot be called from a callback."
  (lyskom-start-of-command 'kom-view-next-text)
  (unwind-protect
      (progn
	(lyskom-tell-internat 'kom-tell-read)
	(let* ((tri (read-list->first lyskom-to-do-list))
	       (priority (read-info->priority
			  (read-list->first lyskom-reading-list)))
	       (text-no (car (text-list->texts (read-info->text-list tri)))))
	  (lyskom-is-read text-no)
	  (lyskom-view-text text-no t nil (read-info->conf-stat tri) 
			    priority nil t))
	(lyskom-wait-queue 'main))
    (lyskom-end-of-command)))


;;; ========================================================================
;;; Functions to get the "best" text and the "least" text and their
;;; helper functions.

;;; Author: Hans Eric Svensson

(defun lyskom-member-of-at-least-one-p (confs)
  "Returns NIL if user is not a member of any of the conferences in
CONFS (list of conference numbers), otherwise a conference number."
   (lyskom-traverse conf confs
     (when (lyskom-get-membership conf t)
       (lyskom-traverse-break conf))))

(defun lyskom-cache-all-memberships (confs)
  "Loads the memberships of all of the conferences in CONFS (list of
conference numbers) into cache (mostly needed to load passive
memberships into cache)."
   (lyskom-traverse conf confs (lyskom-get-membership conf t)))

(defun lyskom-member-one-priority-p (confs)
  "Find out if the maximum priority of the conferences in CONFS (list
of conference numbers) is greater than or equal to the user's session
priority which is given by `lyskom-session-priority' (i.e. if the user
will read a text with the set of conferences as the only recipients).

Returns NIL if user will not read texts to any of the conferences in
CONFS, given the current session priority, T otherwise."
(>= (or (lyskom-get-max-priority confs) -1) lyskom-session-priority))

(defun lyskom-up-to (list element)
  "Destructively remove elements in LIST from the first ELEMENT."
  (let ((tem list) (prev nil))
    (while tem
      (when (eq element (car tem))
        (if prev (setcdr prev nil) (setq tem nil list nil)))
      (setq prev tem tem (cdr tem)))
    list))

(defun lyskom-find-best-text (text-nos came-from loop-as-accept)
"Find the \"best\" text number to read next starting from the text
numbers TEXT-NOS (and CAME-FROM).

A text T2 is better to read than a text T1 if:

1. T1 is a comment or footnote to T2.
2. T2 is unread.
3. The recipients of T2 contain at least one conference of which the
   user is a member.
4. The recipients of T2 of which the user is a member has at least one
   priority which is higher than or equal to the current session
   priority.

-or-

1. T1 is a comment or footnote to the text T3.
2. T2 is a comment or footnote to T3.
3. T2 is unread.
4. T3 is read.
5. T2 commented T3 before T1 commented T3.
6. The recipients of T2 contain at least one conference of which the
   user is a member.
7. The recipients of T2 of which the user is a member has at least one
   priority which is higher than or equal to the current session
   priority.

We also keep track of which text numbers have previously been
considered to detect loops. If a loop is detected and LOOP-AS-ACCEPT
is NIL, NIL is returned. Otherwise, the text number which was
determined to be a part of the loop is returned. CAME-FROM is the text
number of the text which was used to acquire TEXT-NOS. It is only used
when TEXT-NO is a read text to filter out the comments to TEXT-NO
which were made after CAME-FROM (as well as CAME-FROM)."
  (let ((stack (list
                (list text-nos (list came-from) came-from
                      loop-as-accept came-from)))
        (result nil))
    (while (and (not result) stack)
      (let* ((params (car stack))
             (text-nos (nth 0 params)))
        (if (null text-nos)
            (setq result (nth 4 params)
                  stack (cdr stack))
          (let* ((text-no (car text-nos))
                 (visited (nth 1 params))
                 (loop-as-accept (nth 3 params))
                 (text-stat (blocking-do 'get-text-stat text-no))
                 (confs (lyskom-text-recipients text-stat))
                 (but-current (delq lyskom-current-conf confs))
                 (is-member (lyskom-member-of-at-least-one-p but-current))
                 (tmp (if is-member (lyskom-cache-all-memberships confs)))
                 (is-read (lyskom-text-read-p text-stat t)))
            (setcar params (cdr text-nos))
            (cond ((not text-stat)) ; oops!
                  ((memq text-no visited) ; loop detection
                   (and (not is-read) loop-as-accept text-no))
                  (is-read
                   (let* ((came-from (nth 2 params))
                          (more-text-nos (lyskom-up-to
                                          (lyskom-text-comments text-stat)
                                          came-from)))
                     (setq stack (cons (list more-text-nos
                                             (cons text-no visited)
                                             text-no nil nil)
                                       stack))))
                  ((not (or is-read is-member)) ; outside membership
                   (let*
                       ((more-text-nos (lyskom-text-stat-commented-texts
                                        text-stat))
                        (came-from (nth 2 params))
                        (even-more-text-nos (lyskom-up-to
                                             (lyskom-text-comments text-stat)
                                             came-from)))
                     (setq stack (cons (list more-text-nos
                                             (cons text-no visited)
                                             text-no
                                             loop-as-accept
                                             nil)
                                       (cons (list even-more-text-nos
                                                   (cons text-no visited)
                                                   text-no nil nil)
                                             stack)))))
                  (t (let*
                         ((more-text-nos (lyskom-text-stat-commented-texts
                                          text-stat))
                          (text-no-if-fail (and
                                            is-member
                                            (lyskom-member-one-priority-p
                                             but-current)
                                            text-no)))
                     (setq stack (cons (list more-text-nos
                                             (cons text-no visited)
                                             text-no
                                             loop-as-accept
                                             text-no-if-fail)
                                       stack)))))
            (unless kom-review-uses-cache
              (cache-del-text-stat text-no))))))
    result))

(defsubst lyskom-minimum (list)
  "Return the least element in the list LIST."
  (and list (apply #'min list)))

(defun lyskom-find-lowest-text (text-no)
  "Return the \"lowest\" text to read next, starting from the text
number TEXT-NO.

A text T2 is lower than a text T1 if:

1. The text number of T2 is less than the text number of T1.
2. T2 is unread.

We consider all of the texts reachable from TEXT-NO."

  ;; We keep track of which text numbers have previously been considered in
  ;; VISITED to detect loops and save all of the candidates in CANDIDATES.

  (let* ((visited nil)
         (candidates nil)
         (consider (list text-no)))
    (while consider
      (let* ((text-no (car consider))
             (text-stat (blocking-do 'get-text-stat text-no))
             (confs (lyskom-text-recipients text-stat))
             (is-member (lyskom-member-of-at-least-one-p confs))
             (tmp (if is-member (lyskom-cache-all-memberships confs)))
             (is-read (lyskom-text-read-p text-stat t))
             (more-text-nos (lyskom-text-stat-commented-texts text-stat))
             (even-more-text-nos (lyskom-text-comments text-stat)))
        (setq consider (cdr consider))
        (if is-member
            (unless (or is-read (memq text-no visited))
              (setq candidates (cons text-no candidates))
              (if consider
                  (nconc consider more-text-nos even-more-text-nos)
                (setq consider
                      (nconc consider more-text-nos even-more-text-nos)))))
        (setq visited (cons text-no visited))
        (unless kom-review-uses-cache
          (cache-del-text-stat text-no))))
    (lyskom-minimum candidates)))

(defun lyskom-find-unread-ancestor (text-no)
  "Look for unread texts that TEXT-NO is a comment or footnote to.
This function considers all parents of TEXT-NO.  If they fulfill all
of the following requirements they are kept as candidates for further
processing:

 - the current user is an active member of at least one recipient of
   the text.
 - the text is unread.

The parents of each candidate is in turn examined.  The
lowest-numbered candidate is returned."

  ;; We keep track of which text numbers have previously been considered in
  ;; VISITED to detect loops and save all of the candidates in CANDIDATES.

  (let* ((visited nil)
         (candidates (list text-no))
         (consider (list text-no)))
    (while consider
      (let* ((text-no (car consider))
             (text-stat (blocking-do 'get-text-stat text-no))
             (confs (lyskom-text-recipients text-stat))
             (is-member (lyskom-member-of-at-least-one-p confs))
             (tmp (if is-member (lyskom-cache-all-memberships confs)))
             (is-read (lyskom-text-read-at-least-once-p text-stat)))
        (setq consider (cdr consider))
	(when (not (memq text-no visited))
	  (setq visited (cons text-no visited))
	  (when (and is-member (not is-read))
	    (setq candidates (cons text-no candidates))
	    (setq consider
		  (nconc consider
			 (lyskom-text-stat-commented-texts text-stat)))))))
    (lyskom-minimum candidates)))

(defun lyskom-get-max-priority (confs)
"Return the maximum priority of the conferences in CONFS (list of
conference numbers). If the list is empty or if the user is not a
member of any of the conferences NIL is returned."
  (let ((priorities (delq nil (mapcar
                               (lambda (conf-no)
                                 (membership->priority
                                  (lyskom-get-membership conf-no t)))
                               confs))))
    (unless (null priorities) (apply #'max priorities))))

;;;; ================================================================
;;;;                         View next text (cont'd.)

;;; Modified to handle filters

;;; Modified to read commented texts first (Hans Eric Svensson)

(defun lyskom-select-text-to-read (text-no-maybe text-stat-maybe)
  "Select the next text to read."

  (cond

   ((eq kom-read-related-first nil)
    text-no-maybe)

   ((eq kom-read-related-first 'oldest-relative)
    (if kom-read-depth-first
	(lyskom-find-best-text (lyskom-text-stat-commented-texts
				text-stat-maybe)
			       text-no-maybe t)
       (lyskom-find-lowest-text text-no-maybe)))

   ((eq kom-read-related-first 'oldest-ancestor)
    (lyskom-find-unread-ancestor text-no-maybe))

   (t
    (error "Unexpected value for kom-read-related-first"))))

(def-kom-command kom-view-next-text ()
  "Display the next unread text. This is the most common default command."
  (interactive)
  (lyskom-tell-internat 'kom-tell-read)
  (let ((action 'next-text))
    (while (eq action 'next-text)
      (if (read-list-isempty lyskom-reading-list)
	  (progn
	    (if (/= 0 lyskom-current-conf)
		(lyskom-insert-string 'completely-read-conf)
	      (lyskom-insert-string 'not-in-any-conf))
	    (setq action nil))
	      
	(progn
	  (let* ((tri (read-list->first lyskom-reading-list))
		 (type (read-info->type tri))
		 (is-review-tree (memq type '(REVIEW-TREE)))
		 (is-review (or (memq type '(REVIEW REVIEW-MARK REVIEW-FAQ))
				is-review-tree))
                 (is-reading (memq type '(CONF COMM-IN FOOTN-IN)))
		 (text-no-maybe (car (text-list->texts (read-info->text-list
                                                        tri))))
                 (text-stat-maybe (blocking-do 'get-text-stat text-no-maybe))

                 (text-no (if is-reading
			      (lyskom-select-text-to-read
			       text-no-maybe text-stat-maybe)
                            text-no-maybe))

                 (text-stat (blocking-do 'get-text-stat text-no))
                 
                 (priority (read-info->priority tri))
                 (conf-stat (read-info->conf-stat tri))
                 
		 (mark-as-read (not is-review)))
	    (when (or is-review (eq type 'REVIEW-FAQ-TREE))
              (set-text-list->texts
               (read-info->text-list tri)
               (delq text-no (text-list->texts 
                              (read-info->text-list tri)))) ;First entry only
              (unless kom-review-uses-cache
                (cache-del-text-stat text-no)))
	    (setq action
		  (lyskom-view-text text-no mark-as-read 
				    (and kom-read-depth-first
					 (not is-review))
				    conf-stat
				    priority
				    is-review-tree
				    (not is-review)
				    (memq type '(REVIEW REVIEW-MARK))))
	    (if mark-as-read
		(lyskom-is-read text-no)
	      (read-list-delete-text nil lyskom-reading-list)
	      (read-list-delete-text nil lyskom-to-do-list))))))))


(defun lyskom-is-read (text-no)
  "Remove TEXT-NO from the list of texts to read.
Deletes TEXT-NO from lyskom-reading-list and lyskom-to-do-list.
Adds info in lyskom-membership.

This function only modifies the internal state of the client. It does
not mark the text as read in the server. That function is performed by
lyskom-mark-as-read."
  (read-list-delete-text text-no lyskom-reading-list)
  (read-list-delete-text text-no lyskom-to-do-list)
  (initiate-get-text-stat 'main 'lyskom-is-read-handler text-no)
  (setq lyskom-normally-read-texts (cons text-no lyskom-normally-read-texts)))


(defun lyskom-is-read-handler (text-stat)
  "Update lyskom-membership for all recipients to TEXT-STAT."
  (lyskom-traverse
      misc (text-stat->misc-info-list text-stat)
    (if (memq (misc-info->type misc) '(RECPT BCC-RECPT CC-RECPT))
	(let ((membership (lyskom-try-get-membership
			   (misc-info->recipient-no misc) t)))
	  (when membership
            (set-membership->read-texts
             membership
             (vconcat (vector (misc-info->local-no misc))
                      (membership->read-texts membership))))))))


;;; ================================================================
;;;                        Go to next conf.


(def-kom-command kom-go-to-next-conf (&optional num)
  "Go to next conference with unread texts. The current conference is
moved to the end of the list of conferences with unread texts. A
numeric prefix argument is interpreted similar to in `kom-list-news':
with a positive argument go to the next conference with at least that
many unread. With a negative number, go to a conference with at most
that many unread texts."
  (interactive "P")
  (let ((conf-stat nil)
	(num-arg (cond
		  ((numberp num) num)
		  ((and (listp num)
			(numberp (car num))) (car num))
		  (t nil))))
    (when num-arg
      (let ((read-list (cdr (lyskom-list-news)))
	    (msgs-conf nil)
	    (unreads   nil)
	    (at-least  (if  (< num-arg 0) 1 num-arg))
	    (at-most   (and (< num-arg 0) (- num-arg))))
	(while (and read-list (null conf-stat))
	  (setq msgs-conf (car read-list))
	  (setq read-list (cdr read-list))
	  (setq unreads (car msgs-conf))
	  (when (and (>= unreads at-least) ; unreads within lower bound
		     (or (not at-most)
			 (<= unreads at-most))) ; unreads within upper bound
	    (setq conf-stat (cdr msgs-conf))))))
    (if (and num-arg (null conf-stat))
	(lyskom-insert-string 'no-unreads-shown)
      (when (lyskom-check-go-to-conf conf-stat)
	(lyskom-maybe-move-unread t)
	(if conf-stat
	    (lyskom-go-to-conf conf-stat)
	  (lyskom-go-to-next-conf))))))


(defun lyskom-go-to-pri-conf ()
  "Go to the conf with higher priority.
This differs from kom-go-to-next-conf only in the place where the yet unread 
in the current conf is placed."
  (lyskom-start-of-command 'kom-go-to-next-conf)
  (when (lyskom-check-go-to-conf)
    (lyskom-maybe-move-unread nil)
    (lyskom-go-to-next-conf)
    (lyskom-end-of-command)))


(defun lyskom-go-to-next-conf ()
  "Actually go to the next conference on the lyskom-to-do-list"
  ;; Copy first element on lyskom-to-do-list to lyskom-reading-list.
  (if (not (read-list-isempty lyskom-to-do-list))
      (let ((conf-stat (read-info->conf-stat 
                        (read-list->first lyskom-to-do-list))))
        (if conf-stat
            (progn (lyskom-set-mode-line conf-stat)
                   (read-list-enter-first
                    (read-list->first lyskom-to-do-list)
                    lyskom-reading-list)
                   (lyskom-enter-conf conf-stat 
                                      (read-list->first lyskom-to-do-list)))
          (read-list-enter-first
           (read-list->first lyskom-to-do-list)
           lyskom-reading-list)))
    (lyskom-insert-string 'all-conf-unread-r)
    (lyskom-set-mode-line (lyskom-get-string 'all-conf-unread-s))))


(defun lyskom-maybe-move-unread (bury)
  "Empty the reading list.
If the argument BURY is non-nil and there are unread articles left in the
reading list then the conf is inserted last in the to do list."
  (if (not (read-list-isempty lyskom-reading-list))
      (progn
	(if bury
	    (let ((conf-no nil)
		  (r 0))
	      (while (and (not conf-no)
			  (< r (read-list-length lyskom-reading-list)))
		(if (eq (read-info->type (read-list->nth lyskom-reading-list 
							 r))
			'CONF)
		    (setq conf-no (conf-stat->conf-no 
				   (read-info->conf-stat
				    (read-list->nth lyskom-reading-list r))))
		  (++ r)))
	      (if conf-no
		  (progn
		    (read-list-delete-read-info conf-no lyskom-to-do-list)
		    (read-list-enter-first (read-list->nth lyskom-reading-list
							   r)
					   lyskom-to-do-list)
		    (read-list-rotate lyskom-to-do-list)))))
	(set-read-list-empty lyskom-reading-list))))


(defun lyskom-check-go-to-conf (&optional conf)
  "Check if it is OK to go to conf CONF (a conf-stat).
It is assumed that lyskom-pers-no is a member of the conference.
This function interacts with the buffer and the user.

If CONF is nil, check the first conf on the to-do list."
  (let* ((conf-stat (or conf
                        (read-info->conf-stat 
                         (read-list->first lyskom-to-do-list))))
         (mship (and conf-stat 
                     (lyskom-get-membership (conf-stat->conf-no conf-stat) t)))
         (type nil)
         (continue t))
    (when (and conf-stat mship)
      (setq type (membership->type mship))

      (when (membership-type->invitation type)
        ;;; State the invitation,give the user a chance to accept it
        ;;; Leave the conf if the user does not want it
        (lyskom-format-insert 'your-invited
                              conf-stat
                              (membership->created-by mship))
        (if (lyskom-j-or-n-p (lyskom-get-string 'accept-invitation))

            (setq continue (lyskom-add-member (blocking-do 'get-conf-stat lyskom-pers-no) conf-stat))
          (let ((kom-unsubscribe-makes-passive nil))
            (lyskom-sub-member (blocking-do 'get-conf-stat lyskom-pers-no)
                               conf-stat)
            (setq continue nil))))

      (when (and continue (membership-type->secret type))
        (lyskom-format-insert-before-prompt 'bug-secret-mship conf-stat))

      (when (and continue (membership-type->passive type))
        ;;; Offer the user a chance to become an active member
        ;;; If the offer is refused, boot the user off the conference
        (lyskom-format-insert 'enter-passive conf-stat)
        (setq continue nil)
        (if (lyskom-j-or-n-p (lyskom-format 'convert-passive conf-stat))
            (setq continue 
                  (lyskom-add-member (blocking-do 'get-conf-stat lyskom-pers-no) conf-stat))
          (setq continue nil)))
      )

    ;;; Return the result
    continue))

(defun lyskom-reject-recommendation (conf-no)
  "Record that we have rejected an invitation to CONF-NO 
by PERS-NO"
  (unless (lyskom-recommendation-already-rejected conf-no)
    (setq lyskom-rejected-recommendations
          (cons conf-no lyskom-rejected-recommendations))
    (initiate-modify-conf-info 
     'background
     nil
     lyskom-pers-no
     nil
     (list (lyskom-create-aux-item 0 10001 nil nil
                                   (lyskom-create-aux-item-flags nil nil t nil
                                                                 nil nil nil nil)
                                   0
                                   (format "%d" conf-no))))))

(defun lyskom-recommendation-already-rejected (conf-no)
  "Return non-nil if we have rejected recommendation for CONF-NO"
  (member conf-no lyskom-rejected-recommendations))

(defun lyskom-update-rejected-recommendations ()
  "Update the list of rejected recommendations"
  (setq lyskom-rejected-recommendations nil)
  (let* ((conf-stat (blocking-do 'get-conf-stat lyskom-pers-no))
         (aux-list (and conf-stat (lyskom-get-aux-item
                                   (conf-stat->aux-items conf-stat) 
                                   10001))))
    (lyskom-traverse item aux-list
      (when (string-match "^\\([0-9]+\\)" (aux-item->data item))
        (setq lyskom-rejected-recommendations
              (cons (lyskom-string-to-number (match-string 1 (aux-item->data item)))
                    lyskom-rejected-recommendations))))))

(defun lyskom-startup-check-recommended-memberships ()
  "Create invitations for new membership recommendations"
  (let ((recommendations 
         (lyskom-filter-list
          (lambda (rec)
            (not (lyskom-recommendation-already-rejected (car rec))))
          (mapcar (lambda (aux)
                    (when (string-match "^\\([0-9]+\\)\\( [0-9]+\\)?\\( [01]+\\)?" (aux-item->data aux))
                      (let* ((conf-no (lyskom-string-to-number (match-string 1 (aux-item->data aux))))
                             (priority (and conf-no (match-string 2) (lyskom-string-to-number (match-string 2 (aux-item->data aux)))))
                             (mship-type (and priority (match-string 3) (match-string 3 (aux-item->data aux)))))
                        (list conf-no priority mship-type (aux-item->creator aux)))))
                  (lyskom-get-aux-item (server-info->aux-item-list
                                        lyskom-server-info) 29))))
        (collector (make-collector)))
    ;; At this point RECOMMENDATIONS contains a list with one element
    ;; per previously not rejected conference membership recommendations
    ;; (including the ones we are actually members of).

    (lyskom-traverse rec recommendations
      (initiate-query-read-texts 'main
                                 'collector-push
                                 lyskom-pers-no
                                 (car rec)
                                 nil 0
                                 collector))
    (lyskom-wait-queue 'main)

    ;; Now delete recommendations that we are already members of

    (lyskom-traverse mship (collector->value collector)
      (when mship
        (setq recommendations
              (delq (assq (membership->conf-no mship) recommendations)
                    recommendations))))

    ;; At this point recommendations contains only the conferences
    ;; that we have not rejected and that we are not already members
    ;; of.

    (let ((pers-conf-stat (blocking-do 'get-conf-stat lyskom-pers-no)))
      (lyskom-traverse rec recommendations
        (let ((conf-stat (blocking-do 'get-conf-stat (elt rec 0))))
          (when conf-stat
            (lyskom-format-insert 'has-recommended-conf
                                  (elt rec 3)
                                  (elt rec 0))
            (if (lyskom-j-or-n-p (lyskom-format 'accept-recommendation (elt rec 0)))
                (lyskom-add-member pers-conf-stat conf-stat nil nil 
                                   (and (elt rec 1) (list (elt rec 1))) nil)
              (lyskom-reject-recommendation (elt rec 0)))))))))

(defun lyskom-clean-read-faqs (pers-no)
  "Remove all read-faq aux-items from PERS-NO that correspond to texts
that are no longer FAQs or are missing."
  (let ((conf-stat (blocking-do 'get-conf-stat pers-no)))
    (when conf-stat
      (lyskom-traverse item (lyskom-get-aux-item (conf-stat->aux-items conf-stat)
                                                 10000)
        (when (string-match "^\\([0-9]+\\) \\([0-9]+\\)" (aux-item->data item))
          (let ((conf-no (lyskom-string-to-number (match-string 1 (aux-item->data item))))
                (text-no (lyskom-string-to-number (match-string 2 (aux-item->data item)))))
            (initiate-get-text-stat 'background
                                    'lyskom-clean-read-faqs-1
                                    text-no
                                    pers-no
                                    conf-no
                                    item)))))))

(defun lyskom-clean-read-faqs-1 (text-stat pers-no conf-no item)
  "Callback for lyskom-clean-read-faqs"
  (let ((cmp (int-to-string conf-no)))
    (when (or (null text-stat)
              (not (lyskom-traverse aux (lyskom-get-aux-item 
                                         (text-stat->aux-items text-stat)
                                         28)
                     (when (string= cmp (aux-item->data aux))
                       (lyskom-traverse-break t)))))
      (initiate-modify-conf-info 'background
                                 nil
                                 pers-no
                                 (list (aux-item->aux-no item))
                                 nil)
      )))


(defun lyskom-enter-conf (conf-stat read-info)
  "Tell server which conf the user is reading.
Prints the name and amount of unread in the conference we just went to 
according to the value of kom-print-number-of-unread-on-entrance.
Args: CONF-STAT READ-INFO"
  (let ((from-conf lyskom-current-conf)
        (to-conf (conf-stat->conf-no conf-stat)))
    (lyskom-run-hook-with-args 'kom-change-conf-hook 
                               lyskom-current-conf
                               (conf-stat->conf-no conf-stat))
    (unless lyskom-is-anonymous (initiate-pepsi 'main nil to-conf))
    (setq lyskom-current-conf to-conf)
    (lp--update-buffer from-conf)
    (lp--update-buffer to-conf)
    (let ((num-unread (text-list->length (read-info->text-list read-info))))
      (lyskom-enter-conf-print-unread conf-stat num-unread)
      (lyskom-run-hook-with-args 'kom-after-change-conf-hook 
                                 lyskom-current-conf
                                 (conf-stat->conf-no conf-stat)))))

(defun lyskom-enter-conf-print-unread (conf num)
  "Print information about unread (if requested) when entering a conf.
CONF is the conference and NUM is the number of unread in the conference."
  (let ((faq-list (lyskom-get-unread-faqs conf)))
    (lyskom-format-insert "%#1M" conf)
    (when (or (eq num 0) kom-print-number-of-unread-on-entrance faq-list)
      (lyskom-insert " - "))

    (cond ((eq num 0) 
	   (lyskom-format-insert 'conf-all-read conf))
	  (kom-print-number-of-unread-on-entrance
	   (lyskom-format-insert 'enter-conf-unread num)))

    (when faq-list
      (when (or (eq num 0) kom-print-number-of-unread-on-entrance)
	(lyskom-insert ", "))
      (lyskom-format-insert 'enter-conf-unread-faq (length faq-list)))
  
    (lyskom-insert (lyskom-conf-type-marker conf))
    (lyskom-format-insert ".\n")
    (lyskom-present-unread-faqs conf faq-list)))


(defun lyskom-leave-current-conf ()
  "Leave the current conference without going to another one."
  (let ((from-conf lyskom-current-conf))
    (set-read-list-empty lyskom-reading-list)
    (lyskom-run-hook-with-args 'kom-change-conf-hook
                               from-conf 0)
    (setq lyskom-current-conf 0)
    (initiate-pepsi 'main nil 0)
    (lyskom-run-hook-with-args 'kom-after-change-conf-hook
                               from-conf 0)
    ))


;;;================================================================

(defun lyskom-set-mode-line (&optional conf)
  "Sets mode-line-conf-name to the name of the optional argument conf CONF.
CONF can be a a conf-stat or a string."
  (let ((name (cond
	       ((null conf) 
		(if (zerop lyskom-current-conf)
		    (lyskom-get-string 'not-present-anywhere)
		  (initiate-get-conf-stat 'modeline 'lyskom-set-mode-line
					  lyskom-current-conf)
		  nil))
	       ((stringp conf) conf)
	       ((lyskom-conf-stat-p conf)
		(conf-stat->name conf))
	       (t "")))
        (conf-no (if (lyskom-conf-stat-p conf) (conf-stat->conf-no conf) 0))
	(unread -1)
	(total-unread 0)
	(letters 0)
	(len 0)
	(read-info-list nil))

      ;; Set unread to the number of unread texts in CONF.
    (setq read-info-list (read-list->all-entries lyskom-to-do-list))

    ;; This was weird. Someone had begun to write an if, but
    ;; this was all there was: (if 
    (while read-info-list
      (if (read-info->conf-stat (car read-info-list))
          (progn
            (setq len (text-list->length
                       (read-info->text-list (car read-info-list))))
            (if (= conf-no
                   (conf-stat->conf-no 
                    (read-info->conf-stat (car read-info-list))))
                (setq unread len))
            (if (= lyskom-pers-no
                   (conf-stat->conf-no 
                    (read-info->conf-stat (car read-info-list))))
                (setq letters len))
            (setq total-unread (+ total-unread len))))
      (setq read-info-list (cdr read-info-list)))
      (if (= unread -1)
	  (setq unread 0))
      
      (if (null name)
	  nil
	(setq mode-line-conf-name 
	      (substring (concat
			  (if (lyskom-conf-stat-p conf)
			      (if (not (eq lyskom-session-priority 
                                           kom-default-session-priority))
				  (format "(%d/%d:%d) "
					  unread total-unread
					  lyskom-session-priority)
				(format "(%d/%d) " unread total-unread))
			    "")
			  name
			  (make-string 27 ? ))
			 0 27)))

      (if (zerop total-unread)
          (lyskom-remove-unread-buffer lyskom-buffer)
        (lyskom-add-unread-buffer lyskom-buffer))
      (if (zerop letters)
          (lyskom-remove-unread-buffer lyskom-buffer t)
        (lyskom-add-unread-buffer lyskom-buffer t))

      (lyskom-mode-name-from-host)
      (force-mode-line-update)))




;;;; ================================================================
;;;;                   Scrolling and text insertion.


(defvar lyskom-trim-buffer-delete-to)

(defun lyskom-trim-buffer ()
  "Trim the size of a lyskom buffer to lyskom-max-buffer-size
returns t if it trimmed the buffer, nil otherwise."
  (when (and kom-max-buffer-size
	     (> (- (buffer-size) kom-trim-buffer-minimum) kom-max-buffer-size))
    (lyskom-save-excursion
      (let ((lyskom-trim-buffer-delete-to (- (buffer-size)
					     kom-max-buffer-size))
	    (inhibit-read-only t))
	(goto-char (point-min))
	(while (< (point) lyskom-trim-buffer-delete-to)
	  (forward-line 1))
	(setq lyskom-trim-buffer-delete-to (point))
	(run-hooks 'lyskom-trim-buffer-hook)
	(delete-region (point-min) lyskom-trim-buffer-delete-to)))
      t))

;;USER-HOOK: lyskom-garb-lyskom-buffer-to-file
(defun lyskom-garb-lyskom-buffer-to-file ()
  "Appends the deleted initial portions of the buffer to a file.
Put this function in your lyskom-trim-buffer-hook and set
kom-max-buffer-size to something clever, and the trimmed text
will automagically flow into your lyskom log file."
  (append-to-file 1 lyskom-trim-buffer-delete-to
		  (expand-file-name (concat "~/" (buffer-name) "-history"))))


(defun lyskom-scroll ()
  "Scroll screen if necessary.
The position lyskom-last-viewed will always remain visible."
  ;; Find a window to scroll. Scroll the selected window if that shows LysKOM.
  ;; Otherwise scroll any window currently showing LysKOM.
  (let ((win (cond ((eq (window-buffer (selected-window)) (current-buffer))
                    (selected-window))
                   (t nil))))
    (when (and win (not (lyskom-pos-visible-in-window-p (point-max))))
      (goto-char (point-max))
      (recenter -1)
      (unless (lyskom-pos-visible-in-window-p lyskom-last-viewed)
        (set-window-start win lyskom-last-viewed)
        (move-to-window-line -1)
        (vertical-motion 1)
        (when (not (lyskom-pos-visible-in-window-p))
          (forward-char -1)
          (when (> (current-column) (window-width))
            (backward-char (+ (- (current-column) (window-width)) 6)))
          )))))


(defun lyskom-next-property-bounds (from to sym)
  "Sort of like next-text-property-bounds, but limited at the front and
back, and works even if from has the property."
  (let ((result (cond ((get-text-property from sym)
                       (cons from (next-single-property-change from sym)))
                      ((next-single-property-change from sym)
                       (let ((start (next-single-property-change from sym)))
                         (cons start (next-single-property-change start sym)))))))
    (when result
      (cond ((null (cdr result)) (setcdr result (or to (point-max))))
            ((and from (< (car result) from)) (setcar result from))
            ((and to (> (cdr result) to)) (setcar result to)))) 
    result))


(defun lyskom-do-special-inserts (start end sym)
  (condition-case var
      (let ((bounds (lyskom-next-property-bounds start end sym))
            (next (make-marker))
            (fn nil))
        (while bounds
          (set-marker next (cdr bounds))
          (setq fn (get-text-property (car bounds) sym))
          (remove-text-properties (car bounds) (cdr bounds) (list sym))
          (condition-case val
              (funcall (if (listp fn) (car fn) fn)
                       (car bounds)
                       (cdr bounds)
                       (if (listp fn) (cdr fn) nil))
            (error (apply 'message "%S" val)))
          (setq start next)
          (setq bounds (lyskom-next-property-bounds
                        (1- start) (point-max) sym)))
	(set-marker next nil))
    (error (lyskom-ignore var))))

(defun lyskom-do-insert (string)
  (if (lyskom-format-state-p string)
      (let ((start (point)))
	(lyskom-do-insert (lyskom-format-transform-result string))
	(lyskom-traverse defer-info (format-state->delayed-content string)
	  (set-defer-info->pos defer-info 
			       (set-marker (make-marker)
					   (+ start 
					      (defer-info->pos defer-info))))
	  (lyskom-defer-insertion defer-info)))

    (let ((start (point)))
      (insert string)
      (lyskom-do-special-inserts start (point) 'lyskom-fill)
      (lyskom-do-special-inserts start (point) 'lyskom-overlay)
      (lyskom-do-special-inserts start (point) 'special-insert))))



(defun lyskom-insert (string)
  "Insert STRING last in current buffer.
Leaves the point at the end of the buffer if possible without
scrolling past lyskom-last-viewed (generally the most recent prompt.)
Leaves the point at the end of the window if not possible. If buffer
is not on screen then doesn't move point."
  (when (symbolp string) (setq string (lyskom-get-string string)))
  (let ((was-at-max (= (save-excursion (end-of-line) (point)) (point-max))))
    (save-excursion
      (goto-char (point-max))
      (let ((inhibit-read-only t))
        (lyskom-do-insert string))
      (lyskom-trim-buffer))
    (let ((window (get-buffer-window (current-buffer))))
      (if (and window was-at-max)
	  (if (lyskom-pos-visible-in-window-p (point-max) window)
              (goto-char (point-max))
            (and kom-continuous-scrolling (lyskom-scroll)))))))



(defun lyskom-insert-at-point (string)
  "Insert STRING in the current buffer at point."
  (let ((inhibit-read-only t))
    (lyskom-do-insert string))
  (lyskom-trim-buffer))  


(defun lyskom-insert-before-prompt (string)
  "Insert STRING just before the prompt of if no prompt then just buffers.
If prompt on screen then do the scroll if necessary.
The strings buffered are printed before the prompt by lyskom-update-prompt."
  ;;
  ;; This is the policy for moving point:
  ;;
  ;; old-point-max is the point-max before the text is inserted
  ;; new-point-max is the point-max after the text is inserted
  ;;
  ;; If point /= old-point-max, leave the point where it is, and don't
  ;; scroll the window.
  ;;
  ;; If point = old-point-max, set it to new-point-max, and if the
  ;; buffer is in a window, make sure that point is visible.
  ;;
  (cond
   ((and lyskom-executing-command
         (not (eq lyskom-is-waiting t)))
    ;; Don't insert the string until the current command is finished.
    (if (null lyskom-to-be-printed-before-prompt)
        (setq lyskom-to-be-printed-before-prompt (lyskom-queue-create)))
    (lyskom-queue-enter lyskom-to-be-printed-before-prompt 
                        (list string)))
   (t
    ;; For some reaseon save-excursion doesn't work as expected
    (let ((oldpoint (point-marker)))
      (goto-char (point-max))
      (beginning-of-line)
      (let ((inhibit-read-only t))
        ;; Some (all?) xemacsen adds the text-properties on the prompt
        ;; to text inserted before it. We don't want this, so insert a
        ;; blank and remove all text-properties from it, insert the
        ;; text before the blank and finally remove the blank.
        (insert " ")
        (backward-char)
        (remove-text-properties (point) (+ (point) 1) (text-properties-at (point)))
        (lyskom-do-insert string)
        (delete-char 1))
      (goto-char oldpoint)
      (set-marker oldpoint nil))
    (let ((window (get-buffer-window (current-buffer))))
      (if (and window
               (not (lyskom-pos-visible-in-window-p (point) window)))
          ;; This mease that the prompt has been pushed off the bottom
          (save-selected-window
            (select-window window)
            (recenter -1)))))))
      

(defun lyskom-message (format-string &rest args)
  "Like message, but converts iso-8859/1 texts to swascii if necessary.
Args: FORMAT-STRING &rest ARGS"
  (let ((str (apply 'format format-string args)))
    (message "%s" str)))


(defun lyskom-error (format-string &rest args)
  "Like error, but converts iso-8859/1 texts to swascii if necessary.
Args: FORMAT-STRING &rest ARGS"
  (let ((str (apply 'format format-string args)))
    (error "%s" str)))


(defun lyskom-set-last-viewed ()
  (save-excursion
    (vertical-motion 0)
    (setq lyskom-last-viewed (point-marker))))


;;; ================================================================
;;;                  Extended string formatting

;;; Author: David Byers
;;; Original code: Inge Wallin


(defvar lyskom-format-format
  "%\\(=\\|<\\|>\\)?\\(-?[0-9]+\\|-?[0-9]+\\.[0-9]+\\|-?[0-9]+\\.[0-9]+\\.[0-9]+\\)?\\(\\.\\.\\.\\)?\\(:.\\)?\\(#\\([0-9]+\\)\\)?\\(_\\)?\\(:\\)?\\(&\\)?\\([][$@MmPpnrtsdoxlcfgCSDF%?]\\)"
  "regexp matching format string parts.")

(defun lyskom-insert-string (atom)
 "Find the string corresponding to ATOM and insert it into the LysKOM buffer." 
 (if (stringp atom)
     (lyskom-insert atom)
   (lyskom-insert (lyskom-get-string atom))))

(defun lyskom-format-transform-result (state)
  "Convert a format-state to a string."
  (let ((result (format-state->result state)))
    (lyskom-traverse overlay (format-state->delayed-overlays state)
      (add-text-properties (aref overlay 0)
                           (aref overlay 1)
                           (list 'lyskom-overlay 
                             (cons 'lyskom-special-insert-overlay
                                   (aref overlay 2)))
                           result))
    result))

(defun lyskom-limited-make-overlay (start end)
  (let ((val (lyskom-xemacs-or-gnu
              (make-extent start end)
              (make-overlay start end))))
  (when (lyskom-plusp kom-max-overlays)
    (let ((old (nthcdr (1- kom-max-overlays) lyskom-overlay-pool))) ;
      (setq lyskom-overlay-pool 
            (lyskom-nbutlast lyskom-overlay-pool 
                             (- (length lyskom-overlay-pool)
                                kom-max-overlays)))
      (mapc
       (lambda (o) (lyskom-xemacs-or-gnu (delete-extent o)
					 (delete-overlay o))) old))
    (setq lyskom-overlay-pool (cons val lyskom-overlay-pool)))
  val))


(defun lyskom-special-insert-overlay (start end args)
  (lyskom-xemacs-or-gnu
   (let ((overlay (lyskom-limited-make-overlay start end)))
     (while args
       (set-extent-property overlay (car args) (car (cdr args)))
       (setq args (nthcdr 2 args)))
     (set-extent-priority overlay 1000))
   (let ((overlay (lyskom-limited-make-overlay start end)))
     (while args
       (overlay-put overlay (car args) (car (cdr args)))
       (setq args (nthcdr 2 args))))))

(defun lyskom-format (format-string &rest argl)
  (lyskom-format-transform-result (lyskom-do-format format-string argl)))

(defun lyskom-format-insert (format-string &rest argl)
  "Format and insert a string according to FORMAT-STRING.
The string is inserted at the end of the buffer with `lyskom-insert'."
  (lyskom-insert (lyskom-do-format format-string argl t)))

(defun lyskom-format-insert-at-point (format-string &rest argl)
  "Format and insert a string according to FORMAT-STRING.
The string is inserted at point."
  (lyskom-insert-at-point (lyskom-do-format format-string argl t)))


(defun lyskom-format-insert-before-prompt (format-string &rest argl)
  "Format and insert a string according to FORMAT-STRING.
The string is inserted just before the prompt, and if the prompt is not
currently visible the text is queued to be inserted when the prompt
reappears."
  (lyskom-insert-before-prompt
   (lyskom-do-format format-string argl t)))


(defun lyskom-do-format (format-string &optional argl allow-defer)
  "Do the actual formatting and return the resulting format-state."
  (let ((fmt (cond ((stringp format-string) format-string)
		   ((symbolp format-string) (lyskom-get-string 
					     format-string))))
	(state nil))
    (if (null fmt)
        (signal 'lyskom-internal-error (list 'lyskom-format-insert
                                             ": bad format string"))
      (save-excursion
        (if (and (boundp 'lyskom-buffer)
		 kom-deferred-printing
		 lyskom-buffer)
            (set-buffer lyskom-buffer))
	(condition-case error
	    (setq state (lyskom-format-aux (lyskom-create-format-state
					    fmt
					    0
					    argl
                                            (length argl)
					    ""
                                            nil
                                            nil
                                            nil
                                            0)
					   allow-defer))
	  (lyskom-format-error
	   (error "LysKOM internal error formatting %s: %s%s (arg %S, got %S)"
		  format-string (nth 1 error) (nth 2 error) 
                  (nth 3 error) (nth 4 error))))))
    state))



;;;
;;; If you add a format letter, for goodness' sake, don't forget to
;;; att it to the regexp above too!
;;;



(defun lyskom-format-aux (format-state allow-defer)
    (set-format-state->depth format-state
                             (1+ (format-state->depth format-state)))
    (let ((format-length (length (format-state->format-string format-state)))
        (arg-no nil)
        (pad-length nil)
        (frac-min nil)
        (frac-max nil)
        (pad-string nil)
        (format-letter nil)
        (colon-flag nil)
        (equals-flag nil)
        (downcase-flag nil)
        (face-flag nil)
        (trailer nil)
        (abort-format nil))

    ;;
    ;;  Eat the format string bit by bit
    ;;

    (while (and (not abort-format)
                (< (format-state->start format-state)
                   format-length))

      ;;
      ;;    Look for a format letter. If there is none, finish up,
      ;;    otherwise handle each letter separately
      ;;

      (if (null (string-match lyskom-format-format
                              (format-state->format-string format-state)
                              (format-state->start format-state)))
          (progn
            (set-format-state->result 
             format-state
             (concat (format-state->result format-state)
                     (substring (format-state->format-string format-state)
                                (format-state->start format-state))))
            (set-format-state->start format-state
                                     (length (format-state->format-string
                                              format-state))))

        ;;
        ;; A format letter has been found
        ;;

        (set-format-state->result 
         format-state
         (concat (format-state->result format-state)
                 (substring (format-state->format-string format-state)
                            (format-state->start format-state)
                            (match-beginning 0))))

        (set-format-state->start format-state
                                 (match-end 0))

        (if (match-beginning 2)
          (save-match-data
            (let* ((s (match-string 2 (format-state->format-string format-state))))
              (cond ((or (string-match "\\(-?[0-9]+\\)\\.\\([0-9]+\\)\\.\\([0-9]+\\)" s)
                         (string-match "\\(-?[0-9]+\\)\\.\\([0-9]+\\)" s))
                     (setq pad-length (lyskom-string-to-number (match-string 1 s)))
                     (setq frac-min (lyskom-string-to-number (match-string 2 s)))
                     (setq frac-max (lyskom-string-to-number (or (match-string 3 s) (match-string 2 s)))))
                    (t (setq pad-length (lyskom-string-to-number s))))))
          (setq pad-length nil))

	(setq equals-flag (and (match-beginning 1)
                               (aref (match-string 1 
                                                   (format-state->format-string
                                                    format-state)) 0))
	      arg-no (if (match-beginning 6)
			 (lyskom-string-to-number 
                          (match-string 6 (format-state->format-string
                                           format-state)))
		       nil)
              downcase-flag (match-beginning 7)
	      colon-flag (match-beginning 8)
              face-flag (match-beginning 9)
              trailer (match-beginning 3)
              pad-string (and (match-beginning 4)
                              (substring (format-state->format-string format-state)
                                         (1+ (match-beginning 4))
                                         (match-end 4)))
	      format-letter (if (match-beginning 10)
				(aref (format-state->format-string 
				       format-state)
				      (match-beginning 10))
			      (signal 'lyskom-internal-error
				      (list 'lyskom-format-aux 
					    (format-state->format-string
					     format-state)))))


        ;;
        ;;  If the format letter is an end-of-group letter, abort
        ;;  formatting and return to the caller.
        ;;

	(if (= ?\] format-letter)
	    (progn 
	      (setq abort-format t)
	      (set-format-state->start format-state (match-end 0)))

	  (setq format-state 
            (lyskom-format-aux-help 
             format-state
             pad-length
             frac-min
             frac-max
             arg-no
             format-letter
             equals-flag
             trailer
             colon-flag
             face-flag
             downcase-flag
             (or (elt pad-string 0)
                 (if (and (match-beginning 2)
                          (eq (aref (format-state->format-string format-state)
                                    (match-beginning 2))
                              ?0))
                     ?0
                   ?\ ))
	     allow-defer))))))
    (lyskom-tweak-format-state format-state)
    (set-format-state->depth format-state
                             (1- (format-state->depth format-state)))
    format-state)


(defun lyskom-format-aux-help (format-state
                               pad-length
                               frac-min
                               frac-max
                               arg-no
                               format-letter
                               equals-flag
                               trailer
                               colon-flag
                               face-flag
                               downcase-flag
                               pad-letter
			       allow-defer)
  (let ((arg nil)
        (result nil)
        (propl nil)
        (prop-adjust-start 0)
        (prop-adjust-end 0)
        (oldpos (length (format-state->result format-state)))
        (abs-length (cond ((null pad-length) nil)
                          ((< pad-length 0) (- 0 pad-length))
                          (t pad-length))))
    (if (and arg-no 
             (not (eq format-letter ?\?))
             (< (format-state->length format-state) arg-no))
        (signal 'lyskom-format-error (list 'lyskom-format
                                             ": too few arguments")))
    (if arg-no
        (setq arg (nth (1- arg-no) (format-state->argl format-state))))

    (cond
     ;;
     ;;  Format a string or symbol by simply inserting it into the
     ;;  result list
     ;;
     ((= format-letter ?l)
      (setq result 
            (cond ((or (integerp arg)
                       (lyskom-characterp arg))
                   (make-string pad-length arg))
                  ((stringp arg)
                   (let ((count (/ pad-length (length arg)))) 
                     (apply 'concat 
                            (nreverse
                             (cons (substring 
                                    arg 0 
                                    (- pad-length (* (length arg) count)))
                                   (make-list count arg))))))
                  (t (signal 'lyskom-internal-error
                                          (list 'lyskom-format
                                                ": argument error (expected char or string)"
                                                arg-no
                                                arg))))))

     ((= format-letter ?s)
      (setq result (cond ((stringp arg) arg)
                         ((symbolp arg) (symbol-name arg))
                         (t (signal 'lyskom-format-error
                                    (list 'lyskom-format
                                          ": argument error (expected string)"
                                          arg-no
                                          arg)))))
      (when downcase-flag (setq result (downcase result))))
     ;;
     ;;  Format a number by conferting it to a string and inserting
     ;;  it into the result list
     ;;
     ((or (= format-letter ?d)
          (= format-letter ?o)
          (= format-letter ?x))
      (setq result (cond ((integerp arg)
                          (format (format "%%%c" format-letter) arg))
                         ((and (floatp arg) (= format-letter ?d))
                          (format "%0.0f" arg))
                         (t (signal 'lyskom-internal-error
                                    (list 'lyskom-format
                                          ": argument error (expected int)"
                                          arg-no
                                          arg))))))

     ((or (= format-letter ?g)
          (= format-letter ?f)
          (= format-letter ?e))
      (setq result (cond ((numberp arg) 
                          (format (if frac-max
                                      (format "%%.%d%c" 
                                              frac-max
                                              format-letter)
                                      (format "%%%c" format-letter))
                                  arg))
                         (t (signal 'lyskom-internal-error
                                    (list 'lyskom-format
                                          ": argument error (expected number)"
                                          arg-no
                                          arg)))))
      (save-match-data
        (when (and (string-match "\\." result)
                   (string-match "\\.?0+$" result))
          (setq result (substring result 0 (match-beginning 0))))
        (when (and frac-min (> frac-min 0))
          (if (string-match "\\.\\([0-9]*\\)$" result)
              (setq result 
                    (concat result
                            (make-string 
                             (max 0 
                                  (- frac-min
                                     (length (match-string 1 result))))
                             ?0)))
            (setq result
                  (concat result
                          "."
                          (make-string frac-min ?0)))))))

     ((= format-letter ?f)
      (setq result (cond ((numberp arg) (format "%f" arg))
                         (t (signal 'lyskom-internal-error
                                    (list 'lyskom-format
                                          ": argument error (expected number)"
                                          arg-no
                                          arg))))))


     ;;
     ;;  Format a character by converting it to a string and inserting
     ;;  it into the result list
     ;;
     ((= format-letter ?c)
      (setq result (cond ((integerp arg) (char-to-string (lyskom-int-to-char arg)))
                         ((lyskom-characterp arg) (char-to-string arg))
                         (t (signal 'lyskom-internal-error
                                    (list 'lyskom-format
                                          ": argument error (expected char)"
                                          arg-no
                                          arg))))))
     ;;
     ;;  Format a literal percent character by inserting a string
     ;;  containing it into the result list
     ;;
     ((= format-letter ?%)
      (setq result "%")) 

     ;;
     ;;  Format a command name somewhat specially
     ;;
     ((= format-letter ?C)
      (setq result (cond ((stringp arg) arg)
                         ((vectorp arg) 
                          (mapconcat 'single-key-description
                                     (append arg nil) " "))
                         ((and arg (symbolp arg))
                          (if (memq arg lyskom-commands)
			      (lyskom-command-name arg)
			    (prin1-to-string arg t)))
                         (t (format "(%S)" arg)))))

     ;;
     ;;  Format a sexp by princing it. Sort of.
     ;;
     ((= format-letter ?S)
      (setq result (format "%S" arg))
      (when downcase-flag (setq result (downcase result))))

     ;;
     ;;  Format a text property array indicator by retrieving the
     ;;  properties from the argument list and adding a start of 
     ;;  new properties to the format state
     ;;
     ((= format-letter ?@)
      (set-format-state->delayed-propl
       format-state
       (cons (vector (length (format-state->result format-state))
                     arg
                     (format-state->depth format-state))
             (format-state->delayed-propl format-state))))

     ;;
     ;;  Format an overlay
     ;;
     ((= format-letter ?$)
      (when arg
        (set-format-state->delayed-overlays
         format-state
         (cons (vector (length (format-state->result format-state))
                       nil
                       arg
                       (format-state->depth format-state))
               (format-state->delayed-overlays format-state)))))
     ;;
     ;;  Format a subformat list by recursively formatting the contents
     ;;  of the list, augmenting the result and format state
     ;;
     ;;  Idea: If this code used lyskom-do-format instead, we could
     ;;  use it to truncate a complex format by using a format string
     ;;  such as:  "%17[ %#1s will be truncated %]"
     ;;
     ;;  This could be useful for faster response when deferring
     ;;  printing. But this function would become more complex and
     ;;  slower.
     ;;
     ((= format-letter ?\[)
      (setq format-state (lyskom-format-aux format-state allow-defer)
            result nil))

     ;; A predicate
     ;; Get the predicate type and then parse the format string
     ;; accordingly

     ((= format-letter ?\?)
      (unless (string-match "[dbz+]" 
                            (format-state->format-string format-state)
                            (format-state->start format-state))
        (lyskom-error "Unknown predicate in format string %s (%d)"
                      (format-state->format-string format-state)
                      (format-state->start format-state)))
      (set-format-state->start format-state (match-end 0))
      (let ((predicate-type (elt (match-string 0
                                 (format-state->format-string format-state))
                                 0)))
        (cond 

         ;; Plural/singular predicate
         ;; arg is an integer. Use the first subformat if it is one
         ;; and the second if it is other than one.
         ((= predicate-type ?d)
          (setq format-state
                (lyskom-format-do-binary-predicate (= arg 1)
                                                   format-state 
                                                   allow-defer)
                result nil))

         ;; True/false predicate
         ((= predicate-type ?b)
          (setq format-state
                (lyskom-format-do-binary-predicate arg
                                                   format-state
                                                   allow-defer)
                result nil))

         ;; Zero/nonzero predicate
         ((= predicate-type ?z)
          (setq format-state
                (lyskom-format-do-binary-predicate (not (zerop arg))
                                                   format-state
                                                   allow-defer)
                result nil))

         ;; Non-negative predicate
         ((= predicate-type ?+)
          (setq format-state
                (lyskom-format-do-binary-predicate (not (lyskom-minusp arg))
                                                   format-state
                                                   allow-defer)
                result nil))
         )))

     ((= format-letter ?F)
      (set-format-state->delayed-propl
       format-state
       (cons (vector (length (format-state->result format-state))
                     `(lyskom-fill ((lambda (start end width)
                                      (let ((fill-column (or width (- (window-width 8)))))
                                        (fill-region start end))) . ,arg))
                     (format-state->depth format-state))
             (format-state->delayed-propl format-state))))

     ;;
     ;;  Format a conference or person name by retrieving information
     ;;  about the conference or person and inserting it as a button
     ;;  (unless the colon flag is set)
     ;;
     ((or (= format-letter ?M)
          (= format-letter ?P))
      (setq result
            (cond
             ;; The string is already supplied
             ((stringp arg) arg)

             ;; Conference 0 does not exist, and person 0 means anonymous
             ((and (integerp arg)
                   (zerop arg))
	      (setq colon-flag t)
              (lyskom-format (cond ((= format-letter ?P)
                                    (or lyskom-default-pers-string
                                        'person-is-anonymous))
                                   ((= format-letter ?M)
                                    (or lyskom-default-conf-string
                                        'conference-does-not-exist)))
                             arg)
	      )

             ;; Delay the printing
             ((and allow-defer
                   kom-deferred-printing
                   (integerp arg))
              (let ((tmp (cache-get-uconf-stat arg)))
                (if (null tmp)
                    (let* ((format-element
			    (concat "%#2@%"
				    (if equals-flag 
                                        (make-string 1 equals-flag)
                                      "")
				    (if pad-length (int-to-string pad-length))
				    "#1"
				    (if colon-flag ":" "")
				    (char-to-string format-letter)))
			   (defer-info (lyskom-create-defer-info
					'get-uconf-stat
					arg
					'lyskom-deferred-insert-conf
					oldpos
					(if pad-length
                                            (cond ((eq equals-flag ?=) 
                                                   abs-length)
                                                  ((eq equals-flag ?<)
                                                   (min (length lyskom-defer-indicator)
                                                        abs-length))
                                                  (t (max (length lyskom-defer-indicator)
                                                          abs-length)))
                                          (length lyskom-defer-indicator))
					format-element
					lyskom-default-conf-string)))
		      (set-format-state->delayed-content
                       format-state
                       (cons defer-info
                             (format-state->delayed-content
                              format-state)))
                      lyskom-defer-indicator)

                  ;; The conf-stat was in the cache

                  (setq arg tmp)
                  (uconf-stat->name arg)
                  )))

             ;; The argument is an integer and we do not permit 
             ;; deferred printing
             ((integerp arg)
              (let ((conf-stat (blocking-do 'get-uconf-stat arg)))
                (if (null conf-stat)
                    (lyskom-format (if (= format-letter ?P)
                                       'person-does-not-exist
                                     'conference-does-not-exist)
                                   arg)
                  (uconf-stat->name conf-stat))))

             ;; We got a conf-stat, and can use it directly
             ((lyskom-conf-stat-p arg)
              (if face-flag
                  (let ((face (conf-stat-find-aux arg 9)))
                    (lyskom-maybe-add-face-to-string face
                                                     (conf-stat->name arg)))
                (conf-stat->name arg)))

             ;; Argument is a conf-z-info
             ((lyskom-conf-z-info-p arg)
              (conf-z-info->name arg))


             ;; We have an uconf-stat
             ((lyskom-uconf-stat-p arg)
              (uconf-stat->name arg))

             ;; Something went wrong
             (t (signal 'lyskom-internal-error
                        (list 'lyskom-format
                              ": argument error (expected conf)"
                              arg-no
                              arg)))))
      (if (and (not colon-flag)
               (or (lyskom-conf-stat-p arg)
                   (lyskom-uconf-stat-p arg)
                   (lyskom-conf-z-info-p arg)
                   (numberp arg)))
          (setq propl 
                (append
                 (lyskom-default-button (if (= format-letter ?P) 'pers 'conf)
                                        arg)
                 propl))))

     ;;
     ;;  Format a conference or person number the same way as names,
     ;;  but insert the number rather than the name
     ;;
     ((or (= format-letter ?m)
          (= format-letter ?p))
      (setq result
            (cond ((integerp arg) 
		   (int-to-string arg))
                  ((lyskom-conf-z-info-p arg)
                   (int-to-string (conf-z-info->conf-no arg)))
                  ((lyskom-conf-stat-p arg) 
                   (int-to-string (conf-stat->conf-no arg)))
                  ((lyskom-uconf-stat-p arg)
                   (int-to-string (uconf-stat->conf-no arg)))
                  (t (signal 'lyskom-internal-error
                             (list 'lyskom-format
                                   ": argument error (expected conf)"
                                   arg-no
                                   arg)))))
      (if (not colon-flag)
          (setq propl 
                (append 
                 (lyskom-default-button (if (= format-letter ?p) 'pers 'conf)
                                        arg)
                 propl))))
     ;;
     ;;  Format an integer or text-stat as a text number by adding the
     ;;  by inserting a button (unless the colon flag is set)
     ;;
     ((= format-letter ?n)
      (setq result
            (cond ((integerp arg) (int-to-string arg))
                  ((numberp arg) 
                   (setq colon-flag t)
                   (format "%.0f" arg))
                  ((lyskom-text-stat-p arg) (int-to-string
                                             (text-stat->text-no arg)))
                  (t (signal 'lyskom-internal-error
                             (list 'lyskom-format
                                   ": argument error (expected text-no)"
                                   arg-no
                                   arg)))))
      (if (not colon-flag)
          (setq propl
                (append (lyskom-default-button 'text arg) propl))))
     ;;
     ;;  Format a subject line by adding the subject face to the text
     ;;  properties and the subject to the result list
     ;;
     ((= format-letter ?r)
      (setq result (cond ((stringp arg) 
                          (lyskom-format-plaintext-fonts arg)
                          (lyskom-button-transform-text arg))
                         ((consp arg) 
                          (lyskom-format-plaintext-fonts (cdr arg))
                          (lyskom-button-transform-text (cdr arg) (car arg)))
                         (t (signal 'lyskom-internal-error
                                    (list 'lyskom-format
                                          ": argument error (expected subject)"
                                          arg-no
                                          arg)))))
      (if (and (not colon-flag)
               (not (lyskom-face-default-p kom-subject-face)))
          (setq propl (append (list 'face kom-subject-face) propl))))
     ;;
     ;;  Format a LysKOM text body. Currently this does nothing. It
     ;;  should parse the text for buttons
     ;;
     ((= format-letter ?t)

      ;; +++ One would want to do this before or after, but then
      ;; buttons will not be visible and other highlighting will
      ;; disappear.

      ;; (if (not colon-flag)
      ;;     (setq propl (append (list 'face kom-text-face) propl)))

      (setq result
            (cond ((stringp arg) (lyskom-format-text-body arg))
                  ((and (consp arg)
                        (lyskom-text-stat-p (car arg)))
                   (lyskom-format-text-body (cdr arg)
                                            (car arg)))
                  (t (signal 'lyskom-internal-error
                             (list 'lyskom-format
                                   ": argument error (expected text)"
                                   arg-no
                                   arg))))))


     ;;
     ;; Insert some deferred text
     ;;
     ((= format-letter ?D)
      (setq result
	    (cond
	     ((stringp arg) arg)
	     (t
	      (let ((format-element
		     (concat "%"
			     (if equals-flag (make-string 1 equals-flag) "")
			     (if pad-length (int-to-string pad-length))
			     "#1"
			     (if colon-flag ":" "")
			     "s")))
		(set-defer-info->pos arg oldpos)

                ;; Note: length is right below. string-width is wrong.
		(set-defer-info->del-chars
		 arg (if pad-length
                         (cond ((eq equals-flag ?=) 
                                abs-length)
                               ((eq equals-flag ?<)
                                (min (length lyskom-defer-indicator)
                                     abs-length))
                               (t (max (length lyskom-defer-indicator)
                                       abs-length)))
                       (length lyskom-defer-indicator)))
		(set-defer-info->format arg format-element))
	      (set-format-state->delayed-content
	       format-state
	       (cons arg
		     (format-state->delayed-content
		      format-state)))
	      lyskom-defer-indicator))))

     ;;
     ;; The format letter was unknown
     ;;

     (t (signal 'lyskom-internal-error
		(list 'lyskom-format-help format-letter))))

    ;;
    ;; Pad the result to the appropriate length
    ;; Fix flags so text props go in the right places anyway
    ;;

    (cond ((or (null pad-length)
               (null result)) nil)
          ((and (eq equals-flag ?<)
                (> abs-length (lyskom-string-width result))) nil)
          ((> abs-length (lyskom-string-width result))
           (let ((padstring (make-string (- abs-length (lyskom-string-width result))
                                         pad-letter)))
             (if (< pad-length 0)       ; LEFT justify
                 (progn
                   (setq prop-adjust-end (- (- abs-length (lyskom-string-width 
							   result))))
                   (setq result (concat result padstring)))
               (progn
                 (setq prop-adjust-start (- abs-length (lyskom-string-width result)))
                 (setq result (concat padstring result))))))
          ((and (memq equals-flag '(?= ?<))
                (< abs-length (lyskom-string-width result)))
           (setq result (lyskom-truncate-string-to-width result abs-length (and trailer "...")))))

    (if result
        (progn
          (set-format-state->result 
           format-state
           (concat (format-state->result format-state)
                   result))))
    (if (and propl kom-text-properties)
        (add-text-properties
         (+ oldpos prop-adjust-start)
         (+ (length (format-state->result format-state)) prop-adjust-end)
         propl
         (format-state->result format-state))))
  format-state)


(defun lyskom-format-do-binary-predicate (option format-state allow-defer)
  (cond (option
         (setq format-state
               (lyskom-format-enter-subformat format-state allow-defer))
         (lyskom-format-skip-subformat format-state))
        (t
         (lyskom-format-skip-subformat format-state)
         (setq format-state 
               (lyskom-format-enter-subformat format-state allow-defer))
         ))
  format-state)

(defun lyskom-format-enter-subformat (format-state allow-defer)
  "The format string should  be just before a subformat. Enter it."
  (unless (string-match "%\\[" 
                        (format-state->format-string format-state)
                        (format-state->start format-state))
    (lyskom-error "Predicate syntax error in format string %s (%d)"
                  (format-state->format-string format-state)
                  (format-state->start format-state)))
  (set-format-state->start format-state (match-end 0))
  (lyskom-format-aux format-state allow-defer))


(defun lyskom-format-skip-subformat (format-state)
  "Skip the subformat specification at the start of format-state"

  ;; Check that it looks like a subformat in the first place
  (unless (string-match "%\\[" 
                        (format-state->format-string format-state)
                        (format-state->start format-state))
    (lyskom-error "Predicate syntax error in format string %s (%d)"
                  (format-state->format-string format-state)
                  (format-state->start format-state)))
  (set-format-state->start format-state (match-end 0))

  ;; We are now inside the start of the subformat

  (let ((level 1))
    (while (> level 0)
      (if (null (string-match "\\(%\\[\\|%\\]\\)"
                              (format-state->format-string format-state)
                              (format-state->start format-state)))
          (lyskom-error "Bad nesting in format string %s (%d)" 
                        (format-state->format-string format-state)
                        (format-state->start format-state))
        (let ((ch (elt (match-string 
                        1 (format-state->format-string format-state)) 1)))
        (cond ((= ?\[ ch) (setq level (1+ level)))
              (t (setq level (1- level))))
        (set-format-state->start format-state (match-end 0)))))))


(defun lyskom-tweak-format-state (format-state) 
  (let ((dp (format-state->delayed-propl format-state)))
    (while dp
      (when (eq (format-state->depth format-state)
                (aref (car dp) 2))
        (add-text-properties (aref (car dp) 0)
                             (length (format-state->result format-state))
                             (aref (car dp) 1)
                             (format-state->result format-state))
        (set-format-state->delayed-propl 
         format-state
         (delq (car dp) (format-state->delayed-propl format-state))))
      (setq dp (cdr dp))))

  (lyskom-traverse overlay-spec (format-state->delayed-overlays format-state)
    (unless (or (aref overlay-spec 1)
                (not (eq (format-state->depth format-state)
                         (aref overlay-spec 3))))
      (aset overlay-spec 1 (length (format-state->result format-state)))))

  format-state)


;;; ================================================================
;;;			 Text body formatting

(defun lyskom-text-stat-content-type (text text-stat)
  "Return the content type of a text."
  (let* ((ct-item (and text-stat (car (text-stat-find-aux text-stat 1)))))
    (or (cond (ct-item (aux-item->data ct-item))
	      ((and kom-respect-ancient-content-types
		    (string-match "\\`\\(\\S-+\\):\\s-*$" text)
		    (match-beginning 1))
	       (match-string 1 text))
	      (t nil))
	"text/x-kom-basic")))

(lyskom-try-require 'latin-unity)
(lyskom-with-external-functions (smiley-region latin-unity-remap-region)

  (defun lyskom-format-text-body (text &optional text-stat)
    "Format a text for insertion. Does parsing of special markers in the text."
    (let* ((content-type (lyskom-text-stat-content-type text text-stat))
           (fn (and content-type
                    (cdr (let ((case-fold-search t))
                           (lyskom-traverse el lyskom-format-special
                             (when (eq 0 (string-match 
                                          (if (stringp (car el))
                                              (car el)
                                            (symbol-name (car el)))
                                          content-type))
                               (lyskom-traverse-break el)))))))
           (formatted (cond ((null fn) nil)
                            ((listp fn) 
                             (lyskom-traverse el fn
                               (let ((a (funcall el text text-stat)))
                                 (when a (lyskom-traverse-break a)))))
                            ((functionp fn)
                             (funcall fn text text-stat))
                            (t nil))))
      (or formatted text)))

  (defun lyskom-format-plaintext (text text-stat)
    (lyskom-format-plaintext-fonts text)
    (let ((tmp (if kom-text-properties
                   (lyskom-button-transform-text
                    (lyskom-fill-message text)
                    text-stat)
                 (lyskom-fill-message text))))
      (when (and kom-smileys (fboundp 'smiley-region))
        (add-text-properties 0 (length tmp) 
                             '(special-insert lyskom-postprocess-text) tmp))
      (when (fboundp 'latin-unity-remap-region)
        (add-text-properties 0 (length tmp)
                             '(special-insert lyskom-unity-text) tmp))
      tmp))

  (defun lyskom-postprocess-text (start end &rest args)
    (condition-case nil
        (smiley-region start (min (point-max) (1+ end)))
      (error nil)))

  (defvar latin-unity-preferred-coding-system-list)
  (defvar latin-unity-iso-8859-1-aliases)
  (defvar latin-unity-cset-codesys-alist)

  (defun lyskom-unity-text (start end &rest args)
    (lyskom-traverse codesys latin-unity-preferred-coding-system-list
      (condition-case nil
          (progn
            (when (memq codesys latin-unity-iso-8859-1-aliases)
              (setq codesys 'iso-8859-1))
            (let ((gr (or (car (rassq codesys latin-unity-cset-codesys-alist))
                          (and codesys
                               (eq (lyskom-coding-system-type codesys) 'iso2022)
                               (lyskom-coding-system-property codesys 'charset-g1)))))
              (when gr
                (lyskom-traverse-break 
                 (latin-unity-remap-region start (min (point-max) (1+ end))
                                           gr nil t)))))
        (error nil))
      latin-unity-preferred-coding-system-list))

)



(defmacro lyskom-format-plaintext-fonts-body ()
  "Internal macro for lyskom-format-plaintext-fonts"
  (let* ((re-x 0)
         (re-1 "[^\]\|\}\) ^\n\r\t%s]")
         (re-2 "[^^\|<>=%%$\r\n%s]")
         (re-3 '(("_" (prefix . "") (suffix . "") (face . italic))
                 ("*" (prefix . "") (suffix . "") (face . bold))
                 ("/" (prefix . "\\s-") (suffix . "[\].,;:-_!\"#$%&\(\)\[\{\}=+?' \t\r\n]") (face . italic))))
         (re (mapconcat (lambda (el)
                          (let ((x (regexp-quote (car el))))
                            (format "%s\\(%s%s\\(%s*%s\\)?%s\\)%s"
                                    (cdr (assq 'prefix (cdr el)))
                                    x
                                    (format re-1 x)
                                    (format re-2 x)
                                    (format re-1 x)
                                    x
                                    (cdr (assq 'suffix (cdr el)))
                                    )))
                        re-3
                        "\\|")))
    `(let ((start 0))
       (while (string-match ,re text start)
         (cond ,@(mapcar (lambda (el)
                           (prog1
                               `((match-beginning ,(+ 1 (* re-x 2)))
                                 (add-text-properties (1+ (match-beginning ,(+ 1 (* re-x 2))))
                                                      (1- (match-end ,(+ 1 (* re-x 2))))
                                                      '(face ,(cdr (assq 'face (elt re-3 re-x))))
                                                      text))
                             (setq re-x (1+ re-x))))
                         re-3))
         (setq start (1- (match-end 0)))))))

(defun lyskom-format-plaintext-fonts (text)
  (when kom-fontify-text
    (lyskom-format-plaintext-fonts-body)))


(defun lyskom-signal-reformatted-text (how)
  "Signal that the last text was reformatted HOW, which should be a string
in lyskom-messages."
  (or (member how lyskom-last-text-format-flags)
      (setq lyskom-last-text-format-flags (cons how lyskom-last-text-format-flags))))


(lyskom-with-external-functions (w3m-region)
  (defun lyskom-w3m-region (start end &rest args)
    (lyskom-render-html-region start end 'w3m-region)))

(lyskom-with-external-functions (w3-region)
  (defun lyskom-w3-region (start end &rest args)
    (lyskom-render-html-region start end 'w3-region)))

(defun lyskom-render-html-region (start end fun)
  (unwind-protect
      (condition-case var
          (save-restriction
            (let ((buffer-read-only nil))
              (setq start (set-marker (make-marker) start))
              (setq end (set-marker (make-marker) end))
              (narrow-to-region start end)
              (when kom-w3-simplify-body
                (save-excursion
                  (let ((case-fold-search t))
                    (goto-char start)
                    (while (re-search-forward "<body[^>]*>" end t)
                      (replace-match "<body>")))))
              (funcall fun start end)
              (add-text-properties (point-min) (point-max) '(end-closed nil))
	      (set-marker start nil)
	      (set-marker end nil)))
        (error (lyskom-ignore var)))))


(defun lyskom-format-html-plaintext (text text-stat)
  (lyskom-button-transform-text text text-stat))

(defun lyskom-format-html (text text-stat package function)
  ;; Find settings for this author
  (let ((author-setting (and text-stat 
			     (lyskom-indirect-assq (text-stat->author
						    text-stat)
						   kom-format-html-authors))))
    (when (and author-setting
               (condition-case e (progn (require package) t) (error nil)))
      (add-text-properties 0 (length text) `(special-insert ,function) text)
      (lyskom-signal-reformatted-text 'reformat-html)
      (if (string-match "^html:" text)
          (substring text 5)
        text))))

(defun lyskom-format-html-w3 (text text-stat)
  (lyskom-format-html text text-stat 'w3 'lyskom-w3-region))

(defun lyskom-format-html-w3m (text text-stat)
  (lyskom-format-html text text-stat 'w3m 'lyskom-w3m-region))

(defun lyskom-format-pdf (text text-stat)
  (lyskom-signal-reformatted-text 'reformat-pdf)
  (if (fboundp 'doc-view-mode)
      (lyskom-format 'view-pdf (lyskom-default-button 'view-pdf (text-stat->text-no text-stat)))
    (lyskom-format 'pdf-no-show)))

(defun lyskom-format-audio (text text-stat)
  (lyskom-signal-reformatted-text 'reformat-audio)
  (lyskom-format 'audio-no-show (lyskom-text-stat-content-type text text-stat)))

(defun lyskom-format-video (text text-stat)
  (lyskom-signal-reformatted-text 'reformat-video)
  (lyskom-format 'video-no-show (lyskom-text-stat-content-type text text-stat)))

(defun lyskom-format-multipart (text text-stat)
  (lyskom-signal-reformatted-text 'reformat-multipart)
  (lyskom-format 'multipart-no-show (lyskom-text-stat-content-type text text-stat)))

(defun lyskom-format-message (text text-stat)
  (lyskom-signal-reformatted-text 'reformat-message)
  (lyskom-format 'message-no-show (lyskom-text-stat-content-type text text-stat)))

(defun lyskom-format-model (text text-stat)
  (lyskom-signal-reformatted-text 'reformat-model)
  (lyskom-format 'model-no-show (lyskom-text-stat-content-type text text-stat)))

(defun lyskom-format-application (text text-stat)
  (lyskom-signal-reformatted-text 'reformat-application)
  (lyskom-format 'application-no-show (lyskom-text-stat-content-type text text-stat)))

(defun lyskom-format-unknown (text text-stat)
  (lyskom-signal-reformatted-text 'reformat-unknown)
  (lyskom-format 'unknown-no-show (lyskom-text-stat-content-type text text-stat)))

(defun lyskom-format-image (text text-stat)
  (if kom-format-show-images
      (let* ((cti (lyskom-get-aux-item (text-stat->aux-items text-stat) 1))
	     (content-type (and cti (aux-item->data (car cti))))
	     (msg "")
	     (imagetype (intern 
			 ; FIXME: Can the media type contain more than letters and -?
			 (string-replace-match "^.*/\\([-a-zA-Z]*\\)\\(.\\|\n\\)*" 
					       content-type "\\1")))
	     (imagedata text))
	(condition-case nil
	    (lyskom-xemacs-or-gnu
	     (set-extent-end-glyph ; XEmacs
	      (make-extent (point) (point-max))
	      (make-glyph
	       (make-image-instance (vector imagetype :data imagedata))))
	     ; GNU Emacs
	     (unless (and (lyskom-display-images-p)
			  (lyskom-put-image 
			   (lyskom-create-image imagedata imagetype t) 
			   (point-max)))
	       (setq msg (lyskom-format 'image-no-show
					(lyskom-text-stat-content-type text text-stat)
					(format "https://jskom.osd.se/texts/?text=%d" (text-stat->text-no text-stat))
					(format "https://jskom.lysator.liu.se/texts/?text=%d" (text-stat->text-no text-stat))))))
	       ; Errors just marks it as a no show
	  (error (setq msg (lyskom-format 'image-no-show
					  (lyskom-text-stat-content-type text text-stat)
					  (format "https://jskom.osd.se/texts/?text=%d" (text-stat->text-no text-stat))
					  (format "https://jskom.lysator.liu.se/texts/?text=%d" (text-stat->text-no text-stat)))))) 
	(lyskom-signal-reformatted-text 'reformat-image)
	msg)))

(defun lyskom-format-enriched (text text-stat)
  (if (not (fboundp 'format-decode-buffer))
      nil
    (let ((tmpbuf (lyskom-generate-new-buffer "lyskom-enriched")))
      (unwind-protect
          (save-current-buffer
            (set-buffer tmpbuf)
            (if (lyskom-get-aux-item (text-stat->aux-items text-stat) 10002)
                (insert (aux-item->data (car (lyskom-get-aux-item (text-stat->aux-items text-stat) 10002))))
              (insert "Content-Type: text/enriched\n\n"))
            (if (lyskom-get-aux-item (text-stat->aux-items text-stat) 1)
                (insert text)
              (insert (substring text 10)))
            (format-decode-buffer)
            (lyskom-signal-reformatted-text 'reformat-enriched)
            (lyskom-button-transform-text (buffer-string) text-stat)
            ;; (substring (buffer-string) 0 -1) ; Remove the \n
            )
        (kill-buffer tmpbuf)))))

(defun lyskom-format-ö (text text-stat)
  (cond ((string= text "") "")
        (t
         (save-current-buffer
           (set-buffer (lyskom-get-buffer-create 'lyskom-text 
                                                 " lyskom-text" t))
           (erase-buffer)
           (insert text)
           (goto-char (point-min))
           (while (not (looking-at "\\'"))
             (save-restriction
               (narrow-to-region (point)
                                 (save-excursion (end-of-line) (point)))
               (lyskom-fill-region (point-min) (point-max))
               (goto-char (point-max)))
             (forward-line 1))
           (lyskom-signal-reformatted-text 'reformat-filled)
           (if kom-text-properties
               (lyskom-button-transform-text (buffer-string) text-stat)
             (buffer-substring (point-min) (1- (point-max))))))))

(defvar lyskom-doc-view-text-no)
(defvar lyskom-doc-view-temp-file)

(defun lyskom-doc-view-end ()
  (when (boundp 'lyskom-doc-view-temp-file)
    (condition-case nil
        (delete-file lyskom-doc-view-temp-file)
      (error nil))))

(defun lyskom-doc-view-text (text-no)
  (let ((buf (lyskom-traverse buf (lyskom-buffers-of-category 'doc-view)
               (with-current-buffer buf
                 (when (and (boundp 'lyskom-doc-view-text-no)
                            (eq lyskom-doc-view-text-no text-no))
                   (lyskom-traverse-break buf))))))
    (if buf
        (lyskom-display-buffer buf)
      (let* ((text (blocking-do 'get-text text-no))
             (text-stat (blocking-do 'get-text-stat text-no))
             (buf (lyskom-get-buffer-create 'doc-view "lyskom-view-document"))
             (str (text->decoded-text-mass text text-stat))
             (subj "")
             (temp-file (make-temp-file "lyskom")))
    
        (when (string-match "\n" str)
          (setq subj (substring str 0 (match-beginning 0)))
          (setq str (substring str (match-end 0))))
        (lyskom-save-excursion
          (set-buffer buf)
          (erase-buffer)
          (insert str)
          (set-buffer-file-coding-system 'raw-text)
          (write-file (or buffer-file-name temp-file))
          (rename-buffer (lyskom-format 'doc-view-buffer-name text-no subj))
          (doc-view-mode)
          (make-local-variable 'lyskom-doc-view-temp-file)
          (make-local-variable 'lyskom-doc-view-text-no)
          (setq lyskom-doc-view-temp-file temp-file)
          (setq lyskom-doc-view-text-no text-no)
          (add-hook 'kill-buffer-hook 'lyskom-doc-view-end nil t))
        (lyskom-display-buffer buf)))))



(defun lyskom-get-holerith-list (text &optional no-coding)
  "Assume that TEXT is a list of holerith strings. Return those strings."
  (let (result tmp)
    (while (string-match "\\S-" text)
      (setq tmp (lyskom-get-holerith text no-coding))
      (setq result (cons (car tmp) result)
            text (cdr tmp)))
    (nreverse result)))

(defun lyskom-get-holerith-assoc (text &optional no-coding)
  "Assume that TEXT is a list of holerith strings. Return those strings."
  (let (result tmp key value)
    (while (string-match "\\S-" text)
      (setq tmp (lyskom-get-holerith text no-coding))
      (setq key (car tmp) text (cdr tmp))
      (setq tmp (lyskom-get-holerith text no-coding))
      (setq value (car tmp) text (cdr tmp))
      (setq result (cons (cons key value) result)))
    (nreverse result)))

(defun lyskom-split-user-area (text)
  "Return the user area split into components."
  (let ((tmp (lyskom-get-holerith text t)))
    (lyskom-mapcar2 'cons  
             (lyskom-get-holerith-list (car tmp) t)
             (lyskom-get-holerith-list (cdr tmp) t))))

(defun lyskom-format-x-kom/user-area-data (data)
  (let* ((values (lyskom-get-holerith-assoc data t))
         (maxlen (apply 'max (mapcar (lambda (el) (length (car el))) values)))
         (fs (format "%%-%ds%%s" (+ maxlen 2))))
    (mapconcat (lambda (el) (format fs
                                    (concat (car el) ": ")
                                    (cdr el))) values "\n")))


(defun lyskom-format-x-kom/user-area (text text-stat)
;  (condition-case nil
      (let ((result "")
            (user-area (lyskom-split-user-area text)))
        (while user-area
          (let ((key (car (car user-area)))
                (data (cdr (car user-area))))
            (setq user-area (cdr user-area))
            (setq result
                  (concat result
                          key "\n" (make-string (length key) ?=) "\n\n"
                          (cond ((equal key "common")
                                 (lyskom-format-x-kom/user-area-data data))
                                ((equal key "elisp")
                                 (lyskom-format-x-kom/user-area-data data))
                                (t data))
                          (if user-area "\n\n\n" "")))))
        result)


;    (error (concat "Unable to parse user area\n\n" text)))
)



;;; ============================================================
;;; lyskom-fill-message
;;; Author: David Byers
;;;
;;; Wrap the lines of a message with long lines so they're a little easier
;;; to read. Try to ignore what looks like preformatted text. 
;;;
;;; Scan the text line by line, and decide whether to fill or not on a 
;;; paragraph by paragraph basis. 
;;;
;;; An empty line ends the current paragraph.
;;; 
;;; An indented line followed by an unindented line ends the current
;;; paragraph and starts a new one. An indented line followed by the
;;; end of the buffer is also considered a paragraph if we have
;;; started a new paragraph based on indentation at least once before.
;;;
;;; A line that starts with a minus or plus starts a new paragraph.
;;; 
;;; An indented line followed by another line indented the same way
;;; starts a new paragraph if we're not already scanning a paragraph.
;;;
;;; Any text seen when not scanning a paragraph starts a new
;;; paragraph.
;;;
;;; When a paragraph is started, filling may be enabled or disabled or 
;;; set in a "maybe" state.
;;; 
;;; A line that does not look like it belongs to the current paragraph 
;;; because it is inndented incorrectly or because it starts with a
;;; strange character disables filling for the entire paragraph.
;;;
;;; A line that is wider than the window enables filling for the
;;; paragraph, unless filling has been disabled earlier.
;;;
;;; Any line containing three whitespace characters in a row, a space
;;; followed by a tab, or a tab followed by a space, or two tabs in a
;;; row, or the beginning or end of a C comment or four hyphens
;;; disables filling for the entire paragraph.
;;;
;;; A paragraph is not filled if filling has been disabled, or if the
;;; difference in line lengths from paragraph to paragraph is
;;; constant.
;;;

(defconst lyskom-minimum-triangle-size 3
  "Minimum number of lines in a triangle or suchlike.")

(defconst lyskom-minimum-brick-size 2
  "Minimum number of lines in a brick.")

(defun lyskom-fill-region (start end &optional justify nosqueeze to-eop)
  "Fill a region of text, compensating for bugs in Emacs."
  (save-match-data
    (let ((fill-column (if nosqueeze (1- fill-column) fill-column)))
      (when nosqueeze
        (condition-case nil
            (save-excursion
              (goto-char (match-beginning 0))
              (backward-char 1)
              (delete-horizontal-space))
          (error nil)))
      (condition-case nil
          (fill-region start (min end (point-max)) justify nosqueeze to-eop)
        (error nil)))))

(defvar lyskom-fill-disqualifying-regexp
  (let ((res '((triple-whitespace "\\S-[ \t][ \t][ \t]+\\S-")
               (space-tab "\\S-[ \t]* \t[ \t]*\\S-")
               (tab-space "[ \t]*\t [ \t]*")
               (tab-tab "\\S-\\s-*\t\t\\s-*\\S-")
               (multi-dash "----")
               (log-line "[a-zA-Z0-9.]+\\[[0-9]+\\]")
               (c-comment-start "/\\*")
               (c-comment-end "\\*/")
               (line-comment "[^:]//"))))
    (concat "\\("
            (mapconcat (lambda (el) (car (cdr el))) res "\\|")
            "\\)")))


(defmacro lyskom-fill-message-next-line ()
  `(setq next-line t))

(defmacro lyskom-fill-message-return ()
  `(throw 'lyskom-fill-message-return t))

(defsubst lyskom-fill-message-line-length ()
  (- (save-excursion (end-of-line)
                     (skip-chars-backward " \t")
                     (current-column)) (current-column)))

(defmacro lyskom-maybe-fill-region (start end &optional justify nosqueeze to-eop)
  "Fill the region from START to END if the prerequisites for doing
so are met. This macro checks on the internal state of 
lyskom-fill-next-paragraph to see if the text scanned should be filled
or not."
  `(unless (or (<= ,end ,start)
               (<= paragraph-width fill-column)
               quoted-paragraph
               special-characters
               (eq all-colon-lines t)
               (and geometric-text
                    (or (and (eq 0 length-difference)
                             (>= paragraph-length lyskom-minimum-brick-size))
                        (and (not (eq 0 length-difference))
                             (>= paragraph-length lyskom-minimum-triangle-size)))))
     (let ((fill-prefix fill-prefix))
       (when indented-paragraph
         (setq fill-prefix
               (concat (or fill-prefix "")
                       indented-paragraph)))
       (when bulleted-paragraph
         (setq fill-prefix
               (concat (or fill-prefix "")
                       (make-string (length bulleted-paragraph) ?\ ))))
       (lyskom-fill-region ,start ,end ,justify ,nosqueeze ,to-eop)
       (lyskom-signal-reformatted-text 'reformat-filled)
       t)))

(defvar lyskom-fill-inline-text-regexp 
  (concat "^\\("
          (mapconcat (lambda (language)
                       (regexp-quote (lyskom-get-string
                                      'subject nil (car language))))
                     lyskom-languages
                     "\\|")
          "\\).*\n----"))


(defvar lyskom-fill-cancel)
(defun lyskom-fill-message (text)
  (cond 
   ((null kom-autowrap) text)
   ((and (numberp kom-autowrap) (> (length text) kom-autowrap)) text)
   (t (save-current-buffer
        (set-buffer (lyskom-get-buffer-create 'lyskom-text " lyskom-text" t))
        (erase-buffer)
        (insert text)
        (goto-char (point-min))

        (let* ((fill-column 
                (cond ((not (integerp fill-column)) (- (window-width) 5))
                      ((> fill-column (- (window-width) 5)) (- (window-width) 5))
                      (t fill-column)))
               (fill-prefix nil)
               (lyskom-fill-cancel (cons nil nil))
               (timer (and kom-autowrap-timeout
                           (run-at-time kom-autowrap-timeout nil
                                        (lambda (obj) (setcdr obj t)) lyskom-fill-cancel))))
          (condition-case nil
              (while (not (eobp)) (lyskom-fill-next-paragraph))
            (quit nil)
            (error nil)))

        (goto-char (point-max))
        (skip-chars-backward " \t\r\n")

        (let* ((pos (1- (point-max))))
          (while (and (> pos 0) 
                      (progn (goto-char pos)
                             (looking-at "[ \t\n\r]")))
            (setq pos (1- pos)))
          (buffer-substring (point-min) (1+ pos)))))))


(defun lyskom-fill-next-paragraph ()
  "Scan and possibly fill the next paragraph in the current buffer."
  (let* ((start (point))
         (next-char nil)
         (paragraph-length 0)
         (paragraph-width 0)
         (eol-point 0)
         (next-line nil)

         ;; Variables used for geometric texts
         (geometric-text t)
         (current-line-length nil)
         (last-line-length nil)
         (length-difference nil)

         ;; Variables used for special characters
         (special-characters nil)

         ;; Variables used for quoted paragraphs
         (quoted-paragraph nil)

         ;; Variables used for bulleted paragraphs
         (bulleted-paragraph nil)

         ;; Variables used for indented paragraphs
         (indented-paragraph nil)

         ;; Variables used for MIME headers
         (all-colon-lines 'undef)
         )

    ;;
    ;; The end of a paragraph is signalled by throwing to
    ;; here. This simplifies the conditions within the code
    ;; quite considerably.
    ;;

    (catch 'lyskom-fill-message-return

      ;;
      ;; Loop until the end of the buffer
      ;;

      (while (not (eobp))
        (setq next-line nil)

        ;;
        ;; Code can throw to here to prevent further processing
        ;; of a line of input. This is used when a line has been
        ;; completely categorized.
        ;;
        ;; FIXME: Paragraph-width is only used when starting to fill a paragraph, so we should
        ;; FIXME: compute it only at that point. 
        ;;
        ;; FIXME: If we can move the special patterns check, we can lose eol-point as well, and
        ;; FIXME: only compute it when it's needed to calculate paragraph-width.

        (setq next-char (char-after (point)))
        (setq eol-point (save-excursion (end-of-line) (point)))
        (setq paragraph-width (max paragraph-width (- eol-point (point))))

        ;;
        ;; Handle unconditional paragraph break (empty line)
        ;;
        ;; An empty line signals the end of the paragraph, always.
        ;;

        (when (looking-at "^\\s-*$")
          (lyskom-maybe-fill-region start (1- (point)) nil t)
          (lyskom-fill-scan-empty-lines)
          (lyskom-fill-message-return))


        ;;
        ;; Detect special patterns
        ;;
        ;; Here, we look for lines that have patterns that disqualify
        ;; the entire paragraph from being filled. This should catch
        ;; source code and many preformatted things.
        ;;
        ;; FIXME: It is probably possible to do this once when starting
        ;; FIXME: to fill the text. Alternately, we could scan forward
        ;; FIXME: for a para break as soon as we spot this.
        ;; FIXME:
        ;; FIXME: I suspect that indented paragraphs may trigger this
        ;; FIXME: rule.

        (when (save-excursion 
                (re-search-forward lyskom-fill-disqualifying-regexp eol-point t))
          (setq special-characters t))

        (when (looking-at lyskom-fill-inline-text-regexp)
          (setq special-characters t))


        ;;
        ;; Handle unconditional paragraph break (bulleted text)
        ;;
        ;; Bullet items end the current paragraph unconditionally.
        ;; If we have seen text before the item, bail out of here,
        ;; causing the same line to be revisited the next time
        ;; the function is called. Otherwise, keep scanning, but
        ;; set a flag.
        ;;

        (when (and (not next-line) 
                   (looking-at "^\\s-*\\([-*+#]\\|[§#]?\\s-*[0-9]+[.):]?\\)\\s-*"))
          (when (lyskom-maybe-fill-region start (1- (point)) nil t)
            (lyskom-fill-message-return))
          (setq bulleted-paragraph (match-string 0))
          (lyskom-fill-message-next-line))

        ;;
        ;; Handle indented paragraph
        ;;
        ;; - It can be a continuation of a bullet point
        ;; - It can be a continuation of an indented para
        ;; - It can terminate a non-indented para
        ;; - It can continue an all-colon line
        ;;

        (when (and (not next-line) (looking-at "^\\(\\s-+\\)\\S-"))
          (cond (bulleted-paragraph (lyskom-fill-message-next-line))
                ((equal (match-string 1) indented-paragraph)
                 (lyskom-fill-message-next-line))
                ((eq all-colon-lines t) (lyskom-fill-message-next-line))
                ((lyskom-maybe-fill-region start (1- (point)) nil t)
                 (lyskom-fill-message-return))
                (t (setq indented-paragraph (match-string 1)))))

        ;;
        ;; If we have seen indentation, but don't see it now,
        ;; then this isn't an indented paragraph. It's just
        ;; a paragraph with first-line indentation.
        ;;

        (when (and (not next-line) indented-paragraph (looking-at "^\\S-"))
          (setq indented-paragraph nil))


        ;;
        ;; Handle unconditional paragraph break (quoted text)
        ;;
        ;; Quoted text also results in an unconditional paragraph
        ;; break. This must come after handling of indented lines
        ;; since whitespace is not considered a legal line start
        ;; character.
        ;;

        (when (and (not next-line)
                   (not (looking-at "^\\s-"))
                   (< (lyskom-char-to-int next-char)
                      (length lyskom-line-start-chars))
                   (not (aref lyskom-line-start-chars 
                              (lyskom-char-to-int next-char))))
          (lyskom-maybe-fill-region start (1- (point)) nil t)
          (lyskom-fill-scan-quoted-paragraph next-char)
          (lyskom-fill-message-return))

        ;;
        ;; Handle header-like stuff
        ;;
        ;; This rule detects mail-header-like stuff
        ;;

        (if (and (not next-line) 
                 all-colon-lines
                 (looking-at "^\\S-+\\s-*:"))
            (setq all-colon-lines t)
          (setq all-colon-lines nil))

        ;; Handle geometric paragraphs
        ;;
        ;; Geometric paragraphs have constant differences in line
        ;; length. There is also a requirement of minimum size,
        ;; which is enforced in lyskom-maybe-fill-region
        ;;

        (when (and (not next-line) geometric-text)
          (setq current-line-length (lyskom-fill-message-line-length))
          (cond ((and (null length-difference)
                      current-line-length
                      last-line-length)
                 (setq length-difference (- current-line-length last-line-length)))

                ((and current-line-length
                      last-line-length
                      (/= (- current-line-length last-line-length)
                          length-difference))
                 (setq geometric-text nil))))





        (setq last-line-length current-line-length)
        (end-of-line)
        (setq paragraph-length (1+ paragraph-length))
        (unless (eobp)
          (forward-line 1)
          (beginning-of-line)
          (when kom-autowrap-timeout 
            (sit-for 0)
            (and (cdr lyskom-fill-cancel) (error "Out of time"))))

        ) ;; while

      (lyskom-maybe-fill-region start (point) nil t)

      ) ;; catch lyskom-fill-message-return

    ))


(defun lyskom-fill-scan-quoted-paragraph (char)
  (while (and (eq (char-after (point)) char) (not (eobp)))
    (end-of-line)
    (unless (eobp)
      (forward-line 1)
      (beginning-of-line))))

(defun lyskom-fill-scan-empty-lines ()
  (while (and (looking-at "^\\s-*$") (not (eobp)))
    (end-of-line)
    (unless (eobp)
      (forward-line 1)
      (beginning-of-line))))


;;; ============================================================
;;;                     Beeping and feeping
;;;                     Faces and colors
;;;

(defun lyskom-beep (arg &optional optarg)
  "Beep. ARG is how to beep. Optional OPTARG is a modifier. 
nil means don't beep.
t means beep once.
A number means beep that number of times (.1 second delay between beeps).
A string means start the command kom-audio-player with the string as argument.
A symbol other than t means call it as a function.
A list of pairs means OPTARG will be used as a key to look up the real
  value of ARG. The special key t is used when OPTARG is not found."
  (setq optarg
	(cond ((lyskom-conf-stat-p optarg) (conf-stat->conf-no optarg))
	      ((lyskom-uconf-stat-p optarg) (uconf-stat->conf-no optarg))
	      (t optarg)))

  (cond ((null arg))
        ((eq t arg) (ding t))
        ((numberp arg) (while (> arg 0)
                         (ding t)
                         (sit-for kom-ding-pause-amount)
                         (setq arg (1- arg))))
        ((stringp arg) (condition-case nil
                           (start-process "audio" nil kom-audio-player arg)
                         (error nil)))
        ((and (symbolp arg) (fboundp arg))
         (condition-case nil
             (funcall arg)
           (error (message "Error in beep function")
                  (beep))))
        ((and (listp arg)
              (or (assq optarg arg)
                  (assq t arg)))
         (lyskom-beep (cdr (or (assq optarg arg)
                               (assq t arg)))
                      optarg))
        (t (beep))))

(defun lyskom-face-default-p (f1)
  "Return t if f1 is undefined or the default face."
  (not (and (lyskom-find-face f1)
            (face-differs-from-default-p f1))))

;;;; ================================================================
;;;;                         Running in buffer 

;;; Author: Linus


(defun lyskom-text-at-point ()
  "Return the text that point is in, or nil it is impossible to determine."
  (save-excursion
    (lyskom-prev-area 1 'lyskom-text-start)
    (get-text-property (point) 'lyskom-text-start)))

(defun kom-next-prompt (num)
  "Move the cursor to the next prompt in the LysKOM buffer"
  (interactive "p")
  (lyskom-next-area num 'lyskom-prompt))


(defun kom-prev-prompt (num)
  "Move the cursor to the previous prompt in the LysKOM buffer"
  (interactive "p")
  (beginning-of-line)
  (lyskom-prev-area num 'lyskom-prompt))
  


(defun kom-backward-text (&optional arg)
  "Searches backwards for a text start and recenters with that text at the top."
  (interactive "p")
  (lyskom-prev-area (or arg 1) 'lyskom-text-start t)
  (beginning-of-line))

(defun kom-forward-text (&optional arg)
  "Searches forward for a text start and recenters with that text at the top."
  (interactive "p")
  (lyskom-next-area (or arg 1) 'lyskom-text-start t))


(def-kom-command kom-save-text-body (text-no &optional filename)
  "Save the body of the selected text to a file. This command saves
the contents of the text, without LysKOM information around it to a
file, overwriting any previous contents.

See `kom-save-text' for an alternative command."
  (interactive (list (lyskom-read-text-no-prefix-arg 'what-save-no)
                     nil))
  (cond (text-no
         (blocking-do-multiple ((text-stat (get-text-stat text-no))
                                (text (get-text text-no)))
           (if (or (null text-stat) (null text))
               (lyskom-format-insert 'no-such-text-no text-no)
             (let* ((mx-filename (lyskom-get-aux-item (text-stat->aux-items text-stat) 10104))
                    (filename  nil))
               (while (null filename)
                 (setq filename
                       (read-file-name (lyskom-format 'save-text-to-file-q text-no)
                                       (and mx-filename
                                            (file-name-directory (aux-item->data (car mx-filename))))
                                       nil
                                       nil
                                       (and mx-filename
                                            (aux-item->data (car mx-filename)))))
                 (if (file-directory-p filename)
                     (setq filename nil)
                   (if (or (not (file-exists-p filename))
                           (prog1 
                               (lyskom-j-or-n-p
                                (lyskom-format 'save-text-confirm filename))
                             (lyskom-message "")))
                       (let ((buf (lyskom-get-buffer-create 'temp " *kom*-text" t))
                             (str (text->decoded-text-mass text text-stat)))
                         (condition-case nil
                             (progn
                               (lyskom-format-insert 'saving-text text-no filename)
                               (when (string-match "\n" str)
                                 (setq str (substring str (match-end 0))))
                               (save-current-buffer
                                 (set-buffer buf)
                                 (erase-buffer)
                                 (insert str)
				 (let* ((cti (lyskom-get-aux-item (text-stat->aux-items text-stat) 1))
					(content-type (and cti (aux-item->data (car cti))))
					(charset (cdr (assoc 'charset 
							     (cdr (lyskom-mime-decode-content-type 
								   content-type)))))
					(coding-system-for-write (or charset
								     lyskom-server-coding-system)))
                                 (write-region (point-min) (point-max) filename)))

                               (lyskom-insert (lyskom-get-string 'done)))
                           (quit (lyskom-insert (lyskom-get-string 'cancelled)))
                           (error (lyskom-insert (lyskom-get-string 'nope))))))))))))
        (t (lyskom-insert 'confusion-what-to-save))))


(def-kom-command kom-save-text (arg &optional list-of-texts filename)
  "Append the selected texts to a file. The texts to save are
determined by searching backwards in the buffer. A numeric prefix
argument specifies the number of texts to save. Headers are included
in the file.

The file name to save to is read using the minibuffer, and the default
is taken from kom-saved-file-name the first time. Subsequent calls use
the most recently specified file name.

See `kom-save-text-body' for an alternative to this command."
  (interactive "p")
  (let ((name nil))
    (save-excursion
      (while (and arg (> arg 0))
        (kom-backward-text 1)
        (if (looking-at "\\([0-9]+\\)\\s-")
            (setq list-of-texts (cons (lyskom-string-to-number (match-string 1))
                                      list-of-texts)
                  arg (1- arg))
          (setq arg 0)))
      (setq name (expand-file-name
                  (or filename
                      (read-file-name
                       (if (eq 1 (length list-of-texts))
                           (lyskom-format 'save-one-on-file-q (car list-of-texts))
                         (lyskom-format 'save-many-on-file-q (length list-of-texts)))
                       (file-name-directory (or lyskom-saved-file-name kom-saved-file-name))
                       nil
                       nil
                       (file-name-nondirectory (or lyskom-saved-file-name kom-saved-file-name))))))
      (cond ((file-directory-p name)
             (lyskom-format-insert 'cant-save-to-directory name))
            (t 
             (set-buffer lyskom-buffer)
             (if (eq 1 (length list-of-texts))
                 (lyskom-format-insert-before-prompt 'saving-one-on-file 
                                       (car list-of-texts)
                                       name)
               (lyskom-format-insert-before-prompt 'saving-many-on-file
                                     (length list-of-texts)
                                     name))
             (mapc (lambda (n)
                       (blocking-do-multiple ((text-stat (get-text-stat n))
                                              (text (get-text n)))
                         (lyskom-save-text text-stat text name)))
                     list-of-texts)
             (setq lyskom-saved-file-name name))
            ))))

(defun lyskom-save-text (text-stat text filename)
  "Save text in TEXT-STAT and TEXT to FILENAME."
  (let* ((kom-print-relative-dates nil)
         (kom-deferred-printing nil)

         ;; This has to come last
         (buf (lyskom-get-buffer-create 'temp "*kom*-text" t)))
    (save-current-buffer
      (set-buffer buf)
      (erase-buffer)
      (lyskom-view-text (text-stat->text-no text-stat))
      (append-to-file (point-min) (point-max) filename))))



;;; ================================================================
;;;                             To-do


(defvar lyskom-recursive-prompt-update nil)
(defun lyskom-update-all-prompts (&optional force-prompt-update)
  "Update the prompts in all buffers"
  (unless lyskom-recursive-prompt-update
    (let ((lyskom-recursive-prompt-update t))
      (save-excursion
        (lyskom-traverse buffer lyskom-buffer-list
          (set-buffer buffer)
          (lyskom-update-prompt force-prompt-update))
        (lyskom-set-default 'lyskom-need-prompt-update nil)))))

(defun lyskom-update-prompt (&optional force-prompt-update)
  "Print prompt if the client knows which command will be default.
Set lyskom-current-prompt accordingly. Tell server what I am doing."
  (let ((was-at-max (eq (point) (point-max)))
        (saved-suffix (if lyskom-slow-mode
                          (save-excursion (lyskom-get-entered-slow-command))
                        nil)))
    (if (or lyskom-executing-command
            (and lyskom-current-prompt
                 lyskom-dont-change-prompt))
        nil
      (let ((to-do (lyskom-what-to-do))
            (prompt nil)
            (prompt-args nil))
        (setq lyskom-command-to-do to-do)
        (cond

         ((eq to-do 'next-pri-conf)
          (setq prompt
                (cond ((eq lyskom-pers-no
                           (conf-stat->conf-no
                            (read-info->conf-stat (read-list->first
                                                   lyskom-to-do-list))))
                       'go-to-pri-mailbox-prompt)
                      (t 'go-to-pri-conf-prompt)))

          (or (eq lyskom-current-prompt prompt)
              (lyskom-beep kom-ding-on-priority-break)))

         ((eq to-do 'reedit-text)
          (setq prompt 're-edit-text-prompt))

         ((and (eq to-do 'next-pri-session)
               (lyskom-get-prioritized-session))
          (if (and (read-list-isempty lyskom-reading-list)
                   (read-list-isempty lyskom-to-do-list))
              (setq prompt 'next-unread-session-prompt)
            (setq prompt 'next-pri-session-prompt))
          (setq prompt-args 
                (save-current-buffer
                  (set-buffer (lyskom-get-prioritized-session))
                  (list (lyskom-session-nickname)))))

         ((eq to-do 'next-pri-text)
          (setq prompt
                (cond ((eq (conf-stat->conf-no
                            (read-info->conf-stat (read-list->first
                                                   lyskom-to-do-list)))
                           lyskom-pers-no)
                       'read-pri-letter-prompt)
                      (t 'read-pri-text-conf)))

          (or (eq lyskom-current-prompt prompt)
              (lyskom-beep kom-ding-on-priority-break)))

         ((eq to-do 'next-text)
          (setq prompt
                (let ((read-info (read-list->first lyskom-reading-list)))
                  (cond
                   ((eq 'REVIEW (read-info->type read-info))
                    'review-next-text-prompt)
                   ((eq 'REVIEW-TREE (read-info->type read-info))
                    'review-next-comment-prompt)
                   ((eq 'REVIEW-MARK (read-info->type read-info))
                    'review-next-marked-prompt)
                   ((or (eq 'REVIEW-FAQ (read-info->type read-info))
                        (eq 'REVIEW-FAQ-TREE (read-info->type read-info)))
                    'review-next-faq-prompt)
                   ;; The following is not really correct. The text to be
                   ;; read might be in another conference.
                   ((= lyskom-current-conf lyskom-pers-no)
                    'read-next-letter-prompt)
                   ((lyskom-get-aux-item (text-stat->aux-items
                                          (cache-get-text-stat
                                           (car-safe
                                            (text-list->texts
                                             (read-info->text-list
                                              read-info)))))
                                         10100)
                    'read-next-attachment-prompt)
                   ((eq 'FOOTN-IN (read-info->type read-info))
                    'read-next-footnote-prompt)
                   ((eq 'COMM-IN (read-info->type read-info))
                    'read-next-comment-prompt)
                   (t
                    'read-next-text-prompt)))))

         ((eq to-do 'next-conf)
          (setq prompt
                (cond
                 ((eq 'REVIEW-MARK 
                      (read-info->type (read-list->first lyskom-to-do-list)))
                  'go-to-conf-of-marked-prompt)
                 ((eq 'REVIEW
                      (read-info->type (read-list->first lyskom-to-do-list)))
                  'go-to-conf-of-review-prompt)
                 ((eq 'REVIEW-FAQ
                      (read-info->type (read-list->first lyskom-to-do-list)))
                  'go-to-conf-of-review-faq-prompt)
                 ((eq 'REVIEW-FAQ-TREE
                      (read-info->type (read-list->first lyskom-to-do-list)))
                  'go-to-conf-of-review-faq-prompt)
                 ((eq 'REVIEW-TREE 
                      (read-info->type (read-list->first lyskom-to-do-list)))
                  'go-to-conf-of-review-tree-prompt)
                 ((/= lyskom-pers-no
                      (conf-stat->conf-no
                       (read-info->conf-stat (read-list->first
                                              lyskom-to-do-list))))
                  'go-to-next-conf-prompt)
                 (t
                  'go-to-your-mailbox-prompt))))
    
         ((eq to-do 'when-done)
          (if (not lyskom-is-writing)
              (lyskom-tell-server kom-mercial))
          (setq prompt
                (let ((command (lyskom-what-to-do-when-done t)))
                  (cond			    
                   ((lyskom-command-name command lyskom-language))
                   ((and (stringp command)
                         (lyskom-command-name (key-binding command) lyskom-language)))
                   (t (lyskom-format 'the-command command))))))
     
         ((eq to-do 'unknown)		;Pending replies from server.
          (setq prompt nil))
     
         (t (message "%s" to-do)
            (setq prompt "???")))

        (when (or force-prompt-update
                  (not (equal prompt lyskom-current-prompt)))
          (let ((inhibit-read-only t)
                (prompt-text
                 (if prompt
                     (lyskom-modify-prompt
                      (apply 'lyskom-format
                             (cond
                              ((symbolp prompt) (lyskom-get-string prompt))
                              (t prompt))
                             prompt-args))
                   "")))
            (save-excursion
              ;; Insert the new prompt
              (goto-char (point-max))
              (beginning-of-line)
              (lyskom-xemacs-or-gnu
               (let ((extent 
                      (make-extent 0 (length prompt-text)
                                   prompt-text)))
                 (set-extent-property extent 'read-only t)
                 (set-extent-property extent 'rear-nonsticky t)
                 (set-extent-property extent 'duplicable nil))

               (add-text-properties 0 (length prompt-text)
                                    '(read-only t rear-nonsticky t)
                                    prompt-text))
              (insert prompt-text)
              ;; Delete the old prompt
              (when lyskom-current-prompt
                (if (and lyskom-slow-mode 
                         (looking-at 
                          (regexp-quote lyskom-current-prompt-text)))
                    (delete-region (point) (match-end 0))
                  (delete-region (point) (point-max))
                  (if saved-suffix (insert saved-suffix)))))
            (when was-at-max
              (goto-char (point-max)))

            (setq lyskom-current-prompt prompt)
            (setq lyskom-current-prompt-args prompt-args)
            (setq lyskom-current-prompt-text prompt-text))))
      (lyskom-set-mode-line))))

(defun lyskom-modify-prompt (s &optional executing)
  (let ((text (lyskom-format-prompt (cond (lyskom-is-administrator
                                           (if executing 
                                               kom-enabled-prompt-format-executing
                                             kom-enabled-prompt-format))
                                          (lyskom-is-anonymous
                                           (if executing
                                               kom-anonymous-prompt-format-executing
                                             kom-anonymous-prompt-format))
                                          (t (if executing
                                                 kom-user-prompt-format-executing
                                               kom-user-prompt-format)))
                                    s)))
    (add-text-properties 0 (length text)
			 (append
			  (lyskom-default-button
			   'timestamp lyskom-current-prompt-timestamp
			   (list 'timestamp-popup-title
				 (let ((kom-print-relative-dates nil))
                                   (lyskom-format-time
                                    'date-and-time
                                    lyskom-current-prompt-timestamp))))
			  '(lyskom-prompt t))
			 text)
    (when executing (setq lyskom-current-prompt-timestamp (lyskom-current-client-time)))
    text))
           


(defun lyskom-format-prompt (fmt command)
  (let ((start 0)
        (len (length fmt))
        (result nil)
        (tmp nil)
        (format-letter nil)
        (messages (length lyskom-ansaphone-messages)))
    (while (< start len)
      (setq tmp (string-match "%[][cm Sswp#aA]" fmt start))
      (if tmp
          (progn
            (if (> tmp start)
                (setq result (cons (substring fmt start tmp) result)))
            (setq format-letter (elt fmt (1- (match-end 0))))
            (setq start (match-end 0))
            (setq result 
                  (cons
                   (cond ((eq format-letter ?\[) (if kom-ansaphone-on "[" ""))
                         ((eq format-letter ?\]) (if kom-ansaphone-on "]" ""))
                         ((eq format-letter ?c)  command)
                         ((eq format-letter ?w) 
                          (or (conf-stat->name 
                               (cache-get-conf-stat lyskom-current-conf))
                              (lyskom-format 'conference-no
                                             lyskom-current-conf)))
                         ((eq format-letter ?S) lyskom-server-name)
                         ((eq format-letter ?s)
			  (lyskom-session-nickname))
                         ((eq format-letter ?p)
                          (or (conf-stat->name
                               (cache-get-conf-stat lyskom-pers-no))
                              (lyskom-format 'person-no
                                             lyskom-pers-no)))

                         ((eq format-letter ?#) (number-to-string 
                                                lyskom-session-no))
                         ((eq format-letter ?a)
                          (lyskom-get-string 'anonymous))

                         ((eq format-letter ?A)
                          (lyskom-get-string 'Anonymous))

                         ((eq format-letter ?m)
                          (cond ((< messages 1)
                                 "")
                                ((= messages 1)
                                 (format (lyskom-get-string 
                                          'prompt-single-message) messages))
                                ((> messages 1)
                                 (format (lyskom-get-string
                                          'prompt-several-messages) 
                                         messages))))
                         ((eq format-letter ?%) "%")
                         ((eq format-letter ?\ )
                          'SPC))
                   result)))
        (progn
          (setq result (cons (substring fmt start) result))
          (setq start len))))
    (lyskom-build-prompt (nreverse result))))


(defun lyskom-build-prompt (data)
  (let ((result "")
        (separate nil))
    (while data
      (cond ((stringp (car data))
             (cond ((and separate
                         (string-match "\\S-$" result)
                         (string-match "^\\S-" (car data)))
                    (setq result (concat result " " (car data))))
                   (t (setq result (concat result (car data)))))
             (setq separate nil))

            ((eq (car data) 'SPC)
             (setq separate t)))
      (setq data (cdr data)))
    result))

(defun lyskom-get-prioritized-session ()
  "Get the session to go to if we are doing an auto-goto-session"
  (let ((session-list (if (memq kom-server-priority-breaks
				  '(express-letters letters
						    after-conf-letters))
                          lyskom-sessions-with-unread-letters
                        lyskom-sessions-with-unread))
        (session nil)
        (saved-priority nil))
    (while session-list
      (condition-case nil
          (save-current-buffer
            (set-buffer (car session-list))
            (when (or (null saved-priority)
                      (> kom-server-priority saved-priority))
              (setq session (car session-list)
                    saved-priority kom-server-priority)))
        (error nil))
      (setq session-list (cdr session-list)))
    session))


    
    

(defun lyskom-what-to-do ()
  "Check what is to be done. Return an atom as follows:
	next-pri-text	There is a text with higher priority to be read.
	next-pri-conf	There is a conference with higher priority to be read.
	next-text	There are texts on lyskom-reading-list.
	next-conf	There are texts on lyskom-to-do-list.
        reedit-text     There is an edit buffer with an error.
	when-done	There are no unread texts.
        next-pri-session There is a session with unreads.
	unknown	        There are pending replies."
  (cond

   ;; If session breaks are one ... and ...
   ;;   there is a session with higher priority ... and ...
   ;;   it's not the current session ... and ...
   ;;   we either have express breaks or we're at the end of a comment chain
   ;;   the priority of the other session is higher than what we're reading

   ((let* ((pri-session (lyskom-get-prioritized-session))
           (type (unless (read-list-isempty lyskom-reading-list)
                   (read-info->type (read-list->first lyskom-reading-list))))
           (pri (unless (read-list-isempty lyskom-reading-list) 
                  (read-info->priority (read-list->first lyskom-reading-list))))
           (pri-session-pri (save-excursion (when (and pri-session pri)
                                              (set-buffer pri-session)
                                              kom-server-priority))))
      (and (not (eq pri-session (current-buffer)))
           (or (and (memq kom-server-priority-breaks
			    '(express express-letters))
                    pri-session
                    (or (null pri) (> pri-session-pri pri)))
               (and (eq kom-server-priority-breaks 'when-done)
                    pri-session
                    (read-list-isempty lyskom-reading-list)
                    (read-list-isempty lyskom-to-do-list))
               (and (memq kom-server-priority-breaks '(t letters))
                    pri-session
                    (or (null type)
                        (eq type 'CONF)
                        (eq type 'REVIEW)
                        (eq type 'REVIEW-FAQ)
                        (eq type 'REVIEW-MARK))
                    (or (null pri) (> pri-session-pri pri)))
               (and (memq kom-server-priority-breaks
			    '(after-conf after-conf-letters))
                    pri-session
                    (read-list-isempty lyskom-reading-list)
                    (or (null pri) (> pri-session-pri pri)))
               )))
    'next-pri-session)

   ((and kom-higher-priority-breaks
	 (not (read-list-isempty lyskom-reading-list))
	 (not (read-list-isempty lyskom-to-do-list))
	 (let ((type (read-info->type (read-list->first lyskom-reading-list))))
	   (or (eq kom-higher-priority-breaks 'express)
	       (memq type '(CONF REVIEW REVIEW-MARK REVIEW-FAQ))))
	 (> (read-info->priority (read-list->first lyskom-to-do-list))
	    (read-info->priority (read-list->first lyskom-reading-list))))
    (if (> (text-list->length (read-info->text-list
			       (read-list->first lyskom-to-do-list)))
	   1)
       'next-pri-conf
      'next-pri-text))
   ((and (not (read-list-isempty lyskom-reading-list))
         (eq (read-info->type (read-list->first lyskom-reading-list)) 
             'RE-EDIT-TEXT))
    'reedit-text)
   ((not (read-list-isempty lyskom-reading-list))
    'next-text)
   ((not (read-list-isempty lyskom-to-do-list))
    'next-conf)
   ;; This is not really true. The prefetch may still be fetching the
   ;; membership. One possible way is to test for a non-numeric,
   ;; non-nil value. Or even better, introduce a test function to
   ;; isolate the test.
   (lyskom-membership-is-read
    'when-done)
   (t 'unknown)))


(defun lyskom-what-to-do-when-done (&optional nochange)
  "Returns a command, the next command to do from the kom-do-when-done.
If optional argument NOCHANGE is non-nil then the list wont be altered."
  (condition-case nil
      (let* ((now (cdr lyskom-do-when-done))
	     (all (car lyskom-do-when-done))
	     (next (cond
		    ((and now (eq now all) (cdr all))
		     (cdr all))
		    (t all)))
	     (command
	      (cond
	       ((commandp (car now)) (car now))
	       ((and (car next)
                     (listp (car next))
		     (not (eq (car (car next))
			      'lambda)))
		(car (setq now (car next))))
	       (t (or (car (setq now next))
                      'kom-display-time)))))
	(if nochange
	    nil
	  (setq lyskom-do-when-done (cons next (cdr now))))
	command)
    (error
     (lyskom-insert-before-prompt (lyskom-get-string
				   'error-in-kom-do-when-done))
     (lyskom-beep t)
     (setq lyskom-do-when-done '((kom-customize kom-display-time)
				 . (kom-edit-options kom-display-time)))
     'kom-display-time)))


(defun lyskom-prefetch-and-print-prompt ()
  "Prefetch info if needed. Print prompt if not already printed."
  (when (and lyskom-is-waiting
             (listp lyskom-is-waiting)
             (eval lyskom-is-waiting))
      (setq lyskom-is-waiting nil))
  (lyskom-update-prompt))


(defun lyskom-known-texts ()
  "Count how many unread texts the user have, that the client knows about."
  (apply '+ (mapcar (function
		     (lambda (x) (text-list->length (read-info->text-list x))))
		    (read-list->all-entries lyskom-to-do-list))))


(defun lyskom-wait-for-membership ()
  "Give a message and wait for it to be prefetched.
If the full membership hase been read do nothing."
  (let ((total (pers-stat->no-of-confs (blocking-do 'get-pers-stat
						    lyskom-pers-no))))
    (while (and (not (lyskom-membership-is-read))
                (numberp lyskom-membership-is-read))
      (lyskom-message (lyskom-get-string 'waiting-for-membership)
		      lyskom-membership-is-read
		      total)
      (sit-for 0)
      (accept-process-output lyskom-proc 1))))


(defun lyskom-prefetch-all-confs ()
  "Gets all conferences using prefetch."
  (lyskom-wait-for-membership))


;; FIXME: Here we can handle both maps and mappings, but in 
;; FIXME: reality this should *never* get called with a map 
;; FIXME: anymore

(defun lyskom-list-unread (map membership)
  "Args: MAP MEMBERSHIP. Return a list of unread texts.
The list consists of text-nos."
  (if (lyskom-text-mapping-p map)
      (lyskom-list-unread-mapping map membership)
    (let ((read (membership->read-texts membership))
          (first (map->first-local map))
          (i (length (map->text-nos map)))
          (the-map (map->text-nos map)))
      (when (not (null read))
        (while (> i 0)
          (-- i)
          ;; The server always send the read texts in sorted order. This
          ;; means that we can use binary search to look for read texts.

          ;; It might be a good idea to check for zero, and not do a
          ;; sarch in that case, but it depends on how big holes there
          ;; are in the map. In general the extra test is probably a
          ;; slowdow, but when reading the initial part of the I]M map
          ;; it would most likely help a lot.
          (when (lyskom-binsearch  (+ i first) read)
            (aset the-map i 0))))
      (delq 0 (listify-vector the-map)))))

(defun lyskom-list-unread-mapping (map membership)
  "Args: MAP MEMBERSHIP. Return a list of unread texts.
The list consists of text-nos."
  (let ((read (membership->read-texts membership))
        (iter  (text-mapping->iterator map))
        (el nil))
    (when (not (null read))
      (while (setq el (text-mapping-iterator->next iter))
        (when (lyskom-binsearch (text-pair->local-number el) read)
          (text-mapping->remove-local map (text-pair->local-number el)))))
    (text-mapping->global-numbers map)))



;;;; ================================================================


(defun lyskom-vmemq (elt vector)
  "Return t if ELT is a member of (present in) VECTOR."
  (let ((found nil)
	(i (length vector)))
    (while (and (> i 0)
		(not found))
      (-- i)
      (if (eq elt (aref vector i))
	  (setq found t)))
    found))


(defun lyskom-binsearch (el sequence &optional first last+1 fn)
  "Return the index if EL is a member of (present in) SEQUENCE.
SEQUENCE has to be sorted with regard to the comparison function.

Optional arguments FIRST and LAST+1 should only be used if you know
what they are for.

Optional argument FN is the function to use for comparison. It should
take arguments A and B and return non-nil if A is less than B. If not
supplied, FN defaults to <."
  (lyskom-binsearch-internal el sequence
			     (or first 0)
			     (or last+1 (length sequence))
                             (or fn '<)))

(defun lyskom-binsearch-internal (num vector first last+1 less-than)
  "Return the index if ELT is a member of the sorted vector VECTOR."
  (let* ((split (/ (+ first last+1) 2))
	 (splitval (elt vector split)))
    (cond
     ;; Only one element
     ((= (- last+1 first) 1) 
      (if (not (or (funcall less-than num splitval) 
                   (funcall less-than splitval num)))
          split nil))
     ;; Search the left subtree
     ((funcall less-than num splitval)
      (lyskom-binsearch-internal num vector first split less-than))

     ;; Search the left subtree
     ((funcall less-than splitval num)
      (lyskom-binsearch-internal num vector split last+1 less-than))

     ;; Found
     (t split))))

(defvar lyskom-verified-read-predicate nil)
(defun lyskom-verified-read-enter ()
  (interactive)
  (let* ((val (lyskom-minibuffer-contents))
         (err (funcall lyskom-verified-read-predicate val)))
    (if err
        (lyskom-minibuffer-message (format " [%s]" err))
      (exit-minibuffer))))

(defvar lyskom-verified-read-map nil)
(if lyskom-verified-read-map
    nil
  (setq lyskom-verified-read-map (copy-keymap minibuffer-local-map))
  (define-key lyskom-verified-read-map (kbd "RET") 'lyskom-verified-read-enter)
  (define-key lyskom-verified-read-map (kbd "C-j") 'lyskom-verified-read-enter)
  (define-key lyskom-verified-read-map (kbd "C-m") 'lyskom-verified-read-enter)
  )

(defun lyskom-verified-read-from-minibuffer (prompt initial pred)
  "Read something from minibuffer, verifying that it is valid.
PROMPT is the prompt and INITIAL the initial contents of the minibuffer.
PRED is a predicate to check entered data. It should return nil or a string. If
it returns a string, the data is not valid and the string is used as an error 
message."
  (let ((lyskom-verified-read-predicate pred))
    (lyskom-read-from-minibuffer prompt initial lyskom-verified-read-map)))


(defun lyskom-read-num-range-or-date (low high prompt &optional initial empty default)
  "Read a number or a date from the minibuffer.
Args: LOW HIGH PROMPT.
The result will be a number or a list of (YEAR MONTH DATE)."
  (let ((result nil)
        (break nil)
        (val (and initial (number-to-string initial)))
        (prompt (concat (if (symbolp prompt) (lyskom-get-string prompt) prompt)
                        (format "(%d-%d %s) " low high (lyskom-get-string 'or-date)))))
    (while (and (null result) (null break))
      (setq val (lyskom-verified-read-from-minibuffer 
                 prompt 
                 (and val (cons val 0))
                 (lambda (val)
                   (cond 
                    ((string-match "^\\s-*[0-9]+\\s-*$" val)
                     (let ((num (lyskom-string-to-number val)))
                       (unless (and (>= num low) (<= num high))
                         (lyskom-get-string 'number-out-of-range))))
                    ((and empty (string-match "^\\s-*$" val)) nil)
                    (t
                     (condition-case nil
                         (progn (lyskom-parse-date val) nil)
                       (lyskom-error (lyskom-get-string 'invalid-date-entry))))))))
      (cond ((string-match "^\\s-*[0-9]+\\s-*$" val)
             (let ((num (lyskom-string-to-number val)))
               (when (and (>= num low) (<= num high))
                 (setq result num))))
            ((and empty (string-match "^\\s-*$" val))
             (setq break t
                   result default))
            (t (condition-case nil
                   (setq result (lyskom-parse-date val) )
                 (lyskom-error nil)))))
    result))



(defun lyskom-read-num-range (low high &optional prompt show-range default history nildefault)
  "Read a number from the minibuffer.
Args: LOW HIGH &optional PROMPT SHOW-RANGE with default value DEFAULT.
The read number must be within the range [LOW HIGH].
If SHOW-RANGE is non-nil, the prompt will include the range for information
to the user.
HISTORY is the history list to use.
If NILDEFAULT is non-nil, return nil if the user enters an empty string"
  (let ((number (1- low)))
    (while (and (not (and nildefault
                          (null number)))
                (or (< number low)
                    (> number high)))
      (setq number 
            (lyskom-read-number 
             (concat (cond ((null prompt) (lyskom-get-string 'give-a-number))
                           ((symbolp prompt) (lyskom-get-string prompt))
                           (t prompt))
                     (if show-range
                         (format "(%d-%d) " low high)
                       ""))
             default
             history
             nildefault)))
    number))


(defun lyskom-read-number (&optional prompt default history nildefault completions)
  "Read a number from the minibuffer. Optional arguments: PROMPT DEFAULT
If DEFAULT is non-nil, it is written within parenthesis after the prompt.
DEFAULT could also be of the type which (interactive P) generates.
If NILDEFAULT is non-null then typing return will cause the function 
to return nil."
  (let* ((numdefault (cond ((null default) nil)
                           ((integerp default) default)
                           ((listp default) (car default))
                           (t nil)))
         (prompt (concat (cond ((null prompt) (lyskom-get-string 'give-a-number))
                               ((symbolp prompt) (lyskom-get-string prompt))
                               (t prompt))
			 (if numdefault 
			     (format " (%d) " numdefault)
			   " ")))
         (number nil)
         (numstr nil)
         (done nil))
    (while (not done)
      (setq numstr
	    (prog1
                (if completions
                    (lyskom-completing-read prompt
                                            (mapcar (lambda (x) (list (number-to-string x) x))
                                                    completions)
                                            nil
                                            t
                                            nil
                                            history
                                            (when default (number-to-string default)))
                  (lyskom-read-string
                   prompt
                   nil
                   history))))
      (cond ((and (string= numstr "") numdefault)
	     (setq number numdefault done t))
            ((and (string= numstr "") nildefault)
             (setq number nil done t))
	    ((and (string-match "\\`\\s-*[0-9]+\\s-*\\'" numstr)
                  (lyskom-string-to-int numstr))
	     (setq number (lyskom-string-to-int numstr))
             (setq done number))
	    (t (beep))))
    number))


(defun lyskom-read-string (prompt &optional initial history)
  "Read a string from the minibuffer. Arguments: PROMPT INITIAL"
  (read-string prompt initial history))



(defun ja-or-nej-p (prompt &optional initial-input)
  "Same as yes-or-no-p but language-dependent.
Uses lyskom-message, lyskom-read-string to do interaction and
lyskom-get-string to retrieve regexps for answer and string for repeated query."
  (let ((answer "")
	(nagging nil))
    (while (not (or (string-match (lyskom-get-string 'yes-regexp) answer)
		    (string-match (lyskom-get-string 'no-regexp) answer)))
      (if nagging
	  (progn
	    (lyskom-message "%s" (lyskom-get-string 'yes-or-no-nag))
	    (sit-for 2)))
      (setq answer (lyskom-read-string (concat prompt (lyskom-get-string 'yes-or-no))
				       initial-input t))
      (setq nagging t))
    (not (string-match (lyskom-get-string 'no-regexp) answer))))


;;;
;;; j-or-n-p is similar to y-or-n-p.
;;;


(defun j-or-n-p (prompt)
  "Same as y-or-n-p but language-dependent.
Uses lyskom-message, lyskom-read-string to do interaction and
lyskom-get-string to retrieve regexps for answer and string for repeated query."
  (when (symbolp prompt) (setq prompt (lyskom-get-string prompt)))
  (let ((input-char 0)
	(cursor-in-echo-area t)
        (lyskom-inhibit-minibuffer-messages t)
	(nagging nil))

    (while (and (not (char-in-string input-char
                                     (lyskom-get-string 'y-or-n-instring)))
                (not (or (eq input-char ?\C-g)
                              (eq 'keyboard-quit
                                  (lyskom-lookup-key (current-local-map)
                                                     input-char
                                                     t)))))
	(lyskom-message "%s" (concat (if nagging 
					 (lyskom-get-string 'j-or-n-nag)
				       "") 
				     prompt
				     (lyskom-get-string 'j-or-n)))
	(if nagging (beep))

        ;;
        ;; Workaround for Emacs whose read-char does not accept C-g
        ;;

	(setq input-char 
              (let ((inhibit-quit t))
                (prog1 (read-char-exclusive)
                  (setq quit-flag nil))))

        ;;
        ;; Redisplay prompt on C-l
        ;;

        (if (or (eq input-char ?\C-l)
                (eq 'recenter (lyskom-lookup-key (current-local-map)
                                                 input-char
                                                 t)))
            (setq nagging nil)
          (setq nagging t)))

    (when (or (eq input-char ?\C-g)
	      (eq 'keyboard-quit (lyskom-lookup-key (current-local-map)
						    input-char
						    t)))
      (signal 'quit nil))

    (lyskom-message "%s" (concat prompt
                                 (lyskom-get-string 'j-or-n)
                                 (if (char-in-string input-char (lyskom-get-string 'y-instring))
                                     (lyskom-get-string 'yes-string)
                                   (lyskom-get-string 'no-string))))
    (char-in-string input-char (lyskom-get-string 'y-instring))))

  
;;; lyskom-j-or-n-p, lyskom-ja-or-no-p
;;; These versions no longer perform lyskom-end-of-command
;; Author: Linus Tolke

(defun lyskom-j-or-n-p (prompt)
  "Same as j-or-n-p but performs lyskom-end-of-command if quit."
  (condition-case nil
      (j-or-n-p prompt)
    (quit (signal 'quit nil))))


(defun lyskom-ja-or-nej-p (prompt &optional initial-input)
  "Same as ja-or-nej-p but performs lyskom-end-of-command if quit."
  (condition-case nil
      (ja-or-nej-p prompt initial-input)
    (quit (signal 'quit nil))))

(defun lyskom-a-or-b-or-c-p (prompt alternatives &optional default)
  "Like y-or-n-p but choose among several options.
Display PROMPT and ask user to choose among ALTERNATIVES.

ALTERNATIVES are symbols. Each alternative must be found as a string
in lyskom-messages. The first character of the string is the character
to enter to select the alternative and is not displayed.

If non-nil DEFAULT is the default alternative and is selected when the
user types RET.

Returns the selected alternative (a symbol)"
  (when (symbolp prompt) (setq prompt (lyskom-get-string prompt)))
  (let* ((input-char 0)
         (cursor-in-echo-area t)
         (lyskom-inhibit-minibuffer-messages t)
         (alts (mapcar (lambda (alt)
                         (let ((string (lyskom-get-string alt)))
                           (list (elt string 0)
                                 (substring string 1)
                                 alt)))
                         alternatives))
         (alts-string (format "(%s) " (mapconcat (lambda (c)
                                                   (lyskom-format "%#3@%#1c:%#2s"
                                                           (elt c 0)
                                                           (elt c 1)
                                                           (and (eq (elt c 2) default) '(face bold))
                                                           ))
                                                 alts
                                                 ", ")))
         (nagging nil))

    (while (and (not (assq input-char alts))
                (not (or (eq input-char ?\C-g)
                         (eq 'keyboard-quit
                             (lyskom-lookup-key (current-local-map)
                                                input-char
                                                t))))
                (not (and default (eq input-char ?\C-m))))
      (lyskom-message "%s" (concat (if nagging 
                                       (lyskom-get-string 'a-or-b-or-c-nag)
                                     "")
                                   prompt
                                   alts-string))
      (if nagging (beep))

      ;;
      ;; Workaround for Emacs whose read-char does not accept C-g
      ;;

      (setq input-char 
            (let ((inhibit-quit t))
              (prog1 (read-char-exclusive)
                (setq quit-flag nil))))

      ;;
      ;; Redisplay prompt on C-l
      ;;

      (if (or (eq input-char ?\C-l)
              (eq 'recenter (lyskom-lookup-key (current-local-map)
                                               input-char
                                               t)))
          (setq nagging nil)
        (setq nagging t)))

    (when (or (eq input-char ?\C-g)
	      (eq 'keyboard-quit (lyskom-lookup-key (current-local-map)
						    input-char
						    t)))
      (signal 'quit nil))

    (setq alts (cons (list ?\C-m
                           (lyskom-traverse alt alts (when (eq default (elt alt 2)) (lyskom-traverse-break (elt alt 1))))
                           default) alts))
    (lyskom-message "%s" (concat prompt (elt (assq input-char alts) 1)))
    (if (eq input-char ?\C-m)
        default
      (elt (assq input-char alts) 2))))


(defun lyskom-not-implemented ()
  (error "Not implemented"))


;;; Priority filtering

(defun lyskom-visible-membership (membership)
  "Is this conference visible?
Return t is MEMBERSHIPs priority is higher than or equal to
lyskom-session-priority and nil otherwise.

If MEMBERSHIPs prioriy is 0, it always returns nil."
  (let ((priority (membership->priority membership)))
    (and (not (membership-type->passive (membership->type membership)))
	 (>= priority lyskom-session-priority))))

;;; The filter.

(defun lyskom-filter (proc output)
  "Receive replies from LysKOM server."
;  (sit-for 0)				; Why? [Doesn't work in XEmacs 19.14]
;  (setq lyskom-apo-timeout-log
;        (cons (cons (current-time-string) lyskom-apo-timeout) lyskom-apo-timeout-log))
  (lyskom-reset-apo-timeout)            ; Reset accept-process-output timeout
  (let ((old-match-data (match-data))
	;; lyskom-filter-old-buffer is also changed when starting to edit
	;; in function lyskom-edit-text.
	(lyskom-filter-old-buffer (current-buffer)))
    (unwind-protect
	(condition-case nil
	    (progn
	      (setq lyskom-quit-flag nil)

              (if lyskom-debug-communications-to-buffer
                  (save-current-buffer
                    (set-buffer (get-buffer-create "*kom*-replies"))
                    (goto-char (point-max))
                    (princ (lyskom-string-as-unibyte output) (current-buffer))
                    (lyskom-debug-limit-buffer)))

	      (if lyskom-debug-communications-to-buffer
                  (if (not lyskom-debug-what-i-am-doing)
                      (if (not (and (eq ?: (elt output 0))
                                    (eq ?5 (elt output 1))))
                          (lyskom-debug-insert proc "From " output))
                    (lyskom-debug-insert proc "From " output)))

	      (set-buffer (process-buffer proc))
	      (princ (lyskom-string-as-unibyte output) lyskom-unparsed-marker)
	      ;;+++lyskom-string-skip-whitespace
	      (if quit-flag		; We are allowed to break here.
		  (setq inhibit-quit nil)) ; This will break
					   ; instantly.

	      ;; Keep inhibit-quit set to t
	      (cond

	       ((and (> lyskom-string-bytes-missing 0)
		     (< (length output) lyskom-string-bytes-missing))
		(setq lyskom-string-bytes-missing
		      (- lyskom-string-bytes-missing
			 (length output))))

	       ;; This test makes e.g. startup a lot faster. At least
	       ;; it does when the maps are read in one chunk, which
	       ;; they usually aren't anymore.
	       ((not (string-match "\n" output)))

	       ((null lyskom-is-parsing) ;Parse one reply at a time.
		(let ((lyskom-is-parsing t))
		  (unwind-protect
		      (condition-case nil
			  (lyskom-parse-unparsed)
			;; Incomplete answers are normal.
			(lyskom-parse-incomplete))
		    ;; In case it was changed by the handler.
		    (set-buffer (process-buffer proc)))))))
	  ;; condition-case handler
	  (quit (setq lyskom-quit-flag t))
	  ;; (lyskom-protocol-error
	  ;;   (lyskom-message "%s" (lyskom-get-string 'protocol-error) err))
	  )

;;      (unwind-protect
;;          (save-excursion (set-buffer (process-buffer proc))
;;                          (lyskom-check-mship-lists)))

      (setq lyskom-quit-flag (or lyskom-quit-flag quit-flag))
      (setq quit-flag nil)
      (when lyskom-need-prompt-update
        (lyskom-update-all-prompts))
      ;; Restore selected buffer and match data.
      (store-match-data old-match-data)
      (when (buffer-live-p lyskom-filter-old-buffer)
        (set-buffer lyskom-filter-old-buffer)))
    (sit-for 0)))
      

;;; The sentinel

(defun lyskom-sentinel (proc sentinel)
  "Handles changes in the lyskom-process."
  (when (processp proc)
    (lyskom-remove-unread-buffer (process-buffer proc))
    (set-buffer (process-buffer proc)))
  (lyskom-start-of-command (lyskom-get-string 'process-signal) t t)
  (lyskom-format-insert 'closed-connection
                        sentinel 
                        (lyskom-format-time
                         'timeformat-day-yyyy-mm-dd-hh-mm-ss))
  (setq mode-line-process (lyskom-get-string 'mode-line-down))
  ; Notify?
  (when kom-lost-session-notification    
    (if (eq 'all-buffers kom-lost-session-notification)
	(let ((session-nick (lyskom-session-nickname)))	
	  (save-excursion
	    (lyskom-traverse buf lyskom-buffer-list
	      (when (lyskom-buffer-p buf)
		(set-buffer buf)
		(lyskom-insert-before-prompt
		 (lyskom-format 'closed-connection-other-buf
				      session-nick)))))))
    (beep)
    )
  (lyskom-scroll))


;;; ================================================================
;;;         Debug buffer

(defun lyskom-debug-limit-buffer ()
  (when (and (numberp lyskom-debug-communications-limit)
             (> (point-max) lyskom-debug-communications-limit))
    (delete-region (point-min) (- (point-max) lyskom-debug-communications-limit))))

(defun lyskom-debug-insert (proc prefix string)
  (let* ((buf (get-buffer-create lyskom-debug-communications-to-buffer-buffer))
	 (win (get-buffer-window buf 'visible)))
    (if win
        (save-selected-window
          (select-window win)
          (set-buffer buf)
          (let ((move (eobp)))
            (save-excursion
              (goto-char (point-max))
              (insert "\n"
                      prefix
                      (format "%s" proc)
                      ": "
                      string))
            (lyskom-debug-limit-buffer)
            (if move (goto-char (point-max)))))
      (save-current-buffer
	(set-buffer buf)
	(goto-char (point-max))
	(insert "\n"
                prefix
		(format "%s" proc)
		": "  string)
        (lyskom-debug-limit-buffer)))))


;;; For serious bugs
(defun lyskom-really-serious-bug ()
  (let ((debug-on-error t))
    (error "Congratulations! You found a serious bug in lyskom.el.
Press q to leave this buffer, and please run M-x kom-bug-report afterwards.")))
  


;;; ================================================================
;;;         Formatting functions for different data types


(defun lyskom-format-objects (&rest args)
  "Arguments: (&rest ARGS). Format ARGS to correct format to send to server.
Strings are converted to Hollerith strings.
Other objects are converted correctly."
  (concat " " (mapconcat 'lyskom-format-object args " ")))


(defun lyskom-format-object (object)
  (cond
   ((stringp object) (lyskom-prot-a-format-string object))
   ((integerp object) (int-to-string object))
   ((null object) "0")
   ((listp object)
    (cond
     ((eq (car object) 'MISC-LIST)
      (lyskom-prot-a-format-misc-list (cdr object)))
     ((eq (car object) 'AUX-ITEM)
      (lyskom-prot-a-format-aux-item object))
     ((eq (car object) 'AUX-ITEM-FLAGS)
      (lyskom-prot-a-format-aux-item-flags object))
     ((eq (car object) 'CONF-TYPE)
      (lyskom-prot-a-format-conf-type object))
     ((eq (car object) 'PRIVS)
      (lyskom-prot-a-format-privs object))
     ((eq (car object) 'FLAGS)
      (lyskom-prot-a-format-flags object))
     ((eq (car object) 'MEMBERSHIP-TYPE)
      (lyskom-prot-a-format-membership-type object))
     ((eq (car object) 'LIST)
      (lyskom-prot-a-format-simple-list (cdr object)))
     ((eq (car object) 'TIME)
      (lyskom-prot-a-format-time (cdr object)))
     ((eq (car object) 'STRING)
      (lyskom-prot-a-format-raw-string (cdr object)))
     (t
      (signal 'lyskom-internal-error
              (list 'lyskom-format-object
                    ": no support for object "
                    object)))))
   ((eq object t) "1")
   (t (signal 'lyskom-internal-error
              (list 'lyskom-format-object
                    ": no support for object "
                    object)))))


(defun lyskom-prot-a-format-time (time)
  "Format a TIME for output to the server."
  (mapconcat 'int-to-string time " "))

(defun lyskom-prot-a-format-flags (flags)
  "Format personal flags for the server."
  (concat
   (lyskom-prot-a-format-bool (flags->unread_is_secret flags))
   (lyskom-prot-a-format-bool (flags->flg2 flags))
   (lyskom-prot-a-format-bool (flags->flg3 flags))
   (lyskom-prot-a-format-bool (flags->flg4 flags))
   (lyskom-prot-a-format-bool (flags->flg5 flags))
   (lyskom-prot-a-format-bool (flags->flg6 flags))
   (lyskom-prot-a-format-bool (flags->flg7 flags))
   (lyskom-prot-a-format-bool (flags->flg8 flags))))


(defun lyskom-prot-a-format-membership-type (membership-type)
  "Format a MEMBERSHIP-TYPE for output to the server."
  (concat
   (lyskom-prot-a-format-bool (membership-type->invitation membership-type))
   (lyskom-prot-a-format-bool (membership-type->passive membership-type))
   (lyskom-prot-a-format-bool (membership-type->secret membership-type))
   (lyskom-prot-a-format-bool (eq (membership-type->message-flag membership-type)
                                  (membership-type->passive membership-type)))
   (lyskom-prot-a-format-bool (membership-type->rsv2 membership-type))
   (lyskom-prot-a-format-bool (membership-type->rsv3 membership-type))
   (lyskom-prot-a-format-bool (membership-type->rsv4 membership-type))
   (lyskom-prot-a-format-bool (membership-type->rsv5 membership-type))))

(defun lyskom-prot-a-format-conf-type (conf-type)
  "Format a CONF-TYPE for output to the server."
  (concat
   (lyskom-prot-a-format-bool (conf-type->rd_prot conf-type))
   (lyskom-prot-a-format-bool (conf-type->original conf-type))
   (lyskom-prot-a-format-bool (conf-type->secret conf-type))
   (lyskom-prot-a-format-bool (conf-type->letterbox conf-type))
   (if (lyskom-have-feature long-conf-types)
       (concat
         (lyskom-prot-a-format-bool (conf-type->anarchy conf-type))
         (lyskom-prot-a-format-bool (conf-type->forbid-secret conf-type))
         (lyskom-prot-a-format-bool (conf-type->rsv2 conf-type))
         (lyskom-prot-a-format-bool (conf-type->rsv3 conf-type)))
     "")))

(defun lyskom-prot-a-format-aux-item (item)
  "Format an AUX-ITEM for output to the server."
  (concat
   (int-to-string (aux-item->tag item))                     " "
   (lyskom-prot-a-format-aux-item-flags (aux-item->flags item))    " "
   (int-to-string (aux-item->inherit-limit item))           " "
   (lyskom-prot-a-format-raw-string (cons 'raw-text 
                                          (lyskom-aux-item-output-data 
                                           item)))))

(defun lyskom-prot-a-format-aux-item-flags (flags)
  "Format AUX-ITEM-FLAGS for output to the server."
  (concat
   (lyskom-prot-a-format-bool (aux-item-flags->deleted flags))
   (lyskom-prot-a-format-bool (aux-item-flags->inherit flags))
   (lyskom-prot-a-format-bool (aux-item-flags->secret flags))
   (lyskom-prot-a-format-bool (aux-item-flags->anonymous flags))
   (lyskom-prot-a-format-bool (aux-item-flags->dont-garb flags))
   (lyskom-prot-a-format-bool (aux-item-flags->reserved2 flags))
   (lyskom-prot-a-format-bool (aux-item-flags->reserved3 flags))
   (lyskom-prot-a-format-bool (aux-item-flags->reserved4 flags))))


(defun lyskom-prot-a-format-privs (privs)
  "Format PRIVS for output to the server."
  (concat
   (lyskom-prot-a-format-bool (privs->wheel privs))
   (lyskom-prot-a-format-bool (privs->admin privs))
   (lyskom-prot-a-format-bool (privs->statistic privs))
   (lyskom-prot-a-format-bool (privs->create_pers privs))
   (lyskom-prot-a-format-bool (privs->create_conf privs))
   (lyskom-prot-a-format-bool (privs->change_name privs))
   (lyskom-prot-a-format-bool (privs->flg7 privs))
   (lyskom-prot-a-format-bool (privs->flg8 privs))
   (lyskom-prot-a-format-bool (privs->flg9 privs))
   (lyskom-prot-a-format-bool (privs->flg10 privs))
   (lyskom-prot-a-format-bool (privs->flg11 privs))
   (lyskom-prot-a-format-bool (privs->flg12 privs))
   (lyskom-prot-a-format-bool (privs->flg13 privs))
   (lyskom-prot-a-format-bool (privs->flg14 privs))
   (lyskom-prot-a-format-bool (privs->flg15 privs))
   (lyskom-prot-a-format-bool (privs->flg16 privs))))

(defun lyskom-prot-a-format-bool (bool)
  "Format a BOOL for output to the server."
  (if bool "1" "0"))
      

(defun lyskom-prot-a-format-misc-list (misc-list)
  "Format a misc-list for output to the server."
  (let ((result (format "%d {" (length misc-list))))
    (while (not (null misc-list))
      (setq result (concat result " "
			    (lyskom-prot-a-format-misc-item (car misc-list))))
      (setq misc-list (cdr misc-list)))
    (setq result (concat result " }"))))


(defun lyskom-prot-a-format-misc-item (misc-item)
  "Format a misc-item for output to the server."
  (format "%d %d"
	  (cond
	   ((eq (car misc-item) 'RECPT) 0)
	   ((eq (car misc-item) 'CC-RECPT) 1)
	   ((eq (car misc-item) 'COMM-TO) 2)
	   ((eq (car misc-item) 'FOOTN-TO) 4)
           ((eq (car misc-item) 'BCC-RECPT) 
            (if (lyskom-have-feature bcc-misc) 15 1)))
	  (cdr misc-item)))


(defun lyskom-prot-a-format-simple-list (list)
  "Format some kind of list to send to server."
  (apply 'concat (list (format "%d {" (length list))
		       (apply 'lyskom-format-objects list)
		       " }")))

  
(defun lyskom-prot-a-format-string (string)
  (let ((tmp (lyskom-encode-coding-string string lyskom-server-coding-system)))
    (lyskom-string-as-unibyte (format "%dH%s" (lyskom-string-bytes tmp) tmp))))

(defun lyskom-prot-a-format-raw-string (string)
  (lyskom-string-as-unibyte (format "%dH%s" (lyskom-string-bytes (cdr string)) 
			     (cdr string))))





;;;; ================================================================
;;;;                     Utility routines.


;;; lyskom-silent-read was
;; Written by Lars Willf|r <willfor@lysator.liu.se>
;; Copyright and copyleft Lars Willf|r.
;; Last modified jun 93 by Linus Tolke

(defun lyskom-silent-read (prompt-str)
  "Read a string in the minibuffer without echoing.
One parameter - the prompt string."
  (interactive "sPrompt string: ")
  (lyskom-message "%s" prompt-str)
  (let ((input-string "")
	(input-char)
	(cursor-in-echo-area t))
    (while (not (or (eq (setq input-char 
			      (condition-case err
				  (read-char-exclusive)
				(error (if (string= "Non-character input-event"
						    (car (cdr err)))
					   ?\r
					 (signal (car err) (cdr err))))))
			?\r)
		    (eq input-char ?\n)))
      (progn
	(lyskom-message "%s" prompt-str)
	(setq input-string
	      (cond
	       ((eq input-char ?\C-?)
		(if (equal (length input-string) 0)
		    ""
		  (substring input-string 0 -1)))
	       ((eq input-char ?\C-u)
		"")
	       (t
		(concat input-string (char-to-string input-char)))))))
    (message "")
    input-string))


(defun lyskom-session-nickname (&optional in-modeline)
  "Return the nickname to use for this LysKOM session."
  (if kom-session-nickname
      kom-session-nickname
    (let* ((proc (get-buffer-process (current-buffer)))
	   (server (if proc (process-name proc) lyskom-server-name)))
      (or (cdr (assoc server
		      (append kom-server-aliases
			      kom-builtin-server-aliases)))
	  (if in-modeline
	    (format "LysKOM(%s)" server)
	    server)))))
   
;;; This really is a strange thing to do but...
;;
(defun lyskom-mode-name-from-host ()
  "Calculate what to identify the buffer with."
  (setq mode-line-server-name 
	(lyskom-session-nickname t)))

(defvar lyskom-modeline-keymap nil)
(if lyskom-modeline-keymap
    nil
  (setq lyskom-modeline-keymap (make-sparse-keymap))
  (lyskom-xemacs-or-gnu
   (progn (define-key lyskom-modeline-keymap (kbd (lyskom-keys 'button2up)) 'kom-modeline-next-unread-kom)
          (define-key lyskom-modeline-keymap (kbd (lyskom-keys 'button3)) 'kom-modeline-select-unread-kom))
  (progn (define-key lyskom-modeline-keymap [mode-line mouse-2] 'kom-modeline-next-unread-kom)
         (define-key lyskom-modeline-keymap [mode-line mouse-3] 'kom-modeline-select-unread-kom))))


(defun lyskom-make-alternate-unread-mode-line ()
  (save-excursion
    (let ((mline ())
	  (current nil)
	  (upcased nil))
      
      (lyskom-traverse session lyskom-sessions-with-unread
	(set-buffer session)
	(setq current (lyskom-session-nickname))
	(when (memq session lyskom-sessions-with-unread-letters)
	  (setq upcased (upcase current))
	  (setq current
		(if (string= upcased current)
		    (concat "*" current "*")
		  upcased)))
	(setq mline (append mline (list (concat current " ")))))
      (setq mline (sort mline 'string<))
      mline)))

(defun lyskom-make-lyskom-unread-mode-line ()
  (if kom-unread-mode-line-type
      (lyskom-make-alternate-unread-mode-line)
    (lyskom-xemacs-or-gnu
     (list (list 'lyskom-sessions-with-unread 
		 (lyskom-get-string 'mode-line-unread))
	   (list 'lyskom-sessions-with-unread-letters
		 (lyskom-get-string 'mode-line-letters))
	   " ")
     `((lyskom-sessions-with-unread 
	,(lyskom-format "%#1@%#2s"
			(list 'local-map lyskom-modeline-keymap)
			(lyskom-get-string 'mode-line-unread)))
       (lyskom-sessions-with-unread-letters
	,(lyskom-format "%#1@%#2s"
			(list 'local-map lyskom-modeline-keymap)
			(lyskom-get-string 'mode-line-letters)))
       " "))))

(defun lyskom-make-lyskom-unread-title-format ()
  `(kom-show-unread-in-frame-title
    (lyskom-session-has-unreads 
     (" (" ((lyskom-session-has-unreads
             ,(lyskom-recode-string-for-title (lyskom-get-string 'frame-title-unread)
                                              'iso-8859-1))
            (lyskom-session-has-unread-letters ,(lyskom-recode-string-for-title
                                                 (lyskom-get-string 'frame-title-letters)
                                                 'iso-8859-1)))
      ")"))))

(defvar icon-title-format)
(defvar frame-icon-title-format)


(if lyskom-is-loaded
    nil

  ;; Fix altered format for kom-default-language
  (when (and kom-default-language (symbolp kom-default-language))
    (setq kom-default-language (list kom-default-language)))

  ;; Set up default language
  (setq lyskom-language (lyskom-default-language))
  (setq lyskom-global-language lyskom-language)
  (unless kom-default-language
    (setq kom-default-language (list lyskom-language)))


  (setq-default lyskom-collate-table lyskom-default-collate-table)
  ;; We should set lyskom-char-classes to
  ;;     (lyskom-compute-char-classes lyskom-collate-table))
  ;; but that currently fails under Emacs 20.7, because
  ;; lyskom-default-collate-table isn't set properly.
  (setq-default lyskom-char-classes nil)
  (lyskom-set-language lyskom-language 'local)
  (lyskom-set-language lyskom-language 'global)

  (unless (or (memq 'lyskom-unread-mode-line global-mode-string)
              (rassq 'lyskom-unread-mode-line global-mode-string))
    (lyskom-xemacs-or-gnu
     (let ((extent (make-extent nil nil nil)))
       (set-extent-keymap extent lyskom-modeline-keymap)
       (setq global-mode-string
             (append (list "" (cons extent 'lyskom-unread-mode-line))
                     global-mode-string)))
     (setq global-mode-string
           (append '("" lyskom-unread-mode-line) global-mode-string))))

  (if (boundp 'frame-title-format)
      (setq frame-title-format (list ""
                                     frame-title-format
                                     'lyskom-unread-title-format)))

  (if (boundp 'icon-title-format)
      (setq icon-title-format (list ""
                                    icon-title-format
                                    'lyskom-unread-title-format)))  
  (if (boundp 'frame-icon-title-format)
      (setq frame-icon-title-format (list ""
                                          frame-icon-title-format
                                          'lyskom-unread-title-format)))
  
  (add-hook 'kill-buffer-hook 'lyskom-remove-buffer-from-lists)


;;;
;;; Set up lyskom-line-start-chars. The reason we do it here is that
;;; char-to-int may not be defined until compatibility.el has been
;;; loaded.
;;;

  (setq lyskom-line-start-chars
        (let ((tmp (make-vector 256 nil)))
          (mapc
           (function
            (lambda (x)
              (aset tmp (lyskom-char-to-int x) t)))
           (lyskom-maybe-recode-string lyskom-line-start-chars-string 'iso-8859-1 t))
          tmp))


  ;; Setup the queue priorities
  (lyskom-set-queue-priority 'blocking 9)
  (lyskom-set-queue-priority 'main 9)
  (lyskom-set-queue-priority 'sending 9)
  (lyskom-set-queue-priority 'follow 9)
  (lyskom-set-queue-priority 'options 9)
  (lyskom-set-queue-priority 'deferred 6)
  (lyskom-set-queue-priority 'background 6)
  (lyskom-set-queue-priority 'modeline 6)
  (lyskom-set-queue-priority 'async 3)
  (lyskom-set-queue-priority 'prefetch 0)

  ;; Seed the randomizer

  (random t)
  (run-hooks 'lyskom-after-load-hook)
  (run-hooks 'kom-after-load-hook)
  (setq lyskom-is-loaded t))



(provide 'lyskom-rest)

;;; This should be the very last lines of lyskom.el Everything should
;;; be loaded now, so it's time to run the kom-after-load-hook.


(lyskom-end-of-compilation)

;;; Local Variables: 
;;; eval: (put 'lyskom-traverse 'lisp-indent-hook 2)
;;; end: 
