Sync up with Pterodactyl Gnus v0.91, etc. See ChangeLog for more details.
[elisp/gnus.git-] / lisp / mm-util.el
1 ;;; mm-util.el --- Utility functions for MIME things
2 ;; Copyright (C) 1998,99 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
5 ;;      MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; This file is part of GNU Emacs.
7
8 ;; GNU Emacs is free software; you can redistribute it and/or modify
9 ;; 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 ;; any later version.
12
13 ;; GNU Emacs is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
20 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 ;; Boston, MA 02111-1307, USA.
22
23 ;;; Commentary:
24
25 ;;; Code:
26
27 (defconst mm-running-xemacs (string-match "XEmacs" emacs-version))
28
29 (defconst mm-binary-coding-system
30   (if mm-running-xemacs
31       'binary 'no-conversion)
32   "100% binary coding system.")
33
34 (defconst mm-text-coding-system
35   (and (fboundp 'coding-system-list)
36    (if (memq system-type '(windows-nt ms-dos ms-windows))
37        'raw-text-dos 'raw-text))
38   "Text-safe coding system (For removing ^M).")
39
40 (defvar mm-mime-mule-charset-alist
41   '((us-ascii ascii)
42     (iso-8859-1 latin-iso8859-1)
43     (iso-8859-2 latin-iso8859-2)
44     (iso-8859-3 latin-iso8859-3)
45     (iso-8859-4 latin-iso8859-4)
46     (iso-8859-5 cyrillic-iso8859-5)
47     (koi8-r cyrillic-iso8859-5)
48     (iso-8859-6 arabic-iso8859-6)
49     (iso-8859-7 greek-iso8859-7)
50     (iso-8859-8 hebrew-iso8859-8)
51     (iso-8859-9 latin-iso8859-9)
52     (viscii vietnamese-viscii-lower)
53     (iso-2022-jp latin-jisx0201 japanese-jisx0208 japanese-jisx0208-1978)
54     (euc-kr korean-ksc5601)
55     (cn-gb-2312 chinese-gb2312)
56     (cn-big5 chinese-big5-1 chinese-big5-2)
57     (tibetan tibetan)
58     (thai-tis620 thai-tis620)
59     (iso-2022-7bit ethiopic arabic-1-column arabic-2-column)
60     (iso-2022-jp-2 latin-iso8859-1 greek-iso8859-7
61                    latin-jisx0201 japanese-jisx0208-1978
62                    chinese-gb2312 japanese-jisx0208
63                    korean-ksc5601 japanese-jisx0212
64                    katakana-jisx0201)
65     (iso-2022-int-1 latin-iso8859-1 greek-iso8859-7
66                     latin-jisx0201 japanese-jisx0208-1978
67                     chinese-gb2312 japanese-jisx0208
68                     korean-ksc5601 japanese-jisx0212
69                     chinese-cns11643-1 chinese-cns11643-2)
70     (iso-2022-int-1 latin-iso8859-1 latin-iso8859-2
71                     cyrillic-iso8859-5 greek-iso8859-7
72                     latin-jisx0201 japanese-jisx0208-1978
73                     chinese-gb2312 japanese-jisx0208
74                     korean-ksc5601 japanese-jisx0212
75                     chinese-cns11643-1 chinese-cns11643-2
76                     chinese-cns11643-3 chinese-cns11643-4
77                     chinese-cns11643-5 chinese-cns11643-6
78                     chinese-cns11643-7))
79   "Alist of MIME-charset/MULE-charsets.")
80
81
82 (eval-and-compile
83   (mapcar
84    (lambda (elem)
85      (let ((nfunc (intern (format "mm-%s" (car elem)))))
86        (if (fboundp (car elem))
87            (fset nfunc (car elem))
88          (fset nfunc (cdr elem)))))
89    '((decode-coding-string . (lambda (s a) s))
90      (encode-coding-string . (lambda (s a) s))
91      (encode-coding-region . ignore)
92      (coding-system-list . ignore)
93      (decode-coding-region . ignore)
94      (char-int . identity)
95      (device-type . ignore)
96      (coding-system-equal . equal)
97      (annotationp . ignore)
98      (set-buffer-file-coding-system . ignore)
99      (make-char
100       . (lambda (charset int)
101           (int-to-char int)))
102      (read-coding-system
103       . (lambda (prompt)
104           "Prompt the user for a coding system."
105           (completing-read
106            prompt (mapcar (lambda (s) (list (symbol-name (car s))))
107                           mm-mime-mule-charset-alist)))))))
108
109 (defvar mm-coding-system-list nil)
110 (defun mm-get-coding-system-list ()
111   "Get the coding system list."
112   (or mm-coding-system-list
113       (setq mm-coding-system-list (mm-coding-system-list))))
114
115 (defvar mm-charset-synonym-alist
116   '((big5 . cn-big5)
117     (gb2312 . cn-gb-2312)
118     (x-ctext . ctext))
119   "A mapping from invalid charset names to the real charset names.")
120
121 ;;; Internal variables:
122
123 ;;; Functions:
124
125 (defun mm-mule-charset-to-mime-charset (charset)
126   "Return the MIME charset corresponding to MULE CHARSET."
127   (let ((alist mm-mime-mule-charset-alist)
128         out)
129     (while alist
130       (when (memq charset (cdar alist))
131         (setq out (caar alist)
132               alist nil))
133       (pop alist))
134     out))
135
136 (defun mm-charset-to-coding-system (charset &optional lbt)
137   "Return coding-system corresponding to CHARSET.
138 CHARSET is a symbol naming a MIME charset.
139 If optional argument LBT (`unix', `dos' or `mac') is specified, it is
140 used as the line break code type of the coding system."
141   (when (stringp charset)
142     (setq charset (intern (downcase charset))))
143   (setq charset
144         (or (cdr (assq charset mm-charset-synonym-alist))
145             charset))
146   (when lbt
147     (setq charset (intern (format "%s-%s" charset lbt))))
148   (cond
149    ;; Running in a non-MULE environment.
150    ((null (mm-get-coding-system-list))
151     charset)
152    ;; ascii
153    ((eq charset 'us-ascii)
154     'ascii)
155    ;; Check to see whether we can handle this charset.
156    ((memq charset (mm-get-coding-system-list))
157     charset)
158    ;; Nope.
159    (t
160     nil)))
161
162 (defun mm-replace-chars-in-string (string from to)
163   "Replace characters in STRING from FROM to TO."
164   (let ((string (substring string 0))   ;Copy string.
165         (len (length string))
166         (idx 0))
167     ;; Replace all occurrences of FROM with TO.
168     (while (< idx len)
169       (when (= (aref string idx) from)
170         (aset string idx to))
171       (setq idx (1+ idx)))
172     string))
173
174 (defsubst mm-enable-multibyte ()
175   "Enable multibyte in the current buffer."
176   (when (and (fboundp 'set-buffer-multibyte)
177              (default-value 'enable-multibyte-characters))
178     (set-buffer-multibyte t)))
179
180 (defsubst mm-disable-multibyte ()
181   "Disable multibyte in the current buffer."
182   (when (fboundp 'set-buffer-multibyte)
183     (set-buffer-multibyte nil)))
184
185 (defun mm-mime-charset (charset)
186   "Return the MIME charset corresponding to the MULE CHARSET."
187   (if (fboundp 'coding-system-get)
188       ;; This exists in Emacs 20.
189       (or
190        (and (get-charset-property charset 'prefered-coding-system)
191             (coding-system-get
192              (get-charset-property charset 'prefered-coding-system)
193              'mime-charset))
194        (and (eq charset 'ascii)
195             'us-ascii)
196        (get-charset-property charset 'prefered-coding-system)
197        (mm-mule-charset-to-mime-charset charset))
198     ;; This is for XEmacs.
199     (mm-mule-charset-to-mime-charset charset)))
200
201 (defun mm-find-mime-charset-region (b e)
202   "Return the MIME charsets needed to encode the region between B and E."
203   (let ((charsets
204          (mapcar 'mm-mime-charset
205                  (delq 'ascii
206                        (mm-find-charset-region b e)))))
207     (when (memq 'iso-2022-jp-2 charsets)
208       (setq charsets (delq 'iso-2022-jp charsets)))
209     (delete-duplicates charsets)))
210
211 (defsubst mm-multibyte-p ()
212   "Say whether multibyte is enabled."
213   (and (boundp 'enable-multibyte-characters)
214        enable-multibyte-characters))
215
216 (defmacro mm-with-unibyte-buffer (&rest forms)
217   "Create a temporary buffer, and evaluate FORMS there like `progn'.
218 See also `with-temp-file' and `with-output-to-string'."
219   (let ((temp-buffer (make-symbol "temp-buffer"))
220         (multibyte (make-symbol "multibyte")))
221     `(if (not (boundp 'enable-multibyte-characters))
222          (with-temp-buffer ,@forms)
223        (let ((,multibyte (default-value 'enable-multibyte-characters))
224              ,temp-buffer)
225          (unwind-protect
226              (progn
227                (setq-default enable-multibyte-characters nil)
228                (setq ,temp-buffer
229                      (get-buffer-create (generate-new-buffer-name " *temp*")))
230                (unwind-protect
231                    (with-current-buffer ,temp-buffer
232                      (let ((buffer-file-coding-system mm-binary-coding-system)
233                            (coding-system-for-read mm-binary-coding-system)
234                            (coding-system-for-write mm-binary-coding-system))
235                        ,@forms))
236                  (and (buffer-name ,temp-buffer)
237                       (kill-buffer ,temp-buffer))))
238            (setq-default enable-multibyte-characters ,multibyte))))))
239 (put 'mm-with-unibyte-buffer 'lisp-indent-function 0)
240 (put 'mm-with-unibyte-buffer 'edebug-form-spec '(body))
241
242 (defun mm-find-charset-region (b e)
243   "Return a list of charsets in the region."
244   (cond
245    ((and (boundp 'enable-multibyte-characters)
246          enable-multibyte-characters
247          (fboundp 'find-charset-region))
248     (find-charset-region b e))
249    ((not (boundp 'current-language-environment))
250     (save-excursion
251       (save-restriction
252         (narrow-to-region b e)
253         (goto-char (point-min))
254         (skip-chars-forward "\0-\177")
255         (if (eobp)
256             '(ascii)
257           (delq nil (list 'ascii mail-parse-charset))))))
258    (t
259     ;; We are in a unibyte buffer, so we futz around a bit.
260     (save-excursion
261       (save-restriction
262         (narrow-to-region b e)
263         (goto-char (point-min))
264         (let ((entry (assoc (capitalize current-language-environment)
265                             language-info-alist)))
266           (skip-chars-forward "\0-\177")
267           (if (eobp)
268               '(ascii)
269             (list 'ascii (car (last (assq 'charset entry)))))))))))
270
271 (defun mm-read-charset (prompt)
272   "Return a charset."
273   (intern
274    (completing-read
275     prompt
276     (mapcar (lambda (e) (list (symbol-name (car e))))
277             mm-mime-mule-charset-alist)
278     nil t)))
279
280 (defun mm-quote-arg (arg)
281   "Return a version of ARG that is safe to evaluate in a shell."
282   (let ((pos 0) new-pos accum)
283     ;; *** bug: we don't handle newline characters properly
284     (while (setq new-pos (string-match "[]*[;!'`\"$\\& \t{} |()<>]" arg pos))
285       (push (substring arg pos new-pos) accum)
286       (push "\\" accum)
287       (push (list (aref arg new-pos)) accum)
288       (setq pos (1+ new-pos)))
289     (if (= pos 0)
290         arg
291       (apply 'concat (nconc (nreverse accum) (list (substring arg pos)))))))
292
293 (defun mm-auto-mode-alist ()
294   "Return an `auto-mode-alist' with only the .gz (etc) thingies."
295   (let ((alist auto-mode-alist)
296         out)
297     (while alist
298       (when (listp (cdar alist))
299         (push (car alist) out))
300       (pop alist))
301     (nreverse out)))
302
303 (defun mm-insert-file-contents (filename &optional visit beg end replace)
304   "Like `insert-file-contents', q.v., but only reads in the file.
305 A buffer may be modified in several ways after reading into the buffer due
306 to advanced Emacs features, such as file-name-handlers, format decoding,
307 find-file-hooks, etc.
308   This function ensures that none of these modifications will take place."
309   (let ((format-alist nil)
310         (auto-mode-alist (mm-auto-mode-alist))
311         (default-major-mode 'fundamental-mode)
312         (enable-local-variables nil)
313         (after-insert-file-functions nil)
314         (enable-local-eval nil)
315         (find-file-hooks nil))
316     (insert-file-contents filename visit beg end replace)))
317
318 (provide 'mm-util)
319
320 ;;; mm-util.el ends here