(charsets-to-mime-charset): New function; copied from emu.el.
[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.37 1997-03-01 04:12:37 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 charsets-to-mime-charset (charsets)
130   "Return MIME charset from list of charset CHARSETS.
131 This function refers variable `charsets-mime-charset-alist'
132 and `default-mime-charset'. [emu.el]"
133   (if charsets
134       (or (catch 'tag
135             (let ((rest charsets-mime-charset-alist)
136                   cell csl)
137               (while (setq cell (car rest))
138                 (if (catch 'not-subset
139                       (let ((set1 charsets)
140                             (set2 (car cell))
141                             obj)
142                         (while set1
143                           (setq obj (car set1))
144                           (or (memq obj set2)
145                               (throw 'not-subset nil)
146                               )
147                           (setq set1 (cdr set1))
148                           )
149                         t))
150                     (throw 'tag (cdr cell))
151                   )
152                 (setq rest (cdr rest))
153                 )))
154           default-mime-charset)))
155
156 (defun detect-mime-charset-region (start end)
157   "Return MIME charset for region between START and END."
158   (charsets-to-mime-charset
159    (find-charset-string (buffer-substring start end))
160    ))
161
162 (defun encode-mime-charset-region (start end charset)
163   "Encode the text between START and END as MIME CHARSET."
164   (let ((cs (mime-charset-to-coding-system charset)))
165     (if cs
166         (encode-coding-region start end cs)
167       )))
168
169 (defun decode-mime-charset-region (start end charset)
170   "Decode the text between START and END as MIME CHARSET."
171   (let ((cs (mime-charset-to-coding-system charset)))
172     (if cs
173         (decode-coding-region start end cs)
174       )))
175
176 (defun encode-mime-charset-string (string charset)
177   "Encode the STRING as MIME CHARSET."
178   (let ((cs (mime-charset-to-coding-system charset)))
179     (if cs
180         (encode-coding-string string cs)
181       string)))
182
183 (defun decode-mime-charset-string (string charset)
184   "Decode the STRING as MIME CHARSET."
185   (let ((cs (mime-charset-to-coding-system charset)))
186     (if cs
187         (decode-coding-string string cs)
188       string)))
189
190
191 ;;; @ button
192 ;;;
193
194 (defvar running-xemacs (string-match "XEmacs" emacs-version))
195
196 (if running-xemacs
197     (require 'overlay)
198   )
199
200 (defvar mime-button-face 'bold
201   "Face used for content-button or URL-button of MIME-Preview buffer.")
202
203 (defvar mime-button-mouse-face 'highlight
204   "Face used for MIME-preview buffer mouse highlighting.")
205
206 (defun mime-add-button (from to func &optional data)
207   "Create a button between FROM and TO with callback FUNC and data DATA."
208   (and mime-button-face
209        (overlay-put (make-overlay from to) 'face mime-button-face))
210   (add-text-properties from to
211                        (nconc
212                         (and mime-button-mouse-face
213                              (list 'mouse-face mime-button-mouse-face))
214                         (list 'mime-button-callback func)
215                         (and data (list 'mime-button-data data))
216                         ))
217   )
218
219 (defvar mime-button-mother-dispatcher nil)
220
221 (defun mime-button-dispatcher (event)
222   "Select the button under point."
223   (interactive "e")
224   (let (buf point func data)
225     (save-window-excursion
226       (mouse-set-point event)
227       (setq buf (current-buffer)
228             point (point)
229             func (get-text-property (point) 'mime-button-callback)
230             data (get-text-property (point) 'mime-button-data)
231             )
232       )
233     (save-excursion
234       (set-buffer buf)
235       (goto-char point)
236       (if func
237           (apply func data)
238         (if (fboundp mime-button-mother-dispatcher)
239             (funcall mime-button-mother-dispatcher event)
240           )
241         ))))
242
243
244 ;;; @ PGP
245 ;;;
246
247 (defvar pgp-function-alist
248   '(
249     ;; for mime-pgp
250     (verify             mc-verify                       "mc-toplev")
251     (decrypt            mc-decrypt                      "mc-toplev")
252     (fetch-key          mc-pgp-fetch-key                "mc-pgp")
253     (snarf-keys         mc-snarf-keys                   "mc-toplev")
254     ;; for mime-edit
255     (mime-sign          tm:mc-pgp-sign-region           "mime-mc")
256     (traditional-sign   mc-pgp-sign-region              "mc-pgp")
257     (encrypt            tm:mc-pgp-encrypt-region        "mime-mc")
258     (insert-key         mc-insert-public-key            "mc-toplev")
259     )
260   "Alist of service names vs. corresponding functions and its filenames.
261 Each element looks like (SERVICE FUNCTION FILE).
262
263 SERVICE is a symbol of PGP processing.  It allows `verify', `decrypt',
264 `fetch-key', `snarf-keys', `mime-sign', `traditional-sign', `encrypt'
265 or `insert-key'.
266
267 Function is a symbol of function to do specified SERVICE.
268
269 FILE is string of filename which has definition of corresponding
270 FUNCTION.")
271
272 (defmacro pgp-function (method)
273   "Return function to do service METHOD."
274   (` (car (cdr (assq (, method) (symbol-value 'pgp-function-alist)))))
275   )
276
277 (mapcar (function
278          (lambda (method)
279            (autoload (second method)(third method))
280            ))
281         pgp-function-alist)
282
283
284 ;;; @ method selector kernel
285 ;;;
286
287 ;;; @@ field unifier
288 ;;;
289
290 (defun field-unifier-for-default (a b)
291   (let ((ret
292          (cond ((equal a b)    a)
293                ((null (cdr b)) a)
294                ((null (cdr a)) b)
295                )))
296     (if ret
297         (list nil ret nil)
298       )))
299
300 (defun field-unifier-for-mode (a b)
301   (let ((va (cdr a)))
302     (if (if (consp va)
303             (member (cdr b) va)
304           (equal va (cdr b))
305           )
306         (list nil b nil)
307       )))
308
309 (defun field-unify (a b)
310   (let ((sym (intern (concat "field-unifier-for-" (symbol-name (car a))))))
311     (or (fboundp sym)
312         (setq sym (function field-unifier-for-default))
313         )
314     (funcall sym a b)
315     ))
316
317
318 ;;; @@ type unifier
319 ;;;
320
321 (defun assoc-unify (class instance)
322   (catch 'tag
323     (let ((cla (copy-alist class))
324           (ins (copy-alist instance))
325           (r class)
326           cell aret ret prev rest)
327       (while r
328         (setq cell (car r))
329         (setq aret (assoc (car cell) ins))
330         (if aret
331             (if (setq ret (field-unify cell aret))
332                 (progn
333                   (if (car ret)
334                       (setq prev (put-alist (car (car ret))
335                                             (cdr (car ret))
336                                             prev))
337                     )
338                   (if (nth 2 ret)
339                       (setq rest (put-alist (car (nth 2 ret))
340                                             (cdr (nth 2 ret))
341                                             rest))
342                     )
343                   (setq cla (put-alist (car cell)(cdr (nth 1 ret)) cla))
344                   (setq ins (del-alist (car cell) ins))
345                   )
346               (throw 'tag nil)
347               ))
348         (setq r (cdr r))
349         )
350       (setq r (copy-alist ins))
351       (while r
352         (setq cell (car r))
353         (setq aret (assoc (car cell) cla))
354         (if aret
355             (if (setq ret (field-unify cell aret))
356                 (progn
357                   (if (car ret)
358                       (setq prev (put-alist (car (car ret))
359                                             (cdr (car ret))
360                                             prev))
361                     )
362                   (if (nth 2 ret)
363                       (setq rest (put-alist (car (nth 2 ret))
364                                             (cdr (nth 2 ret))
365                                             rest))
366                     )
367                   (setq cla (del-alist (car cell) cla))
368                   (setq ins (put-alist (car cell)(cdr (nth 1 ret)) ins))
369                   )
370               (throw 'tag nil)
371               ))
372         (setq r (cdr r))
373         )
374       (list prev (append cla ins) rest)
375       )))
376
377 (defun get-unified-alist (db al)
378   (let ((r db) ret)
379     (catch 'tag
380       (while r
381         (if (setq ret (nth 1 (assoc-unify (car r) al)))
382             (throw 'tag ret)
383           )
384         (setq r (cdr r))
385         ))))
386
387 (defun delete-atype (atl al)
388   (let* ((r atl) ret oal)
389     (setq oal
390           (catch 'tag
391             (while r
392               (if (setq ret (nth 1 (assoc-unify (car r) al)))
393                   (throw 'tag (car r))
394                 )
395               (setq r (cdr r))
396               )))
397     (delete oal atl)
398     ))
399
400 (defun remove-atype (sym al)
401   (and (boundp sym)
402        (set sym (delete-atype (eval sym) al))
403        ))
404
405 (defun replace-atype (atl old-al new-al)
406   (let* ((r atl) ret oal)
407     (if (catch 'tag
408           (while r
409             (if (setq ret (nth 1 (assoc-unify (car r) old-al)))
410                 (throw 'tag (rplaca r new-al))
411               )
412             (setq r (cdr r))
413             ))
414         atl)))
415
416 (defun set-atype (sym al &rest options)
417   (if (null (boundp sym))
418       (set sym al)
419     (let* ((replacement (memq 'replacement options))
420            (ignore-fields (car (cdr (memq 'ignore options))))
421            (remove (or (car (cdr (memq 'remove options)))
422                        (let ((ral (copy-alist al)))
423                          (mapcar (function
424                                   (lambda (type)
425                                     (setq ral (del-alist type ral))
426                                     ))
427                                  ignore-fields)
428                          ral)))
429            )
430       (set sym
431            (or (if replacement
432                    (replace-atype (eval sym) remove al)
433                  )
434                (cons al
435                      (delete-atype (eval sym) remove)
436                      )
437                )))))
438
439
440 ;;; @ rot13-47
441 ;;;
442 ;; caesar-region written by phr@prep.ai.mit.edu  Nov 86
443 ;; modified by tower@prep Nov 86
444 ;; gnus-caesar-region
445 ;; Modified by umerin@flab.flab.Fujitsu.JUNET for ROT47.
446 (defun tm:caesar-region (&optional n)
447   "Caesar rotation of region by N, default 13, for decrypting netnews.
448 ROT47 will be performed for Japanese text in any case."
449   (interactive (if current-prefix-arg   ; Was there a prefix arg?
450                    (list (prefix-numeric-value current-prefix-arg))
451                  (list nil)))
452   (cond ((not (numberp n)) (setq n 13))
453         (t (setq n (mod n 26))))        ;canonicalize N
454   (if (not (zerop n))           ; no action needed for a rot of 0
455       (progn
456         (if (or (not (boundp 'caesar-translate-table))
457                 (/= (aref caesar-translate-table ?a) (+ ?a n)))
458             (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
459               (message "Building caesar-translate-table...")
460               (setq caesar-translate-table (make-vector 256 0))
461               (while (< i 256)
462                 (aset caesar-translate-table i i)
463                 (setq i (1+ i)))
464               (setq lower (concat lower lower) upper (upcase lower) i 0)
465               (while (< i 26)
466                 (aset caesar-translate-table (+ ?a i) (aref lower (+ i n)))
467                 (aset caesar-translate-table (+ ?A i) (aref upper (+ i n)))
468                 (setq i (1+ i)))
469               ;; ROT47 for Japanese text.
470               ;; Thanks to ichikawa@flab.fujitsu.junet.
471               (setq i 161)
472               (let ((t1 (logior ?O 128))
473                     (t2 (logior ?! 128))
474                     (t3 (logior ?~ 128)))
475                 (while (< i 256)
476                   (aset caesar-translate-table i
477                         (let ((v (aref caesar-translate-table i)))
478                           (if (<= v t1) (if (< v t2) v (+ v 47))
479                             (if (<= v t3) (- v 47) v))))
480                   (setq i (1+ i))))
481               (message "Building caesar-translate-table...done")))
482         (let ((from (region-beginning))
483               (to (region-end))
484               (i 0) str len)
485           (setq str (buffer-substring from to))
486           (setq len (length str))
487           (while (< i len)
488             (aset str i (aref caesar-translate-table (aref str i)))
489             (setq i (1+ i)))
490           (goto-char from)
491           (delete-region from to)
492           (insert str)))))
493
494
495 ;;; @ field
496 ;;;
497
498 (defsubst regexp-or (&rest args)
499   (concat "\\(" (mapconcat (function identity) args "\\|") "\\)"))
500
501 (defun tm:set-fields (sym field-list &optional regexp-sym)
502   (or regexp-sym
503       (setq regexp-sym
504             (let ((name (symbol-name sym)))
505               (intern
506                (concat (if (string-match "\\(.*\\)-list" name)
507                            (substring name 0 (match-end 1))
508                          name)
509                        "-regexp")
510                )))
511       )
512   (set sym field-list)
513   (set regexp-sym
514        (concat "^" (apply (function regexp-or) field-list) ":"))
515   )
516
517 (defun tm:add-fields (sym field-list &optional regexp-sym)
518   (or regexp-sym
519       (setq regexp-sym
520             (let ((name (symbol-name sym)))
521               (intern
522                (concat (if (string-match "\\(.*\\)-list" name)
523                            (substring name 0 (match-end 1))
524                          name)
525                        "-regexp")
526                )))
527       )
528   (let ((fields (eval sym)))
529     (mapcar (function
530              (lambda (field)
531                (or (member field fields)
532                    (setq fields (cons field fields))
533                    )
534                ))
535             (reverse field-list)
536             )
537     (set regexp-sym
538          (concat "^" (apply (function regexp-or) fields) ":"))
539     (set sym fields)
540     ))
541
542 (defun tm:delete-fields (sym field-list &optional regexp-sym)
543   (or regexp-sym
544       (setq regexp-sym
545             (let ((name (symbol-name sym)))
546               (intern
547                (concat (if (string-match "\\(.*\\)-list" name)
548                            (substring name 0 (match-end 1))
549                          name)
550                        "-regexp")
551                )))
552       )
553   (let ((fields (eval sym)))
554     (mapcar (function
555              (lambda (field)
556                (setq fields (delete field fields))
557                ))
558             field-list)
559     (set regexp-sym
560          (concat "^" (apply (function regexp-or) fields) ":"))
561     (set sym fields)
562     ))
563
564
565 ;;; @ RCS version
566 ;;;
567
568 (defsubst get-version-string (id)
569   "Return a version-string from RCS ID."
570   (and (string-match ",v \\([0-9][0-9.][0-9.]+\\)" id)
571        (substring id (match-beginning 1)(match-end 1))
572        ))
573
574
575 ;;; @ Other Utility
576 ;;;
577
578 (defsubst eliminate-top-spaces (string)
579   "Eliminate top sequence of space or tab in STRING."
580   (if (string-match "^[ \t]+" string)
581       (substring string (match-end 0))
582     string))
583
584 (defun call-after-loaded (module func &optional hook-name)
585   "If MODULE is provided, then FUNC is called.
586 Otherwise func is set to MODULE-load-hook.
587 If optional argument HOOK-NAME is specified,
588 it is used as hook to set."
589   (if (featurep module)
590       (funcall func)
591     (or hook-name
592         (setq hook-name (intern (concat (symbol-name module) "-load-hook")))
593         )
594     (add-hook hook-name func)
595     ))
596
597 (defmacro defun-maybe (name &rest everything-else)
598   (or (and (fboundp name)
599            (not (get name 'defun-maybe))
600            )
601       `(or (fboundp (quote ,name))
602            (progn
603              (defun ,name ,@everything-else)
604              (put (quote ,name) 'defun-maybe t)
605              ))
606       ))
607
608 (put 'defun-maybe 'lisp-indent-function 'defun)
609
610 (defun-maybe functionp (obj)
611   "Returns t if OBJ is a function, nil otherwise.
612 \[XEmacs emulating function]"
613   (or (subrp obj)
614       (byte-code-function-p obj)
615       (and (symbolp obj)(fboundp obj))
616       (and (consp obj)(eq (car obj) 'lambda))
617       ))
618
619
620 ;;; @ end
621 ;;;
622
623 (provide 'mime-def)
624
625 ;;; mime-def.el ends here