utilities.el 19.9 KB
Newer Older
1
2
;;;;; -*-coding: raw-text;-*-
;;;;;
3
4
5
6
7
8
9
;;;;; $Id$
;;;;; Copyright (C) 1996  Lysator Academic Computer Association.
;;;;;
;;;;; This file is part of the LysKOM server.
;;;;; 
;;;;; LysKOM is free software; you can redistribute it and/or modify it
;;;;; under the terms of the GNU General Public License as published by 
10
;;;;; the Free Software Foundation; either version 2, or (at your option) 
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
;;;;; 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. 
;;;;;
;;;; ================================================================
;;;; ================================================================
;;;;
29
;;;; File: utilities.el
30
31
32
33
34
35
36
37
38
39
40
;;;;
;;;; This file contains general lisp utility functions and
;;;; lyskom-specific utility functions (such as date formatting and
;;;; minibuffer reading)
;;;;


(setq lyskom-clientversion-long
      (concat lyskom-clientversion-long
	      "$Id$\n"))

David Byers's avatar
David Byers committed
41
42
43
44
45
46
;;;
;;; Need Per Abrahamsens widget and custom packages There should be a
;;; better way of doing this, but I'll be darned if I know how. The
;;; various files need to be loaded in a very specific order.
;;;

47
;;; Define widget wrappers for all the functions in macros.el
48
49
50
51
52
53
54
55
56
57
58
59
60
61

;;;
;;; Lisp utility functions
;;;

(defsubst listify-vector (vector)
  "Turn VECTOR into a list"
  (append vector nil))

(defun reverse-assoc (key cache)
  "Same as assoc, but searches on last element in a list"
  (reverse (assoc key (mapcar (function reverse) cache))))


62
63
64
65
66
67
(defun nfirst (n list)
  "Return a list of the N first elements of LIST."
  (if (or (<= n 0) (not list))
      nil
    (cons (car list) (nfirst (1- n) (cdr list)))))

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
;;;
;;; +++ 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))))
85

86
87
88
89
90
91
(defun skip-first-zeros (list)
  (while (and list (zerop (car list)))
    (setq list (cdr list)))
  list)


92
93
94
95
96
97
(defun filter-list (test list)
  (cond ((null list) '())
	((apply test (car list) nil)
	 (cons (car list) (filter-list test (cdr list))))
	(t (filter-list test (cdr list)))))

98
99
100
101
102
103
104
;;;============================================================
;;;
;;; Utility functions.
;;;
;;; These should be shared in LysKOM
;;;

105
(lyskom-provide-function copy-tree (l)
106
107
108
109
110
  "Recursively copy the list L"
  (cond ((atom l) l)
        (t (cons (copy-tree (car l))
                 (copy-tree (cdr l))))))

David Byers's avatar
David Byers committed
111
112
113
114
115
(lyskom-provide-function functionp (obj)
  "Returns t if OBJ is a function, nil otherwise."
  (cond
   ((symbolp obj) (fboundp obj))
   ((subrp obj))
116
   ((byte-code-function-p obj))
David Byers's avatar
David Byers committed
117
118
119
   ((consp obj)
    (if (eq (car obj) 'lambda) (listp (car (cdr obj)))))
   (t nil)))
120
121


122
123
124
125
(defun lyskom-ignore (&rest args)
  "Ignore all arguments"
  )

126
127
128
129
130
131
132
133
134
135
(defun 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))


136
137
138
139
140
141
142
143
144
(defun 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)))


145
(defun lyskom-maxint ()
David Byers's avatar
David Byers committed
146
147
148
149
  (let ((n 1) 
        (l nil)
        (i 31))
    (while (and (> n 0) (> i 0))
150
      (setq l (cons n l))
David Byers's avatar
David Byers committed
151
152
      (setq n (* 2 n))
      (setq i (1- i)))
153
154
    (apply '+ l)))

David Byers's avatar
David Byers committed
155
156
157
158
;; Set lyskom-maxint correctly

(setq lyskom-max-int (lyskom-maxint))

David Byers's avatar
David Byers committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

(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))))
                               
      

178
179
180
181
182
(defun lyskom-emacs-version ()
  (cond ((string-match "^XEmacs" (emacs-version)) 'xemacs)
	(t 'emacs)))


David Byers's avatar
David Byers committed
183
184
(defvar lyskom-apo-timeout 0
  "Current millisecond timeout value for accept-process-output")
185

David Byers's avatar
David Byers committed
186
187
(defvar lyskom-apo-timeout-index 0
  "Index in lyskom-apo-timeout-vector-max where last timeout is")
188

David Byers's avatar
David Byers committed
189
190
191
(defconst 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")
192

David Byers's avatar
David Byers committed
193
194
(defconst lyskom-apo-timeout-vector-max (1- (length lyskom-apo-timeout-vector))
  "Maximum index in lyskom-apo-timeout-vector")
195

196
(defun lyskom-next-apo-timeout ()
David Byers's avatar
David Byers committed
197
198
199
200
201
  (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))))))
202

203
(defun lyskom-reset-apo-timeout ()
David Byers's avatar
David Byers committed
204
205
  (setq lyskom-apo-timeout-index -1)
  (setq lyskom-apo-timeout 0))
206

207
(defun lyskom-accept-process-output ()
David Byers's avatar
David Byers committed
208
209
210
  "Call accept-process-output with the correct timeout values."
  (lyskom-next-apo-timeout)
  (accept-process-output nil 0 lyskom-apo-timeout))
211

212
213
214
215
216
217
218
219
220
221
222
223
224
(defun lyskom-current-time (&optional secs)
  "Return the time in a format that LysKOM understands.
If optional argument SECS is set, it is used in place of the value
of \(current-time\)."
  (let ((time (decode-time (or secs (current-time)))))
    (setcar (cdr (cdr (cdr (cdr time))))
            (1- (car (cdr (cdr (cdr (cdr time)))))))
    (setcar (cdr (cdr (cdr (cdr (cdr time)))))
            (- (car (cdr (cdr (cdr (cdr (cdr time))))))
               1900))
    time))


225

226
227
228
229
;;;
;;; LysKOM utility functions
;;;

David Byers's avatar
David Byers committed
230
231
232
233
234
235
236
237
238
239
;;;
;;; 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.
;;;

240
(defvar lyskom-default-collate-table
David Byers's avatar
David Byers committed
241
  "\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[\\]~!AAAA[]ACEEEEIIIINOOOO\\OUUUYYAAAA[]ACEEEEIIIINOOOO\\OUUUYY"
David Byers's avatar
David Byers committed
242
  "String mapping lowercase to uppercase and equivalents to each others.")
243
244
245

(defsubst lyskom-unicase-char (c)
  "Smash case and diacritical marks on c." 
David Byers's avatar
David Byers committed
246
  (aref lyskom-collate-table (char-to-int c)))
247
248
249

(defun lyskom-unicase (s)
  "Smash case and diacritical marks of all chars in s." 
250
  (lyskom-save-excursion
David Byers's avatar
X    
David Byers committed
251
   (set-buffer lyskom-buffer)
252
253
254
255
256
257
   (let ((l (length s))
	 (s2 (copy-sequence s)))
     (while (> l 0)
       (setq l (1- l))
       (aset s2 l (lyskom-unicase-char (aref s2 l))))
     s2)))
David Byers's avatar
David Byers committed
258

David Byers's avatar
X    
David Byers committed
259
260
261
262
263
264
265
266
267
268
269
(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 (downcase key))
        (result nil))
    (while list
      (when (string= s (downcase (car (car list))))
        (setq result (car list))
        (setq list nil))
      (setq list (cdr list)))
    result))
David Byers's avatar
David Byers committed
270

David Byers's avatar
X    
David Byers committed
271
272
(defun lyskom-set-default (sym val)
  "Set the value of SYM in the LysKOM buffer to VAL."
David Byers's avatar
David Byers committed
273
  (save-excursion
David Byers's avatar
X    
David Byers committed
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
    (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-excursion
    (set-buffer (or (and (boundp 'lyskom-buffer)
                         (bufferp lyskom-buffer)
                         (buffer-live-p lyskom-buffer)
                         lyskom-buffer)
                    (current-buffer)))
David Byers's avatar
David Byers committed
289
290
    (symbol-value sym)))

David Byers's avatar
X    
David Byers committed
291
292
(defun lyskom-default-value-safe (sym)
  "Get the value of SYM in the LysKOM buffer"
David Byers's avatar
David Byers committed
293
  (save-excursion
David Byers's avatar
X    
David Byers committed
294
295
296
297
298
299
    (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))))
300

David Byers's avatar
David Byers committed
301

302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
;;; ============================================================
;;; Prefix arguments

(defun lyskom-read-text-no-prefix-arg (prompt &optional command)
  "Call in interactive list to read text-no"
  (cond
   ((null current-prefix-arg) lyskom-current-text)
   ((integerp current-prefix-arg) current-prefix-arg)
   ((listp current-prefix-arg) 
    (lyskom-read-number prompt (lyskom-text-at-point)))
   (t (lyskom-error (lyskom-get-string 'bad-text-no-prefix)
                    current-prefix-arg))))



317
318
319
320
321
322
323
324
325
326
327
;;; ======================================================================
;;; Display device management
;;;


;;; Definition of some useful functions from XEmacs

(lyskom-provide-function console-type (&optional console)
  (or window-system 'tty))

(lyskom-provide-function device-class (&optional device)
David Kågedal's avatar
David Kågedal committed
328
329
330
331
332
333
334
  (condition-case nil
      (if (x-display-grayscale-p)
	  (if (x-display-color-p)
	      'color
	    'grayscale)
	'mono)
    (error 'mono)))
335
336
337
338
339
340
341
342
343
344
345
346
347


(lyskom-provide-function frame-property (frame property &optional default)
  (or (cdr (assq property (frame-parameters frame)))
      default))


;;; XEmacs doesn't seem to have a background-mode frame property

(defun lyskom-background-mode ()
  (frame-property (selected-frame) 'background-mode 'light))


David Byers's avatar
X    
David Byers committed
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
;;; ======================================================================
;;; 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-excursion (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-excursion (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-excursion (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. It only works on strings
;;;

(defun lyskom-princ (string &optional stream)
  "Similar to princ but will only print a string. Does not lose text properties
under XEmacs."
  (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))))

          (insert string))
      (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))))


410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
;;; ======================================================================
;;; Faces
;;;

(defun lyskom-set-face-foreground (face color)
  (condition-case nil
      (set-face-foreground face color)
    (error nil)))

(defun lyskom-set-face-background (face color)
  (condition-case nil
      (set-face-background face color)
    (error nil)))


David Kågedal's avatar
David Kågedal committed
425
(defun lyskom-set-face-scheme (scheme)
426
427
428
  "Set the LysKOM color and face scheme to SCHEME. Valid schemes are listed
in lyskom-face-schemes."
  (let ((tmp (assoc scheme lyskom-face-schemes)))
429
430
431
432
433
434
435
436
437
438
439
440
441
    (when (and tmp
               (fboundp 'copy-face)
               (fboundp 'lyskom-set-face-foreground)
               (fboundp 'lyskom-set-face-background))
      (mapcar 
       (function
        (lambda (spec)
          (copy-face (or (elt spec 1) 'default) (elt spec 0))
          (if (elt spec 2)
              (lyskom-set-face-foreground (elt spec 0) (elt spec 2)))
          (if (elt spec 3)
              (lyskom-set-face-background (elt spec 0) (elt spec 3)))))
       (cdr tmp)))))
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463


(defun lyskom-face-resource (face-name attr type)
  (if (eq (lyskom-emacs-version) 'xemacs)
      ;; XEmac style
      (let ((val (x-get-resource (concat face-name ".attribute" attr)
				 (concat "Face.Attribute" attr)
				 type)))
	(cond ((eq type 'string) val)
	      ((and (eq type 'boolean) val) (if (car val) 'on 'off))
	      (t val)))
    ;; Emacs style
    (let ((val (x-get-resource (concat face-name ".attribute" attr)
			       (concat "Face.Attribute" attr))))
      (cond ((eq type 'string) val)
	    ((and val
		  (eq type 'boolean)
		  (member (downcase val) '("on" "true"))) 'on)
	    ((and val (eq type 'boolean)) 'off)
	    (t val)))))


David Byers's avatar
X    
David Byers committed
464
(defun lyskom-modify-face (what face)
David Byers's avatar
David Byers committed
465
  (condition-case nil
David Byers's avatar
X    
David Byers committed
466
467
468
469
      (funcall (intern (concat "make-face-" (symbol-name what)))
               face)
    (error nil)))

470
471
472
473
474
475
476
(defun lyskom-setup-faces ()
  "Initalize the faces in the LysKOM client.
This sets the face scheme according to `kom-default-face-scheme', and
also reads the proper X resources."
  (unless kom-default-face-scheme
    (setq kom-default-face-scheme
	  (condition-case nil
David Kågedal's avatar
David Kågedal committed
477
	      (cond ((eq (device-class) 'mono) 'monochrome)
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
		    ((eq (lyskom-background-mode) 'dark)
		     'inverse)
		    (t 'default))
	    (error 'default))))  
  (lyskom-set-face-scheme kom-default-face-scheme)
  (if (eq (console-type) 'x)
      (mapcar
       (function
	(lambda (face)
	  (let* ((face-name (symbol-name face))
		 (fg (lyskom-face-resource face-name "Foreground" 'string))
		 (bg (lyskom-face-resource face-name "Background" 'string))
		 (bl (lyskom-face-resource face-name "Bold" 'boolean))
		 (it (lyskom-face-resource face-name "Italic" 'boolean))
		 (ul (lyskom-face-resource face-name "Underline" 'boolean)))
	    (if fg (set-face-foreground face fg))
	    (if bg (set-face-background face bg))
David Byers's avatar
X    
David Byers committed
495
496
497
498
	    (if (eq bl 'on) (lyskom-modify-face 'bold face))
	    (if (eq bl 'off) (lyskom-modify-face 'unbold face))
	    (if (eq it 'on) (lyskom-modify-face 'italic face))
	    (if (eq it 'off) (lyskom-modify-face 'unitalic face))
499
500
	    (if ul (set-face-underline-p face (eq ul 'on))))))
       lyskom-faces)))
501
502


David Byers's avatar
David Byers committed
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
;;; ============================================================
;;; Date and time utilities

(defun lyskom-client-date-string (&optional fmt)
  "Format the current client time as a string.
The optional format string FMT specifies the format. If no format string
is supplied time-yyyy-mm-dd-hh-mm is used. The arguments to the format
string are the following: the year, the month, the day, the hour, the 
minutes, the seconds, the full name of the day of week, the abbreviated
name of the day of week."
  (let ((now (decode-time)))
    (lyskom-format (or fmt 'time-yyyy-mm-dd-hh-mm)
                   (elt now 5)
                   (elt now 4)
                   (elt now 3)
                   (elt now 2)
                   (elt now 1)
                   (elt now 0)
                   (elt (lyskom-get-string 'weekdays)
                        (elt now 6))
                   (elt (lyskom-get-string 'weekdays-short)
                        (elt now 6)))))

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
;;; ============================================================
;;; Keymap utilities

(defun lyskom-lookup-key (keymap event &optional accept-default)
  (if (null keymap)
      (and accept-default 
           (lookup-key global-map event))
    (if (not (arrayp event))
	(setq event (vector 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)))))

611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628

;;;
;;; 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 (string= tmp "") 
        tmp
      (concat "[" tmp "]"))))