Require cl.
[elisp/semi.git] / mime-def.el
1 ;;; mime-def.el --- definition module for SEMI
2
3 ;; Copyright (C) 1995,1996,1997 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; Version: $Id: mime-def.el,v 0.35 1997-02-28 06:46:48 tmorioka Exp $
7 ;; Keywords: definition, MIME, multimedia, mail, news
8
9 ;; This file is part of SEMI (SEMI is Emacs MIME Interfaces).
10
11 ;; This program is free software; you can redistribute it and/or
12 ;; modify it under the terms of the GNU General Public License as
13 ;; published by the Free Software Foundation; either version 2, or (at
14 ;; your option) any later version.
15
16 ;; This program is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Code:
27
28 (require 'cl)
29
30
31 ;;; @ variables
32 ;;;
33
34 (defvar mime/tmp-dir (or (getenv "TM_TMP_DIR") "/tmp/"))
35
36 (defvar mime/use-multi-frame
37   (and (>= emacs-major-version 19) window-system))
38
39 (defvar mime/find-file-function
40   (if mime/use-multi-frame
41       (function find-file-other-frame)
42     (function find-file)
43     ))
44
45 (defvar mime/output-buffer-window-is-shared-with-bbdb t
46   "*If t, mime/output-buffer window is shared with BBDB window.")
47
48
49 ;;; @ constants
50 ;;;
51
52 (defconst mime/output-buffer-name "*MIME-out*")
53 (defconst mime/temp-buffer-name " *MIME-temp*")
54
55
56 ;;; @ definitions about MIME
57 ;;;
58
59 (defconst mime/tspecials "][\000-\040()<>@,\;:\\\"/?.=")
60 (defconst mime/token-regexp (concat "[^" mime/tspecials "]+"))
61 (defconst mime-charset-regexp mime/token-regexp)
62
63 (defconst mime/content-type-subtype-regexp
64   (concat mime/token-regexp "/" mime/token-regexp))
65
66 (defconst mime/disposition-type-regexp mime/token-regexp)
67
68
69 ;;; @ MIME charset
70 ;;;
71
72 (defvar charsets-mime-charset-alist
73   '(((ascii)                                            . us-ascii)
74     ((ascii latin-iso8859-1)                            . iso-8859-1)
75     ((ascii latin-iso8859-2)                            . iso-8859-2)
76     ((ascii latin-iso8859-3)                            . iso-8859-3)
77     ((ascii latin-iso8859-4)                            . iso-8859-4)
78 ;;; ((ascii cyrillic-iso8859-5)                         . iso-8859-5)
79     ((ascii cyrillic-iso8859-5)                         . koi8-r)
80     ((ascii arabic-iso8859-6)                           . iso-8859-6)
81     ((ascii greek-iso8859-7)                            . iso-8859-7)
82     ((ascii hebrew-iso8859-8)                           . iso-8859-8)
83     ((ascii latin-iso8859-9)                            . iso-8859-9)
84     ((ascii latin-jisx0201
85             japanese-jisx0208-1978 japanese-jisx0208)   . iso-2022-jp)
86     ((ascii korean-ksc5601)                             . euc-kr)
87     ((ascii chinese-gb2312)                             . cn-gb-2312)
88     ((ascii chinese-big5-1 chinese-big5-2)              . cn-big5)
89     ((ascii latin-iso8859-1 greek-iso8859-7
90             latin-jisx0201 japanese-jisx0208-1978
91             chinese-gb2312 japanese-jisx0208
92             korean-ksc5601 japanese-jisx0212)           . iso-2022-jp-2)
93     ((ascii latin-iso8859-1 greek-iso8859-7
94             latin-jisx0201 japanese-jisx0208-1978
95             chinese-gb2312 japanese-jisx0208
96             korean-ksc5601 japanese-jisx0212
97             chinese-cns11643-1 chinese-cns11643-2)      . iso-2022-int-1)
98     ((ascii latin-iso8859-1 latin-iso8859-2
99             cyrillic-iso8859-5 greek-iso8859-7
100             latin-jisx0201 japanese-jisx0208-1978
101             chinese-gb2312 japanese-jisx0208
102             korean-ksc5601 japanese-jisx0212
103             chinese-cns11643-1 chinese-cns11643-2
104             chinese-cns11643-3 chinese-cns11643-4
105             chinese-cns11643-5 chinese-cns11643-6
106             chinese-cns11643-7)                         . iso-2022-cjk)
107     ))
108
109 (defvar default-mime-charset 'x-ctext)
110
111 (defvar mime-charset-coding-system-alist
112   '((x-ctext            . ctext)
113     (gb2312             . cn-gb-2312)
114     (iso-2022-jp-2      . iso-2022-ss2-7)
115     ))
116
117 (defun mime-charset-to-coding-system (charset &optional lbt)
118   (if (stringp charset)
119       (setq charset (intern (downcase charset)))
120     )
121   (let ((cs
122          (or (cdr (assq charset mime-charset-coding-system-alist))
123              (and (coding-system-p charset) charset)
124              )))
125     (if lbt
126         (intern (concat (symbol-name cs) "-" (symbol-name lbt)))
127       cs)))
128
129 (defun detect-mime-charset-region (start end)
130   "Return MIME charset for region between START and END."
131   (charsets-to-mime-charset
132    (find-charset-string (buffer-substring start end))
133    ))
134
135 (defun encode-mime-charset-region (start end charset)
136   "Encode the text between START and END as MIME CHARSET."
137   (let ((cs (mime-charset-to-coding-system charset)))
138     (if cs
139         (encode-coding-region start end cs)
140       )))
141
142 (defun decode-mime-charset-region (start end charset)
143   "Decode the text between START and END as MIME CHARSET."
144   (let ((cs (mime-charset-to-coding-system charset)))
145     (if cs
146         (decode-coding-region start end cs)
147       )))
148
149 (defun encode-mime-charset-string (string charset)
150   "Encode the STRING as MIME CHARSET."
151   (let ((cs (mime-charset-to-coding-system charset)))
152     (if cs
153         (encode-coding-string string cs)
154       string)))
155
156 (defun decode-mime-charset-string (string charset)
157   "Decode the STRING as MIME CHARSET."
158   (let ((cs (mime-charset-to-coding-system charset)))
159     (if cs
160         (decode-coding-string string cs)
161       string)))
162
163
164 ;;; @ button
165 ;;;
166
167 (defvar running-xemacs (string-match "XEmacs" emacs-version))
168
169 (if running-xemacs
170     (require 'overlay)
171   )
172
173 (defvar mime-button-face 'bold
174   "Face used for content-button or URL-button of MIME-Preview buffer.")
175
176 (defvar mime-button-mouse-face 'highlight
177   "Face used for MIME-preview buffer mouse highlighting.")
178
179 (defun mime-add-button (from to func &optional data)
180   "Create a button between FROM and TO with callback FUNC and data DATA."
181   (and mime-button-face
182        (overlay-put (make-overlay from to) 'face mime-button-face))
183   (add-text-properties from to
184                        (nconc
185                         (and mime-button-mouse-face
186                              (list 'mouse-face mime-button-mouse-face))
187                         (list 'mime-button-callback func)
188                         (and data (list 'mime-button-data data))
189                         ))
190   )
191
192 (defvar mime-button-mother-dispatcher nil)
193
194 (defun mime-button-dispatcher (event)
195   "Select the button under point."
196   (interactive "e")
197   (let (buf point func data)
198     (save-window-excursion
199       (mouse-set-point event)
200       (setq buf (current-buffer)
201             point (point)
202             func (get-text-property (point) 'mime-button-callback)
203             data (get-text-property (point) 'mime-button-data)
204             )
205       )
206     (save-excursion
207       (set-buffer buf)
208       (goto-char point)
209       (if func
210           (apply func data)
211         (if (fboundp mime-button-mother-dispatcher)
212             (funcall mime-button-mother-dispatcher event)
213           )
214         ))))
215
216
217 ;;; @ PGP
218 ;;;
219
220 (defvar pgp-function-alist
221   '(
222     ;; for mime-pgp
223     (verify             mc-verify                       "mc-toplev")
224     (decrypt            mc-decrypt                      "mc-toplev")
225     (fetch-key          mc-pgp-fetch-key                "mc-pgp")
226     (snarf-keys         mc-snarf-keys                   "mc-toplev")
227     ;; for mime-edit
228     (mime-sign          tm:mc-pgp-sign-region           "mime-mc")
229     (traditional-sign   mc-pgp-sign-region              "mc-pgp")
230     (encrypt            tm:mc-pgp-encrypt-region        "mime-mc")
231     (insert-key         mc-insert-public-key            "mc-toplev")
232     )
233   "Alist of service names vs. corresponding functions and its filenames.
234 Each element looks like (SERVICE FUNCTION FILE).
235
236 SERVICE is a symbol of PGP processing.  It allows `verify', `decrypt',
237 `fetch-key', `snarf-keys', `mime-sign', `traditional-sign', `encrypt'
238 or `insert-key'.
239
240 Function is a symbol of function to do specified SERVICE.
241
242 FILE is string of filename which has definition of corresponding
243 FUNCTION.")
244
245 (defmacro pgp-function (method)
246   "Return function to do service METHOD."
247   (` (car (cdr (assq (, method) (symbol-value 'pgp-function-alist)))))
248   )
249
250 (mapcar (function
251          (lambda (method)
252            (autoload (second method)(third method))
253            ))
254         pgp-function-alist)
255
256
257 ;;; @ method selector kernel
258 ;;;
259
260 ;;; @@ field unifier
261 ;;;
262
263 (defun field-unifier-for-default (a b)
264   (let ((ret
265          (cond ((equal a b)    a)
266                ((null (cdr b)) a)
267                ((null (cdr a)) b)
268                )))
269     (if ret
270         (list nil ret nil)
271       )))
272
273 (defun field-unifier-for-mode (a b)
274   (let ((va (cdr a)))
275     (if (if (consp va)
276             (member (cdr b) va)
277           (equal va (cdr b))
278           )
279         (list nil b nil)
280       )))
281
282 (defun field-unify (a b)
283   (let ((sym (intern (concat "field-unifier-for-" (symbol-name (car a))))))
284     (or (fboundp sym)
285         (setq sym (function field-unifier-for-default))
286         )
287     (funcall sym a b)
288     ))
289
290
291 ;;; @@ type unifier
292 ;;;
293
294 (defun assoc-unify (class instance)
295   (catch 'tag
296     (let ((cla (copy-alist class))
297           (ins (copy-alist instance))
298           (r class)
299           cell aret ret prev rest)
300       (while r
301         (setq cell (car r))
302         (setq aret (assoc (car cell) ins))
303         (if aret
304             (if (setq ret (field-unify cell aret))
305                 (progn
306                   (if (car ret)
307                       (setq prev (put-alist (car (car ret))
308                                             (cdr (car ret))
309                                             prev))
310                     )
311                   (if (nth 2 ret)
312                       (setq rest (put-alist (car (nth 2 ret))
313                                             (cdr (nth 2 ret))
314                                             rest))
315                     )
316                   (setq cla (put-alist (car cell)(cdr (nth 1 ret)) cla))
317                   (setq ins (del-alist (car cell) ins))
318                   )
319               (throw 'tag nil)
320               ))
321         (setq r (cdr r))
322         )
323       (setq r (copy-alist ins))
324       (while r
325         (setq cell (car r))
326         (setq aret (assoc (car cell) cla))
327         (if aret
328             (if (setq ret (field-unify cell aret))
329                 (progn
330                   (if (car ret)
331                       (setq prev (put-alist (car (car ret))
332                                             (cdr (car ret))
333                                             prev))
334                     )
335                   (if (nth 2 ret)
336                       (setq rest (put-alist (car (nth 2 ret))
337                                             (cdr (nth 2 ret))
338                                             rest))
339                     )
340                   (setq cla (del-alist (car cell) cla))
341                   (setq ins (put-alist (car cell)(cdr (nth 1 ret)) ins))
342                   )
343               (throw 'tag nil)
344               ))
345         (setq r (cdr r))
346         )
347       (list prev (append cla ins) rest)
348       )))
349
350 (defun get-unified-alist (db al)
351   (let ((r db) ret)
352     (catch 'tag
353       (while r
354         (if (setq ret (nth 1 (assoc-unify (car r) al)))
355             (throw 'tag ret)
356           )
357         (setq r (cdr r))
358         ))))
359
360 (defun delete-atype (atl al)
361   (let* ((r atl) ret oal)
362     (setq oal
363           (catch 'tag
364             (while r
365               (if (setq ret (nth 1 (assoc-unify (car r) al)))
366                   (throw 'tag (car r))
367                 )
368               (setq r (cdr r))
369               )))
370     (delete oal atl)
371     ))
372
373 (defun remove-atype (sym al)
374   (and (boundp sym)
375        (set sym (delete-atype (eval sym) al))
376        ))
377
378 (defun replace-atype (atl old-al new-al)
379   (let* ((r atl) ret oal)
380     (if (catch 'tag
381           (while r
382             (if (setq ret (nth 1 (assoc-unify (car r) old-al)))
383                 (throw 'tag (rplaca r new-al))
384               )
385             (setq r (cdr r))
386             ))
387         atl)))
388
389 (defun set-atype (sym al &rest options)
390   (if (null (boundp sym))
391       (set sym al)
392     (let* ((replacement (memq 'replacement options))
393            (ignore-fields (car (cdr (memq 'ignore options))))
394            (remove (or (car (cdr (memq 'remove options)))
395                        (let ((ral (copy-alist al)))
396                          (mapcar (function
397                                   (lambda (type)
398                                     (setq ral (del-alist type ral))
399                                     ))
400                                  ignore-fields)
401                          ral)))
402            )
403       (set sym
404            (or (if replacement
405                    (replace-atype (eval sym) remove al)
406                  )
407                (cons al
408                      (delete-atype (eval sym) remove)
409                      )
410                )))))
411
412
413 ;;; @ rot13-47
414 ;;;
415 ;; caesar-region written by phr@prep.ai.mit.edu  Nov 86
416 ;; modified by tower@prep Nov 86
417 ;; gnus-caesar-region
418 ;; Modified by umerin@flab.flab.Fujitsu.JUNET for ROT47.
419 (defun tm:caesar-region (&optional n)
420   "Caesar rotation of region by N, default 13, for decrypting netnews.
421 ROT47 will be performed for Japanese text in any case."
422   (interactive (if current-prefix-arg   ; Was there a prefix arg?
423                    (list (prefix-numeric-value current-prefix-arg))
424                  (list nil)))
425   (cond ((not (numberp n)) (setq n 13))
426         (t (setq n (mod n 26))))        ;canonicalize N
427   (if (not (zerop n))           ; no action needed for a rot of 0
428       (progn
429         (if (or (not (boundp 'caesar-translate-table))
430                 (/= (aref caesar-translate-table ?a) (+ ?a n)))
431             (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
432               (message "Building caesar-translate-table...")
433               (setq caesar-translate-table (make-vector 256 0))
434               (while (< i 256)
435                 (aset caesar-translate-table i i)
436                 (setq i (1+ i)))
437               (setq lower (concat lower lower) upper (upcase lower) i 0)
438               (while (< i 26)
439                 (aset caesar-translate-table (+ ?a i) (aref lower (+ i n)))
440                 (aset caesar-translate-table (+ ?A i) (aref upper (+ i n)))
441                 (setq i (1+ i)))
442               ;; ROT47 for Japanese text.
443               ;; Thanks to ichikawa@flab.fujitsu.junet.
444               (setq i 161)
445               (let ((t1 (logior ?O 128))
446                     (t2 (logior ?! 128))
447                     (t3 (logior ?~ 128)))
448                 (while (< i 256)
449                   (aset caesar-translate-table i
450                         (let ((v (aref caesar-translate-table i)))
451                           (if (<= v t1) (if (< v t2) v (+ v 47))
452                             (if (<= v t3) (- v 47) v))))
453                   (setq i (1+ i))))
454               (message "Building caesar-translate-table...done")))
455         (let ((from (region-beginning))
456               (to (region-end))
457               (i 0) str len)
458           (setq str (buffer-substring from to))
459           (setq len (length str))
460           (while (< i len)
461             (aset str i (aref caesar-translate-table (aref str i)))
462             (setq i (1+ i)))
463           (goto-char from)
464           (delete-region from to)
465           (insert str)))))
466
467
468 ;;; @ field
469 ;;;
470
471 (defsubst regexp-or (&rest args)
472   (concat "\\(" (mapconcat (function identity) args "\\|") "\\)"))
473
474 (defun tm:set-fields (sym field-list &optional regexp-sym)
475   (or regexp-sym
476       (setq regexp-sym
477             (let ((name (symbol-name sym)))
478               (intern
479                (concat (if (string-match "\\(.*\\)-list" name)
480                            (substring name 0 (match-end 1))
481                          name)
482                        "-regexp")
483                )))
484       )
485   (set sym field-list)
486   (set regexp-sym
487        (concat "^" (apply (function regexp-or) field-list) ":"))
488   )
489
490 (defun tm:add-fields (sym field-list &optional regexp-sym)
491   (or regexp-sym
492       (setq regexp-sym
493             (let ((name (symbol-name sym)))
494               (intern
495                (concat (if (string-match "\\(.*\\)-list" name)
496                            (substring name 0 (match-end 1))
497                          name)
498                        "-regexp")
499                )))
500       )
501   (let ((fields (eval sym)))
502     (mapcar (function
503              (lambda (field)
504                (or (member field fields)
505                    (setq fields (cons field fields))
506                    )
507                ))
508             (reverse field-list)
509             )
510     (set regexp-sym
511          (concat "^" (apply (function regexp-or) fields) ":"))
512     (set sym fields)
513     ))
514
515 (defun tm:delete-fields (sym field-list &optional regexp-sym)
516   (or regexp-sym
517       (setq regexp-sym
518             (let ((name (symbol-name sym)))
519               (intern
520                (concat (if (string-match "\\(.*\\)-list" name)
521                            (substring name 0 (match-end 1))
522                          name)
523                        "-regexp")
524                )))
525       )
526   (let ((fields (eval sym)))
527     (mapcar (function
528              (lambda (field)
529                (setq fields (delete field fields))
530                ))
531             field-list)
532     (set regexp-sym
533          (concat "^" (apply (function regexp-or) fields) ":"))
534     (set sym fields)
535     ))
536
537
538 ;;; @ RCS version
539 ;;;
540
541 (defsubst get-version-string (id)
542   "Return a version-string from RCS ID."
543   (and (string-match ",v \\([0-9][0-9.][0-9.]+\\)" id)
544        (substring id (match-beginning 1)(match-end 1))
545        ))
546
547
548 ;;; @ Other Utility
549 ;;;
550
551 (defun call-after-loaded (module func &optional hook-name)
552   "If MODULE is provided, then FUNC is called.
553 Otherwise func is set to MODULE-load-hook.
554 If optional argument HOOK-NAME is specified,
555 it is used as hook to set."
556   (if (featurep module)
557       (funcall func)
558     (or hook-name
559         (setq hook-name (intern (concat (symbol-name module) "-load-hook")))
560         )
561     (add-hook hook-name func)
562     ))
563
564 (defmacro defun-maybe (name &rest everything-else)
565   (or (and (fboundp name)
566            (not (get name 'defun-maybe))
567            )
568       `(or (fboundp (quote ,name))
569            (progn
570              (defun ,name ,@everything-else)
571              (put (quote ,name) 'defun-maybe t)
572              ))
573       ))
574
575 (put 'defun-maybe 'lisp-indent-function 'defun)
576
577 (defun-maybe functionp (obj)
578   "Returns t if OBJ is a function, nil otherwise.
579 \[XEmacs emulating function]"
580   (or (subrp obj)
581       (byte-code-function-p obj)
582       (and (symbolp obj)(fboundp obj))
583       (and (consp obj)(eq (car obj) 'lambda))
584       ))
585
586
587 ;;; @ end
588 ;;;
589
590 (provide 'mime-def)
591
592 ;;; mime-def.el ends here