Importing Pterodactyl Gnus v0.93.
[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 (defconst mm-auto-save-coding-system
122   (cond 
123    ((memq 'emacs-mule (mm-get-coding-system-list))
124     (if (memq system-type '(windows-nt ms-dos ms-windows))
125         'emacs-mule-dos 'emacs-mule))
126    ((memq 'escape-quoted (mm-get-coding-system-list))
127     'escape-quoted)
128    ((memq 'no-conversion (mm-get-coding-system-list))
129     'no-conversion)
130    (t nil))
131   "Coding system of auto save file.")
132
133 ;;; Internal variables:
134
135 ;;; Functions:
136
137 (defun mm-mule-charset-to-mime-charset (charset)
138   "Return the MIME charset corresponding to MULE CHARSET."
139   (let ((alist mm-mime-mule-charset-alist)
140         out)
141     (while alist
142       (when (memq charset (cdar alist))
143         (setq out (caar alist)
144               alist nil))
145       (pop alist))
146     out))
147
148 (defun mm-charset-to-coding-system (charset &optional lbt)
149   "Return coding-system corresponding to CHARSET.
150 CHARSET is a symbol naming a MIME charset.
151 If optional argument LBT (`unix', `dos' or `mac') is specified, it is
152 used as the line break code type of the coding system."
153   (when (stringp charset)
154     (setq charset (intern (downcase charset))))
155   (setq charset
156         (or (cdr (assq charset mm-charset-synonym-alist))
157             charset))
158   (when lbt
159     (setq charset (intern (format "%s-%s" charset lbt))))
160   (cond
161    ;; Running in a non-MULE environment.
162    ((null (mm-get-coding-system-list))
163     charset)
164    ;; ascii
165    ((eq charset 'us-ascii)
166     'ascii)
167    ;; Check to see whether we can handle this charset.
168    ((memq charset (mm-get-coding-system-list))
169     charset)
170    ;; Nope.
171    (t
172     nil)))
173
174 (defun mm-replace-chars-in-string (string from to)
175   "Replace characters in STRING from FROM to TO."
176   (let ((string (substring string 0))   ;Copy string.
177         (len (length string))
178         (idx 0))
179     ;; Replace all occurrences of FROM with TO.
180     (while (< idx len)
181       (when (= (aref string idx) from)
182         (aset string idx to))
183       (setq idx (1+ idx)))
184     string))
185
186 (defsubst mm-enable-multibyte ()
187   "Enable multibyte in the current buffer."
188   (when (and (fboundp 'set-buffer-multibyte)
189              (default-value 'enable-multibyte-characters))
190     (set-buffer-multibyte t)))
191
192 (defsubst mm-disable-multibyte ()
193   "Disable multibyte in the current buffer."
194   (when (fboundp 'set-buffer-multibyte)
195     (set-buffer-multibyte nil)))
196
197 (defun mm-preferred-coding-system (charset)
198   ;; A typo in some Emacs versions.
199   (or (get-charset-property charset 'prefered-coding-system)
200       (get-charset-property charset 'preffered-coding-system)))
201
202 (defun mm-mime-charset (charset)
203   "Return the MIME charset corresponding to the MULE CHARSET."
204   (if (fboundp 'coding-system-get)
205       ;; This exists in Emacs 20.
206       (or
207        (and (mm-preferred-coding-system charset)
208             (coding-system-get
209              (mm-preferred-coding-system charset) 'mime-charset))
210        (and (eq charset 'ascii)
211             'us-ascii)
212        (mm-preferred-coding-system charset)
213        (mm-mule-charset-to-mime-charset charset))
214     ;; This is for XEmacs.
215     (mm-mule-charset-to-mime-charset charset)))
216
217 (defun mm-find-mime-charset-region (b e)
218   "Return the MIME charsets needed to encode the region between B and E."
219   (let ((charsets
220          (mapcar 'mm-mime-charset
221                  (delq 'ascii
222                        (mm-find-charset-region b e)))))
223     (when (memq 'iso-2022-jp-2 charsets)
224       (setq charsets (delq 'iso-2022-jp charsets)))
225     (delete-duplicates charsets)))
226
227 (defsubst mm-multibyte-p ()
228   "Say whether multibyte is enabled."
229   (and (boundp 'enable-multibyte-characters)
230        enable-multibyte-characters))
231
232 (defmacro mm-with-unibyte-buffer (&rest forms)
233   "Create a temporary buffer, and evaluate FORMS there like `progn'.
234 See also `with-temp-file' and `with-output-to-string'."
235   (let ((temp-buffer (make-symbol "temp-buffer"))
236         (multibyte (make-symbol "multibyte")))
237     `(if (not (boundp 'enable-multibyte-characters))
238          (with-temp-buffer ,@forms)
239        (let ((,multibyte (default-value 'enable-multibyte-characters))
240              ,temp-buffer)
241          (unwind-protect
242              (progn
243                (setq-default enable-multibyte-characters nil)
244                (setq ,temp-buffer
245                      (get-buffer-create (generate-new-buffer-name " *temp*")))
246                (unwind-protect
247                    (with-current-buffer ,temp-buffer
248                      (let ((buffer-file-coding-system mm-binary-coding-system)
249                            (coding-system-for-read mm-binary-coding-system)
250                            (coding-system-for-write mm-binary-coding-system))
251                        ,@forms))
252                  (and (buffer-name ,temp-buffer)
253                       (kill-buffer ,temp-buffer))))
254            (setq-default enable-multibyte-characters ,multibyte))))))
255 (put 'mm-with-unibyte-buffer 'lisp-indent-function 0)
256 (put 'mm-with-unibyte-buffer 'edebug-form-spec '(body))
257
258 (defun mm-find-charset-region (b e)
259   "Return a list of charsets in the region."
260   (cond
261    ((and (boundp 'enable-multibyte-characters)
262          enable-multibyte-characters
263          (fboundp 'find-charset-region))
264     (find-charset-region b e))
265    ((not (boundp 'current-language-environment))
266     (save-excursion
267       (save-restriction
268         (narrow-to-region b e)
269         (goto-char (point-min))
270         (skip-chars-forward "\0-\177")
271         (if (eobp)
272             '(ascii)
273           (delq nil (list 'ascii mail-parse-charset))))))
274    (t
275     ;; We are in a unibyte buffer, so we futz around a bit.
276     (save-excursion
277       (save-restriction
278         (narrow-to-region b e)
279         (goto-char (point-min))
280         (let ((entry (assoc (capitalize current-language-environment)
281                             language-info-alist)))
282           (skip-chars-forward "\0-\177")
283           (if (eobp)
284               '(ascii)
285             (list 'ascii (car (last (assq 'charset entry)))))))))))
286
287 (defun mm-read-charset (prompt)
288   "Return a charset."
289   (intern
290    (completing-read
291     prompt
292     (mapcar (lambda (e) (list (symbol-name (car e))))
293             mm-mime-mule-charset-alist)
294     nil t)))
295
296 (defun mm-quote-arg (arg)
297   "Return a version of ARG that is safe to evaluate in a shell."
298   (let ((pos 0) new-pos accum)
299     ;; *** bug: we don't handle newline characters properly
300     (while (setq new-pos (string-match "[]*[;!'`\"$\\& \t{} |()<>]" arg pos))
301       (push (substring arg pos new-pos) accum)
302       (push "\\" accum)
303       (push (list (aref arg new-pos)) accum)
304       (setq pos (1+ new-pos)))
305     (if (= pos 0)
306         arg
307       (apply 'concat (nconc (nreverse accum) (list (substring arg pos)))))))
308
309 (defun mm-auto-mode-alist ()
310   "Return an `auto-mode-alist' with only the .gz (etc) thingies."
311   (let ((alist auto-mode-alist)
312         out)
313     (while alist
314       (when (listp (cdar alist))
315         (push (car alist) out))
316       (pop alist))
317     (nreverse out)))
318
319 (defun mm-insert-file-contents (filename &optional visit beg end replace)
320   "Like `insert-file-contents', q.v., but only reads in the file.
321 A buffer may be modified in several ways after reading into the buffer due
322 to advanced Emacs features, such as file-name-handlers, format decoding,
323 find-file-hooks, etc.
324   This function ensures that none of these modifications will take place."
325   (let ((format-alist nil)
326         (auto-mode-alist (mm-auto-mode-alist))
327         (default-major-mode 'fundamental-mode)
328         (enable-local-variables nil)
329         (after-insert-file-functions nil)
330         (enable-local-eval nil)
331         (find-file-hooks nil))
332     (insert-file-contents filename visit beg end replace)))
333
334 (provide 'mm-util)
335
336 ;;; mm-util.el ends here