8e625c936e464c737838a4ed7ea2352387004769
[gnus] / lisp / mm-util.el
1 ;;; mm-util.el --- Utility functions for Mule and low level things
2
3 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 ;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
6 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;;      MORIOKA Tomohiko <morioka@jaist.ac.jp>
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 3, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 ;; Boston, MA 02110-1301, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 ;; For Emacs < 22.2.
30 (eval-and-compile
31   (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
32
33 (eval-when-compile (require 'cl))
34 (require 'mail-prsvr)
35
36 (eval-and-compile
37   (if (featurep 'xemacs)
38       (unless (ignore-errors
39                 (require 'timer-funcs))
40         (require 'timer))
41     (require 'timer)))
42
43 (defvar mm-mime-mule-charset-alist )
44
45 (eval-and-compile
46   (mapc
47    (lambda (elem)
48      (let ((nfunc (intern (format "mm-%s" (car elem)))))
49        (if (fboundp (car elem))
50            (defalias nfunc (car elem))
51          (defalias nfunc (cdr elem)))))
52    '((coding-system-list . ignore)
53      (char-int . identity)
54      (coding-system-equal . equal)
55      (annotationp . ignore)
56      (set-buffer-file-coding-system . ignore)
57      (read-charset
58       . (lambda (prompt)
59           "Return a charset."
60           (intern
61            (completing-read
62             prompt
63             (mapcar (lambda (e) (list (symbol-name (car e))))
64                     mm-mime-mule-charset-alist)
65             nil t))))
66      (subst-char-in-string
67       . (lambda (from to string &optional inplace)
68           ;; stolen (and renamed) from nnheader.el
69           "Replace characters in STRING from FROM to TO.
70           Unless optional argument INPLACE is non-nil, return a new string."
71           (let ((string (if inplace string (copy-sequence string)))
72                 (len (length string))
73                 (idx 0))
74             ;; Replace all occurrences of FROM with TO.
75             (while (< idx len)
76               (when (= (aref string idx) from)
77                 (aset string idx to))
78               (setq idx (1+ idx)))
79             string)))
80      (replace-in-string
81       . (lambda (string regexp rep &optional literal)
82           "See `replace-regexp-in-string', only the order of args differs."
83           (replace-regexp-in-string regexp rep string nil literal)))
84      (string-as-unibyte . identity)
85      (string-make-unibyte . identity)
86      ;; string-as-multibyte often doesn't really do what you think it does.
87      ;; Example:
88      ;;    (aref (string-as-multibyte "\201") 0) -> 129 (aka ?\201)
89      ;;    (aref (string-as-multibyte "\300") 0) -> 192 (aka ?\300)
90      ;;    (aref (string-as-multibyte "\300\201") 0) -> 192 (aka ?\300)
91      ;;    (aref (string-as-multibyte "\300\201") 1) -> 129 (aka ?\201)
92      ;; but
93      ;;    (aref (string-as-multibyte "\201\300") 0) -> 2240
94      ;;    (aref (string-as-multibyte "\201\300") 1) -> <error>
95      ;; Better use string-to-multibyte or encode-coding-string.
96      ;; If you really need string-as-multibyte somewhere it's usually
97      ;; because you're using the internal emacs-mule representation (maybe
98      ;; because you're using string-as-unibyte somewhere), which is
99      ;; generally a problem in itself.
100      ;; Here is an approximate equivalence table to help think about it:
101      ;; (string-as-multibyte s)   ~= (decode-coding-string s 'emacs-mule)
102      ;; (string-to-multibyte s)   ~= (decode-coding-string s 'binary)
103      ;; (string-make-multibyte s) ~= (decode-coding-string s locale-coding-system)
104      (string-as-multibyte . identity)
105      (multibyte-string-p . ignore)
106      (insert-byte . insert-char)
107      (multibyte-char-to-unibyte . identity)
108      (special-display-p
109       . (lambda (buffer-name)
110           "Returns non-nil if a buffer named BUFFER-NAME gets a special frame."
111           (and special-display-function
112                (or (and (member buffer-name special-display-buffer-names) t)
113                    (cdr (assoc buffer-name special-display-buffer-names))
114                    (catch 'return
115                      (dolist (elem special-display-regexps)
116                        (and (stringp elem)
117                             (string-match elem buffer-name)
118                             (throw 'return t))
119                        (and (consp elem)
120                             (stringp (car elem))
121                             (string-match (car elem) buffer-name)
122                             (throw 'return (cdr elem))))))))))))
123
124 (eval-and-compile
125   (if (featurep 'xemacs)
126       (if (featurep 'file-coding)
127           ;; Don't modify string if CODING-SYSTEM is nil.
128           (progn
129             (defun mm-decode-coding-string (str coding-system)
130               (if coding-system
131                   (decode-coding-string str coding-system)
132                 str))
133             (defun mm-encode-coding-string (str coding-system)
134               (if coding-system
135                   (encode-coding-string str coding-system)
136                 str))
137             (defun mm-decode-coding-region (start end coding-system)
138               (if coding-system
139                   (decode-coding-region start end coding-system)))
140             (defun mm-encode-coding-region (start end coding-system)
141               (if coding-system
142                   (encode-coding-region start end coding-system))))
143         (defun mm-decode-coding-string (str coding-system) str)
144         (defun mm-encode-coding-string (str coding-system) str)
145         (defalias 'mm-decode-coding-region 'ignore)
146         (defalias 'mm-encode-coding-region 'ignore))
147     (defalias 'mm-decode-coding-string 'decode-coding-string)
148     (defalias 'mm-encode-coding-string 'encode-coding-string)
149     (defalias 'mm-decode-coding-region 'decode-coding-region)
150     (defalias 'mm-encode-coding-region 'encode-coding-region)))
151
152 (defalias 'mm-string-to-multibyte
153   (cond
154    ((featurep 'xemacs)
155     'identity)
156    ((fboundp 'string-to-multibyte)
157     'string-to-multibyte)
158    (t
159     (lambda (string)
160       "Return a multibyte string with the same individual chars as string."
161       (mapconcat
162        (lambda (ch) (mm-string-as-multibyte (char-to-string ch)))
163        string "")))))
164
165 (eval-and-compile
166   (defalias 'mm-char-or-char-int-p
167     (cond
168      ((fboundp 'char-or-char-int-p) 'char-or-char-int-p)
169      ((fboundp 'char-valid-p) 'char-valid-p)
170      (t 'identity))))
171
172 ;; Fixme:  This seems always to be used to read a MIME charset, so it
173 ;; should be re-named and fixed (in Emacs) to offer completion only on
174 ;; proper charset names (base coding systems which have a
175 ;; mime-charset defined).  XEmacs doesn't believe in mime-charset;
176 ;; test with
177 ;;   `(or (coding-system-get 'iso-8859-1 'mime-charset)
178 ;;        (coding-system-get 'iso-8859-1 :mime-charset))'
179 ;; Actually, there should be an `mm-coding-system-mime-charset'.
180 (eval-and-compile
181   (defalias 'mm-read-coding-system
182     (cond
183      ((fboundp 'read-coding-system)
184       (if (and (featurep 'xemacs)
185                (<= (string-to-number emacs-version) 21.1))
186           (lambda (prompt &optional default-coding-system)
187             (read-coding-system prompt))
188         'read-coding-system))
189      (t (lambda (prompt &optional default-coding-system)
190           "Prompt the user for a coding system."
191           (completing-read
192            prompt (mapcar (lambda (s) (list (symbol-name (car s))))
193                           mm-mime-mule-charset-alist)))))))
194
195 (defvar mm-coding-system-list nil)
196 (defun mm-get-coding-system-list ()
197   "Get the coding system list."
198   (or mm-coding-system-list
199       (setq mm-coding-system-list (mm-coding-system-list))))
200
201 (defun mm-coding-system-p (cs)
202   "Return non-nil if CS is a symbol naming a coding system.
203 In XEmacs, also return non-nil if CS is a coding system object.
204 If CS is available, return CS itself in Emacs, and return a coding
205 system object in XEmacs."
206   (if (fboundp 'find-coding-system)
207       (and cs (find-coding-system cs))
208     (if (fboundp 'coding-system-p)
209         (when (coding-system-p cs)
210           cs)
211       ;; no-MULE XEmacs:
212       (car (memq cs (mm-get-coding-system-list))))))
213
214 (defun mm-codepage-setup (number &optional alias)
215   "Create a coding system cpNUMBER.
216 The coding system is created using `codepage-setup'.  If ALIAS is
217 non-nil, an alias is created and added to
218 `mm-charset-synonym-alist'.  If ALIAS is a string, it's used as
219 the alias.  Else windows-NUMBER is used."
220   (interactive
221    (let ((completion-ignore-case t)
222          (candidates (if (fboundp 'cp-supported-codepages)
223                          (cp-supported-codepages)
224                        ;; Removed in Emacs 23 (unicode), sosignal an error:
225                        (error "`codepage-setup' is obsolete in this Emacs version."))))
226      (list (completing-read "Setup DOS Codepage: (default 437) " candidates
227                             nil t nil nil "437"))))
228   (when alias
229     (setq alias (if (stringp alias)
230                     (intern alias)
231                   (intern (format "windows-%s" number)))))
232   (let* ((cp (intern (format "cp%s" number))))
233     (unless (mm-coding-system-p cp)
234       (codepage-setup number))
235     (when (and alias
236                ;; Don't add alias if setup of cp failed.
237                (mm-coding-system-p cp))
238       (add-to-list 'mm-charset-synonym-alist (cons alias cp)))))
239
240 (defvar mm-charset-synonym-alist
241   `(
242     ;; Not in XEmacs, but it's not a proper MIME charset anyhow.
243     ,@(unless (mm-coding-system-p 'x-ctext)
244         '((x-ctext . ctext)))
245     ;; ISO-8859-15 is very similar to ISO-8859-1.  But it's _different_ in 8
246     ;; positions!
247     ,@(unless (mm-coding-system-p 'iso-8859-15)
248         '((iso-8859-15 . iso-8859-1)))
249     ;; BIG-5HKSCS is similar to, but different than, BIG-5.
250     ,@(unless (mm-coding-system-p 'big5-hkscs)
251         '((big5-hkscs . big5)))
252     ;; A Microsoft misunderstanding.
253     ,@(when (and (not (mm-coding-system-p 'unicode))
254                  (mm-coding-system-p 'utf-16-le))
255         '((unicode . utf-16-le)))
256     ;; A Microsoft misunderstanding.
257     ,@(unless (mm-coding-system-p 'ks_c_5601-1987)
258         (if (mm-coding-system-p 'cp949)
259             '((ks_c_5601-1987 . cp949))
260           '((ks_c_5601-1987 . euc-kr))))
261     ;; Windows-31J is Windows Codepage 932.
262     ,@(when (and (not (mm-coding-system-p 'windows-31j))
263                  (mm-coding-system-p 'cp932))
264         '((windows-31j . cp932)))
265     ;; Charset name: GBK, Charset aliases: CP936, MS936, windows-936
266     ;; http://www.iana.org/assignments/charset-reg/GBK
267     ;; Emacs 22.1 has cp936, but not gbk, so we alias it:
268     ,@(when (and (not (mm-coding-system-p 'gbk))
269                  (mm-coding-system-p 'cp936))
270         '((gbk . cp936)))
271     ;; ISO8859-1 is a bogus name for ISO-8859-1
272     ,@(when (and (not (mm-coding-system-p 'iso8859-1))
273                  (mm-coding-system-p 'iso-8859-1))
274         '((iso8859-1 . iso-8859-1)))
275     )
276   "A mapping from unknown or invalid charset names to the real charset names.
277
278 See `mm-codepage-iso-8859-list' and `mm-codepage-ibm-list'.")
279
280 (defcustom mm-codepage-iso-8859-list
281   (list 1250 ;; Windows-1250 is a variant of Latin-2 heavily used by Microsoft
282         ;; Outlook users in Czech republic.  Use this to allow reading of
283         ;; their e-mails.  cp1250 should be defined by M-x codepage-setup
284         ;; (Emacs 21).
285         '(1252 . 1) ;; Windows-1252 is a superset of iso-8859-1 (West
286                     ;; Europe).  See also `gnus-article-dumbquotes-map'.
287         '(1254 . 9) ;; Windows-1254 is a superset of iso-8859-9 (Turkish).
288         '(1255 . 8));; Windows-1255 is a superset of iso-8859-8 (Hebrew).
289   "A list of Windows codepage numbers and iso-8859 charset numbers.
290
291 If an element is a number corresponding to a supported windows
292 codepage, appropriate entries to `mm-charset-synonym-alist' are
293 added by `mm-setup-codepage-iso-8859'.  An element may also be a
294 cons cell where the car is a codepage number and the cdr is the
295 corresponding number of an iso-8859 charset."
296   :type '(list (set :inline t
297                     (const 1250 :tag "Central and East European")
298                     (const (1252 . 1) :tag "West European")
299                     (const (1254 . 9) :tag "Turkish")
300                     (const (1255 . 8) :tag "Hebrew"))
301                (repeat :inline t
302                        :tag "Other options"
303                        (choice
304                         (integer :tag "Windows codepage number")
305                         (cons (integer :tag "Windows codepage number")
306                               (integer :tag "iso-8859 charset  number")))))
307   :version "22.1" ;; Gnus 5.10.9
308   :group 'mime)
309
310 (defcustom mm-codepage-ibm-list
311   (list 437 ;; (US etc.)
312         860 ;; (Portugal)
313         861 ;; (Iceland)
314         862 ;; (Israel)
315         863 ;; (Canadian French)
316         865 ;; (Nordic)
317         852 ;;
318         850 ;; (Latin 1)
319         855 ;; (Cyrillic)
320         866 ;; (Cyrillic - Russian)
321         857 ;; (Turkish)
322         864 ;; (Arabic)
323         869 ;; (Greek)
324         874);; (Thai)
325   ;; In Emacs 23 (unicode), cp... and ibm... are aliases.
326   ;; Cf. http://thread.gmane.org/v9lkng5nwy.fsf@marauder.physik.uni-ulm.de
327   "List of IBM codepage numbers.
328
329 The codepage mappings slighly differ between IBM and other vendors.
330 See \"ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/IBM/README.TXT\".
331
332 If an element is a number corresponding to a supported windows
333 codepage, appropriate entries to `mm-charset-synonym-alist' are
334 added by `mm-setup-codepage-ibm'."
335   :type '(list (set :inline t
336                     (const 437 :tag "US etc.")
337                     (const 860 :tag "Portugal")
338                     (const 861 :tag "Iceland")
339                     (const 862 :tag "Israel")
340                     (const 863 :tag "Canadian French")
341                     (const 865 :tag "Nordic")
342                     (const 852)
343                     (const 850 :tag "Latin 1")
344                     (const 855 :tag "Cyrillic")
345                     (const 866 :tag "Cyrillic - Russian")
346                     (const 857 :tag "Turkish")
347                     (const 864 :tag "Arabic")
348                     (const 869 :tag "Greek")
349                     (const 874 :tag "Thai"))
350                (repeat :inline t
351                        :tag "Other options"
352                        (integer :tag "Codepage number")))
353   :version "22.1" ;; Gnus 5.10.9
354   :group 'mime)
355
356 (defun mm-setup-codepage-iso-8859 (&optional list)
357   "Add appropriate entries to `mm-charset-synonym-alist'.
358 Unless LIST is given, `mm-codepage-iso-8859-list' is used."
359   (unless list
360     (setq list mm-codepage-iso-8859-list))
361   (dolist (i list)
362     (let (cp windows iso)
363       (if (consp i)
364           (setq cp (intern (format "cp%d" (car i)))
365                 windows (intern (format "windows-%d" (car i)))
366                 iso (intern (format "iso-8859-%d" (cdr i))))
367         (setq cp (intern (format "cp%d" i))
368               windows (intern (format "windows-%d" i))))
369       (unless (mm-coding-system-p windows)
370         (if (mm-coding-system-p cp)
371             (add-to-list 'mm-charset-synonym-alist (cons windows cp))
372           (add-to-list 'mm-charset-synonym-alist (cons windows iso)))))))
373
374 (defun mm-setup-codepage-ibm (&optional list)
375   "Add appropriate entries to `mm-charset-synonym-alist'.
376 Unless LIST is given, `mm-codepage-ibm-list' is used."
377   (unless list
378     (setq list mm-codepage-ibm-list))
379   (dolist (number list)
380     (let ((ibm (intern (format "ibm%d" number)))
381           (cp  (intern (format "cp%d" number))))
382       (when (and (not (mm-coding-system-p ibm))
383                  (mm-coding-system-p cp))
384         (add-to-list 'mm-charset-synonym-alist (cons ibm cp))))))
385
386 ;; Initialize:
387 (mm-setup-codepage-iso-8859)
388 (mm-setup-codepage-ibm)
389
390 (defcustom mm-charset-override-alist
391   '((iso-8859-1 . windows-1252)
392     (iso-8859-8 . windows-1255)
393     (iso-8859-9 . windows-1254))
394   "A mapping from undesired charset names to their replacement.
395
396 You may add pairs like (iso-8859-1 . windows-1252) here,
397 i.e. treat iso-8859-1 as windows-1252.  windows-1252 is a
398 superset of iso-8859-1."
399   :type '(list (set :inline t
400                     (const (iso-8859-1 . windows-1252))
401                     (const (iso-8859-8 . windows-1255))
402                     (const (iso-8859-9 . windows-1254))
403                     (const (undecided  . windows-1252)))
404                (repeat :inline t
405                        :tag "Other options"
406                        (cons (symbol :tag "From charset")
407                              (symbol :tag "To charset"))))
408   :version "22.1" ;; Gnus 5.10.9
409   :group 'mime)
410
411 (defcustom mm-charset-eval-alist
412   (if (featurep 'xemacs)
413       nil ;; I don't know what would be useful for XEmacs.
414     '(;; Emacs 21 offers 1250 1251 1253 1257.  Emacs 22 provides autoloads for
415       ;; 1250-1258 (i.e. `mm-codepage-setup' does nothing).
416       (windows-1250 . (mm-codepage-setup 1250 t))
417       (windows-1251 . (mm-codepage-setup 1251 t))
418       (windows-1253 . (mm-codepage-setup 1253 t))
419       (windows-1257 . (mm-codepage-setup 1257 t))))
420   "An alist of (CHARSET . FORM) pairs.
421 If an article is encoded in an unknown CHARSET, FORM is
422 evaluated.  This allows to load additional libraries providing
423 charsets on demand.  If supported by your Emacs version, you
424 could use `autoload-coding-system' here."
425   :version "22.1" ;; Gnus 5.10.9
426   :type '(list (set :inline t
427                     (const (windows-1250 . (mm-codepage-setup 1250 t)))
428                     (const (windows-1251 . (mm-codepage-setup 1251 t)))
429                     (const (windows-1253 . (mm-codepage-setup 1253 t)))
430                     (const (windows-1257 . (mm-codepage-setup 1257 t)))
431                     (const (cp850 . (mm-codepage-setup 850 nil))))
432                (repeat :inline t
433                        :tag "Other options"
434                        (cons (symbol :tag "charset")
435                              (symbol :tag "form"))))
436   :group 'mime)
437 (put 'mm-charset-eval-alist 'risky-local-variable t)
438
439 (defvar mm-binary-coding-system
440   (cond
441    ((mm-coding-system-p 'binary) 'binary)
442    ((mm-coding-system-p 'no-conversion) 'no-conversion)
443    (t nil))
444   "100% binary coding system.")
445
446 (defvar mm-text-coding-system
447   (or (if (memq system-type '(windows-nt ms-dos ms-windows))
448           (and (mm-coding-system-p 'raw-text-dos) 'raw-text-dos)
449         (and (mm-coding-system-p 'raw-text) 'raw-text))
450       mm-binary-coding-system)
451   "Text-safe coding system (For removing ^M).")
452
453 (defvar mm-text-coding-system-for-write nil
454   "Text coding system for write.")
455
456 (defvar mm-auto-save-coding-system
457   (cond
458    ((mm-coding-system-p 'utf-8-emacs)   ; Mule 7
459     (if (memq system-type '(windows-nt ms-dos ms-windows))
460         (if (mm-coding-system-p 'utf-8-emacs-dos)
461             'utf-8-emacs-dos mm-binary-coding-system)
462       'utf-8-emacs))
463    ((mm-coding-system-p 'emacs-mule)
464     (if (memq system-type '(windows-nt ms-dos ms-windows))
465         (if (mm-coding-system-p 'emacs-mule-dos)
466             'emacs-mule-dos mm-binary-coding-system)
467       'emacs-mule))
468    ((mm-coding-system-p 'escape-quoted) 'escape-quoted)
469    (t mm-binary-coding-system))
470   "Coding system of auto save file.")
471
472 (defvar mm-universal-coding-system mm-auto-save-coding-system
473   "The universal coding system.")
474
475 ;; Fixme: some of the cars here aren't valid MIME charsets.  That
476 ;; should only matter with XEmacs, though.
477 (defvar mm-mime-mule-charset-alist
478   `((us-ascii ascii)
479     (iso-8859-1 latin-iso8859-1)
480     (iso-8859-2 latin-iso8859-2)
481     (iso-8859-3 latin-iso8859-3)
482     (iso-8859-4 latin-iso8859-4)
483     (iso-8859-5 cyrillic-iso8859-5)
484     ;; Non-mule (X)Emacs uses the last mule-charset for 8bit characters.
485     ;; The fake mule-charset, gnus-koi8-r, tells Gnus that the default
486     ;; charset is koi8-r, not iso-8859-5.
487     (koi8-r cyrillic-iso8859-5 gnus-koi8-r)
488     (iso-8859-6 arabic-iso8859-6)
489     (iso-8859-7 greek-iso8859-7)
490     (iso-8859-8 hebrew-iso8859-8)
491     (iso-8859-9 latin-iso8859-9)
492     (iso-8859-14 latin-iso8859-14)
493     (iso-8859-15 latin-iso8859-15)
494     (viscii vietnamese-viscii-lower)
495     (iso-2022-jp latin-jisx0201 japanese-jisx0208 japanese-jisx0208-1978)
496     (euc-kr korean-ksc5601)
497     (gb2312 chinese-gb2312)
498     (gbk chinese-gbk)
499     (gb18030 gb18030-2-byte
500              gb18030-4-byte-bmp gb18030-4-byte-smp
501              gb18030-4-byte-ext-1 gb18030-4-byte-ext-2)
502     (big5 chinese-big5-1 chinese-big5-2)
503     (tibetan tibetan)
504     (thai-tis620 thai-tis620)
505     (windows-1251 cyrillic-iso8859-5)
506     (iso-2022-7bit ethiopic arabic-1-column arabic-2-column)
507     (iso-2022-jp-2 latin-iso8859-1 greek-iso8859-7
508                    latin-jisx0201 japanese-jisx0208-1978
509                    chinese-gb2312 japanese-jisx0208
510                    korean-ksc5601 japanese-jisx0212)
511     (iso-2022-int-1 latin-iso8859-1 greek-iso8859-7
512                     latin-jisx0201 japanese-jisx0208-1978
513                     chinese-gb2312 japanese-jisx0208
514                     korean-ksc5601 japanese-jisx0212
515                     chinese-cns11643-1 chinese-cns11643-2)
516     (iso-2022-int-1 latin-iso8859-1 latin-iso8859-2
517                     cyrillic-iso8859-5 greek-iso8859-7
518                     latin-jisx0201 japanese-jisx0208-1978
519                     chinese-gb2312 japanese-jisx0208
520                     korean-ksc5601 japanese-jisx0212
521                     chinese-cns11643-1 chinese-cns11643-2
522                     chinese-cns11643-3 chinese-cns11643-4
523                     chinese-cns11643-5 chinese-cns11643-6
524                     chinese-cns11643-7)
525     (iso-2022-jp-3 latin-jisx0201 japanese-jisx0208-1978 japanese-jisx0208
526                    japanese-jisx0213-1 japanese-jisx0213-2)
527     (shift_jis latin-jisx0201 katakana-jisx0201 japanese-jisx0208)
528     ,(cond ((fboundp 'unicode-precedence-list)
529             (cons 'utf-8 (delq 'ascii (mapcar 'charset-name
530                                               (unicode-precedence-list)))))
531            ((or (not (fboundp 'charsetp)) ;; non-Mule case
532                 (charsetp 'unicode-a)
533                 (not (mm-coding-system-p 'mule-utf-8)))
534             '(utf-8 unicode-a unicode-b unicode-c unicode-d unicode-e))
535            (t ;; If we have utf-8 we're in Mule 5+.
536             (append '(utf-8)
537                     (delete 'ascii
538                             (coding-system-get 'mule-utf-8 'safe-charsets))))))
539   "Alist of MIME-charset/MULE-charsets.")
540
541 (defun mm-enrich-utf-8-by-mule-ucs ()
542   "Make the `utf-8' MIME charset usable by the Mule-UCS package.
543 This function will run when the `un-define' module is loaded under
544 XEmacs, and fill the `utf-8' entry in `mm-mime-mule-charset-alist'
545 with Mule charsets.  It is completely useless for Emacs."
546   (when (boundp 'unicode-basic-translation-charset-order-list)
547     (condition-case nil
548         (let ((val (delq
549                     'ascii
550                     (copy-sequence
551                      (symbol-value
552                       'unicode-basic-translation-charset-order-list))))
553               (elem (assq 'utf-8 mm-mime-mule-charset-alist)))
554           (if elem
555               (setcdr elem val)
556             (setq mm-mime-mule-charset-alist
557                   (nconc mm-mime-mule-charset-alist
558                          (list (cons 'utf-8 val))))))
559       (error))))
560
561 ;; Correct by construction, but should be unnecessary for Emacs:
562 (if (featurep 'xemacs)
563     (eval-after-load "un-define" '(mm-enrich-utf-8-by-mule-ucs))
564   (when (and (fboundp 'coding-system-list)
565              (fboundp 'sort-coding-systems))
566     (let ((css (sort-coding-systems (coding-system-list 'base-only)))
567           cs mime mule alist)
568       (while css
569         (setq cs (pop css)
570               mime (or (coding-system-get cs :mime-charset); Emacs 23 (unicode)
571                        (coding-system-get cs 'mime-charset)))
572         (when (and mime
573                    (not (eq t (setq mule
574                                     (coding-system-get cs 'safe-charsets))))
575                    (not (assq mime alist)))
576           (push (cons mime (delq 'ascii mule)) alist)))
577       (setq mm-mime-mule-charset-alist (nreverse alist)))))
578
579 (defvar mm-hack-charsets '(iso-8859-15 iso-2022-jp-2)
580   "A list of special charsets.
581 Valid elements include:
582 `iso-8859-15'    convert ISO-8859-1, -9 to ISO-8859-15 if ISO-8859-15 exists.
583 `iso-2022-jp-2'  convert ISO-2022-jp to ISO-2022-jp-2 if ISO-2022-jp-2 exists."
584 )
585
586 (defvar mm-iso-8859-15-compatible
587   '((iso-8859-1 "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE")
588     (iso-8859-9 "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE\xD0\xDD\xDE\xF0\xFD\xFE"))
589   "ISO-8859-15 exchangeable coding systems and inconvertible characters.")
590
591 (defvar mm-iso-8859-x-to-15-table
592   (and (fboundp 'coding-system-p)
593        (mm-coding-system-p 'iso-8859-15)
594        (mapcar
595         (lambda (cs)
596           (if (mm-coding-system-p (car cs))
597               (let ((c (string-to-char
598                         (decode-coding-string "\341" (car cs)))))
599                 (cons (char-charset c)
600                       (cons
601                        (- (string-to-char
602                            (decode-coding-string "\341" 'iso-8859-15)) c)
603                        (string-to-list (decode-coding-string (car (cdr cs))
604                                                              (car cs))))))
605             '(gnus-charset 0)))
606         mm-iso-8859-15-compatible))
607   "A table of the difference character between ISO-8859-X and ISO-8859-15.")
608
609 (defcustom mm-coding-system-priorities
610   (if (boundp 'current-language-environment)
611       (let ((lang (symbol-value 'current-language-environment)))
612         (cond ((string= lang "Japanese")
613                ;; Japanese users prefer iso-2022-jp to euc-japan or
614                ;; shift_jis, however iso-8859-1 should be used when
615                ;; there are only ASCII text and Latin-1 characters.
616                '(iso-8859-1 iso-2022-jp iso-2022-jp-2 shift_jis utf-8)))))
617   "Preferred coding systems for encoding outgoing messages.
618
619 More than one suitable coding system may be found for some text.
620 By default, the coding system with the highest priority is used
621 to encode outgoing messages (see `sort-coding-systems').  If this
622 variable is set, it overrides the default priority."
623   :version "21.2"
624   :type '(repeat (symbol :tag "Coding system"))
625   :group 'mime)
626
627 ;; ??
628 (defvar mm-use-find-coding-systems-region
629   (fboundp 'find-coding-systems-region)
630   "Use `find-coding-systems-region' to find proper coding systems.
631
632 Setting it to nil is useful on Emacsen supporting Unicode if sending
633 mail with multiple parts is preferred to sending a Unicode one.")
634
635 ;;; Internal variables:
636
637 ;;; Functions:
638
639 (defun mm-mule-charset-to-mime-charset (charset)
640   "Return the MIME charset corresponding to the given Mule CHARSET."
641   (if (and (fboundp 'find-coding-systems-for-charsets)
642            (fboundp 'sort-coding-systems))
643       (let ((css (sort (sort-coding-systems
644                         (find-coding-systems-for-charsets (list charset)))
645                        'mm-sort-coding-systems-predicate))
646             cs mime)
647         (while (and (not mime)
648                     css)
649           (when (setq cs (pop css))
650             (setq mime (or (coding-system-get cs :mime-charset)
651                            (coding-system-get cs 'mime-charset)))))
652         mime)
653     (let ((alist (mapcar (lambda (cs)
654                            (assq cs mm-mime-mule-charset-alist))
655                          (sort (mapcar 'car mm-mime-mule-charset-alist)
656                                'mm-sort-coding-systems-predicate)))
657           out)
658       (while alist
659         (when (memq charset (cdar alist))
660           (setq out (caar alist)
661                 alist nil))
662         (pop alist))
663       out)))
664
665 (defun mm-charset-to-coding-system (charset &optional lbt
666                                             allow-override)
667   "Return coding-system corresponding to CHARSET.
668 CHARSET is a symbol naming a MIME charset.
669 If optional argument LBT (`unix', `dos' or `mac') is specified, it is
670 used as the line break code type of the coding system.
671
672 If ALLOW-OVERRIDE is given, use `mm-charset-override-alist' to
673 map undesired charset names to their replacement.  This should
674 only be used for decoding, not for encoding."
675   ;; OVERRIDE is used (only) in `mm-decode-body' and `mm-decode-string'.
676   (when (stringp charset)
677     (setq charset (intern (downcase charset))))
678   (when lbt
679     (setq charset (intern (format "%s-%s" charset lbt))))
680   (cond
681    ((null charset)
682     charset)
683    ;; Running in a non-MULE environment.
684    ((or (null (mm-get-coding-system-list))
685         (not (fboundp 'coding-system-get)))
686     charset)
687    ;; Check override list quite early.  Should only used for decoding, not for
688    ;; encoding!
689    ((and allow-override
690          (let ((cs (cdr (assq charset mm-charset-override-alist))))
691            (and cs (mm-coding-system-p cs) cs))))
692    ;; ascii
693    ((eq charset 'us-ascii)
694     'ascii)
695    ;; Check to see whether we can handle this charset.  (This depends
696    ;; on there being some coding system matching each `mime-charset'
697    ;; property defined, as there should be.)
698    ((and (mm-coding-system-p charset)
699 ;;; Doing this would potentially weed out incorrect charsets.
700 ;;;      charset
701 ;;;      (eq charset (coding-system-get charset 'mime-charset))
702          )
703     charset)
704    ;; Eval expressions from `mm-charset-eval-alist'
705    ((let* ((el (assq charset mm-charset-eval-alist))
706            (cs (car el))
707            (form (cdr el)))
708       (and cs
709            form
710            (prog2
711                ;; Avoid errors...
712                (condition-case nil (eval form) (error nil))
713                ;; (message "Failed to eval `%s'" form))
714                (mm-coding-system-p cs)
715              (message "Added charset `%s' via `mm-charset-eval-alist'" cs))
716            cs)))
717    ;; Translate invalid charsets.
718    ((let ((cs (cdr (assq charset mm-charset-synonym-alist))))
719       (and cs
720            (mm-coding-system-p cs)
721            ;; (message
722            ;;  "Using synonym `%s' from `mm-charset-synonym-alist' for `%s'"
723            ;;  cs charset)
724            cs)))
725    ;; Last resort: search the coding system list for entries which
726    ;; have the right mime-charset in case the canonical name isn't
727    ;; defined (though it should be).
728    ((let (cs)
729       ;; mm-get-coding-system-list returns a list of cs without lbt.
730       ;; Do we need -lbt?
731       (dolist (c (mm-get-coding-system-list))
732         (if (and (null cs)
733                  (eq charset (or (coding-system-get c :mime-charset)
734                                  (coding-system-get c 'mime-charset))))
735             (setq cs c)))
736       (unless cs
737         ;; Warn the user about unknown charset:
738         (if (fboundp 'gnus-message)
739             (gnus-message 7 "Unknown charset: %s" charset)
740           (message "Unknown charset: %s" charset)))
741       cs))))
742
743 (eval-and-compile
744   (defvar mm-emacs-mule (and (not (featurep 'xemacs))
745                              (boundp 'default-enable-multibyte-characters)
746                              default-enable-multibyte-characters
747                              (fboundp 'set-buffer-multibyte))
748     "True in Emacs with Mule.")
749
750   (if mm-emacs-mule
751       (defun mm-enable-multibyte ()
752         "Set the multibyte flag of the current buffer.
753 Only do this if the default value of `enable-multibyte-characters' is
754 non-nil.  This is a no-op in XEmacs."
755         (set-buffer-multibyte 'to))
756     (defalias 'mm-enable-multibyte 'ignore))
757
758   (if mm-emacs-mule
759       (defun mm-disable-multibyte ()
760         "Unset the multibyte flag of in the current buffer.
761 This is a no-op in XEmacs."
762         (set-buffer-multibyte nil))
763     (defalias 'mm-disable-multibyte 'ignore)))
764
765 (defun mm-preferred-coding-system (charset)
766   ;; A typo in some Emacs versions.
767   (or (get-charset-property charset 'preferred-coding-system)
768       (get-charset-property charset 'prefered-coding-system)))
769
770 ;; Mule charsets shouldn't be used.
771 (defsubst mm-guess-charset ()
772   "Guess Mule charset from the language environment."
773   (or
774    mail-parse-mule-charset ;; cached mule-charset
775    (progn
776      (setq mail-parse-mule-charset
777            (and (boundp 'current-language-environment)
778                 (car (last
779                       (assq 'charset
780                             (assoc current-language-environment
781                                    language-info-alist))))))
782      (if (or (not mail-parse-mule-charset)
783              (eq mail-parse-mule-charset 'ascii))
784          (setq mail-parse-mule-charset
785                (or (car (last (assq mail-parse-charset
786                                     mm-mime-mule-charset-alist)))
787                    ;; default
788                    'latin-iso8859-1)))
789      mail-parse-mule-charset)))
790
791 (defun mm-charset-after (&optional pos)
792   "Return charset of a character in current buffer at position POS.
793 If POS is nil, it defauls to the current point.
794 If POS is out of range, the value is nil.
795 If the charset is `composition', return the actual one."
796   (let ((char (char-after pos)) charset)
797     (if (< (mm-char-int char) 128)
798         (setq charset 'ascii)
799       ;; charset-after is fake in some Emacsen.
800       (setq charset (and (fboundp 'char-charset) (char-charset char)))
801       (if (eq charset 'composition)     ; Mule 4
802           (let ((p (or pos (point))))
803             (cadr (find-charset-region p (1+ p))))
804         (if (and charset (not (memq charset '(ascii eight-bit-control
805                                                     eight-bit-graphic))))
806             charset
807           (mm-guess-charset))))))
808
809 (defun mm-mime-charset (charset)
810   "Return the MIME charset corresponding to the given Mule CHARSET."
811   (if (eq charset 'unknown)
812       (error "The message contains non-printable characters, please use attachment"))
813   (if (and (fboundp 'coding-system-get) (fboundp 'get-charset-property))
814       ;; This exists in Emacs 20.
815       (or
816        (and (mm-preferred-coding-system charset)
817             (or (coding-system-get
818                  (mm-preferred-coding-system charset) :mime-charset)
819                 (coding-system-get
820                  (mm-preferred-coding-system charset) 'mime-charset)))
821        (and (eq charset 'ascii)
822             'us-ascii)
823        (mm-preferred-coding-system charset)
824        (mm-mule-charset-to-mime-charset charset))
825     ;; This is for XEmacs.
826     (mm-mule-charset-to-mime-charset charset)))
827
828 (if (fboundp 'delete-dups)
829     (defalias 'mm-delete-duplicates 'delete-dups)
830   (defun mm-delete-duplicates (list)
831     "Destructively remove `equal' duplicates from LIST.
832 Store the result in LIST and return it.  LIST must be a proper list.
833 Of several `equal' occurrences of an element in LIST, the first
834 one is kept.
835
836 This is a compatibility function for Emacsen without `delete-dups'."
837     ;; Code from `subr.el' in Emacs 22:
838     (let ((tail list))
839       (while tail
840         (setcdr tail (delete (car tail) (cdr tail)))
841         (setq tail (cdr tail))))
842     list))
843
844 ;; Fixme:  This is used in places when it should be testing the
845 ;; default multibyteness.  See mm-default-multibyte-p.
846 (eval-and-compile
847   (if (and (not (featurep 'xemacs))
848            (boundp 'enable-multibyte-characters))
849       (defun mm-multibyte-p ()
850         "Non-nil if multibyte is enabled in the current buffer."
851         enable-multibyte-characters)
852     (defun mm-multibyte-p () (featurep 'mule))))
853
854 (defun mm-default-multibyte-p ()
855   "Return non-nil if the session is multibyte.
856 This affects whether coding conversion should be attempted generally."
857   (if (featurep 'mule)
858       (if (boundp 'default-enable-multibyte-characters)
859           default-enable-multibyte-characters
860         t)))
861
862 (defun mm-iso-8859-x-to-15-region (&optional b e)
863   (if (fboundp 'char-charset)
864       (let (charset item c inconvertible)
865         (save-restriction
866           (if e (narrow-to-region b e))
867           (goto-char (point-min))
868           (skip-chars-forward "\0-\177")
869           (while (not (eobp))
870             (cond
871              ((not (setq item (assq (char-charset (setq c (char-after)))
872                                     mm-iso-8859-x-to-15-table)))
873               (forward-char))
874              ((memq c (cdr (cdr item)))
875               (setq inconvertible t)
876               (forward-char))
877              (t
878               (insert-before-markers (prog1 (+ c (car (cdr item)))
879                                        (delete-char 1)))))
880             (skip-chars-forward "\0-\177")))
881         (not inconvertible))))
882
883 (defun mm-sort-coding-systems-predicate (a b)
884   (let ((priorities
885          (mapcar (lambda (cs)
886                    ;; Note: invalid entries are dropped silently
887                    (and (setq cs (mm-coding-system-p cs))
888                         (coding-system-base cs)))
889                  mm-coding-system-priorities)))
890     (and (setq a (mm-coding-system-p a))
891          (if (setq b (mm-coding-system-p b))
892              (> (length (memq (coding-system-base a) priorities))
893                 (length (memq (coding-system-base b) priorities)))
894            t))))
895
896 (eval-when-compile
897   (autoload 'latin-unity-massage-name "latin-unity")
898   (autoload 'latin-unity-maybe-remap "latin-unity")
899   (autoload 'latin-unity-representations-feasible-region "latin-unity")
900   (autoload 'latin-unity-representations-present-region "latin-unity"))
901
902 (defvar latin-unity-coding-systems)
903 (defvar latin-unity-ucs-list)
904
905 (defun mm-xemacs-find-mime-charset-1 (begin end)
906   "Determine which MIME charset to use to send region as message.
907 This uses the XEmacs-specific latin-unity package to better handle the
908 case where identical characters from diverse ISO-8859-? character sets
909 can be encoded using a single one of the corresponding coding systems.
910
911 It treats `mm-coding-system-priorities' as the list of preferred
912 coding systems; a useful example setting for this list in Western
913 Europe would be '(iso-8859-1 iso-8859-15 utf-8), which would default
914 to the very standard Latin 1 coding system, and only move to coding
915 systems that are less supported as is necessary to encode the
916 characters that exist in the buffer.
917
918 Latin Unity doesn't know about those non-ASCII Roman characters that
919 are available in various East Asian character sets.  As such, its
920 behavior if you have a JIS 0212 LATIN SMALL LETTER A WITH ACUTE in a
921 buffer and it can otherwise be encoded as Latin 1, won't be ideal.
922 But this is very much a corner case, so don't worry about it."
923   (let ((systems mm-coding-system-priorities) csets psets curset)
924
925     ;; Load the Latin Unity library, if available.
926     (when (and (not (featurep 'latin-unity)) (locate-library "latin-unity"))
927       (require 'latin-unity))
928
929     ;; Now, can we use it?
930     (if (featurep 'latin-unity)
931         (progn
932           (setq csets (latin-unity-representations-feasible-region begin end)
933                 psets (latin-unity-representations-present-region begin end))
934
935           (catch 'done
936
937             ;; Pass back the first coding system in the preferred list
938             ;; that can encode the whole region.
939             (dolist (curset systems)
940               (setq curset (latin-unity-massage-name 'buffer-default curset))
941
942               ;; If the coding system is a universal coding system, then
943               ;; it can certainly encode all the characters in the region.
944               (if (memq curset latin-unity-ucs-list)
945                   (throw 'done (list curset)))
946
947               ;; If a coding system isn't universal, and isn't in
948               ;; the list that latin unity knows about, we can't
949               ;; decide whether to use it here. Leave that until later
950               ;; in `mm-find-mime-charset-region' function, whence we
951               ;; have been called.
952               (unless (memq curset latin-unity-coding-systems)
953                 (throw 'done nil))
954
955               ;; Right, we know about this coding system, and it may
956               ;; conceivably be able to encode all the characters in
957               ;; the region.
958               (if (latin-unity-maybe-remap begin end curset csets psets t)
959                   (throw 'done (list curset))))
960
961             ;; Can't encode using anything from the
962             ;; `mm-coding-system-priorities' list.
963             ;; Leave `mm-find-mime-charset' to do most of the work.
964             nil))
965
966       ;; Right, latin unity isn't available; let `mm-find-charset-region'
967       ;; take its default action, which equally applies to GNU Emacs.
968       nil)))
969
970 (defmacro mm-xemacs-find-mime-charset (begin end)
971   (when (featurep 'xemacs)
972     `(and (featurep 'mule) (mm-xemacs-find-mime-charset-1 ,begin ,end))))
973
974 (declare-function mm-delete-duplicates "mm-util" (list))
975
976 (defun mm-find-mime-charset-region (b e &optional hack-charsets)
977   "Return the MIME charsets needed to encode the region between B and E.
978 nil means ASCII, a single-element list represents an appropriate MIME
979 charset, and a longer list means no appropriate charset."
980   (let (charsets)
981     ;; The return possibilities of this function are a mess...
982     (or (and (mm-multibyte-p)
983              mm-use-find-coding-systems-region
984              ;; Find the mime-charset of the most preferred coding
985              ;; system that has one.
986              (let ((systems (find-coding-systems-region b e)))
987                (when mm-coding-system-priorities
988                  (setq systems
989                        (sort systems 'mm-sort-coding-systems-predicate)))
990                (setq systems (delq 'compound-text systems))
991                (unless (equal systems '(undecided))
992                  (while systems
993                    (let* ((head (pop systems))
994                           (cs (or (coding-system-get head :mime-charset)
995                                   (coding-system-get head 'mime-charset))))
996                      ;; The mime-charset (`x-ctext') of
997                      ;; `compound-text' is not in the IANA list.  We
998                      ;; shouldn't normally use anything here with a
999                      ;; mime-charset having an `x-' prefix.
1000                      ;; Fixme:  Allow this to be overridden, since
1001                      ;; there is existing use of x-ctext.
1002                      ;; Also people apparently need the coding system
1003                      ;; `iso-2022-jp-3' (which Mule-UCS defines with
1004                      ;; mime-charset, though it's not valid).
1005                      (if (and cs
1006                               (not (string-match "^[Xx]-" (symbol-name cs)))
1007                               ;; UTF-16 of any variety is invalid for
1008                               ;; text parts and, unfortunately, has
1009                               ;; mime-charset defined both in Mule-UCS
1010                               ;; and versions of Emacs.  (The name
1011                               ;; might be `mule-utf-16...'  or
1012                               ;; `utf-16...'.)
1013                               (not (string-match "utf-16" (symbol-name cs))))
1014                          (setq systems nil
1015                                charsets (list cs))))))
1016                charsets))
1017         ;; If we're XEmacs, and some coding system is appropriate,
1018         ;; mm-xemacs-find-mime-charset will return an appropriate list.
1019         ;; Otherwise, we'll get nil, and the next setq will get invoked.
1020         (setq charsets (mm-xemacs-find-mime-charset b e))
1021
1022         ;; Fixme: won't work for unibyte Emacs 23:
1023
1024         ;; We're not multibyte, or a single coding system won't cover it.
1025         (setq charsets
1026               (mm-delete-duplicates
1027                (mapcar 'mm-mime-charset
1028                        (delq 'ascii
1029                              (mm-find-charset-region b e))))))
1030     (if (and (> (length charsets) 1)
1031              (memq 'iso-8859-15 charsets)
1032              (memq 'iso-8859-15 hack-charsets)
1033              (save-excursion (mm-iso-8859-x-to-15-region b e)))
1034         (dolist (x mm-iso-8859-15-compatible)
1035           (setq charsets (delq (car x) charsets))))
1036     (if (and (memq 'iso-2022-jp-2 charsets)
1037              (memq 'iso-2022-jp-2 hack-charsets))
1038         (setq charsets (delq 'iso-2022-jp charsets)))
1039     ;; Attempt to reduce the number of charsets if utf-8 is available.
1040     (if (and (featurep 'xemacs)
1041              (> (length charsets) 1)
1042              (mm-coding-system-p 'utf-8))
1043         (let ((mm-coding-system-priorities
1044                (cons 'utf-8 mm-coding-system-priorities)))
1045           (setq charsets
1046                 (mm-delete-duplicates
1047                  (mapcar 'mm-mime-charset
1048                          (delq 'ascii
1049                                (mm-find-charset-region b e)))))))
1050     charsets))
1051
1052 (defmacro mm-with-unibyte-buffer (&rest forms)
1053   "Create a temporary buffer, and evaluate FORMS there like `progn'.
1054 Use unibyte mode for this."
1055   `(let (default-enable-multibyte-characters)
1056      (with-temp-buffer ,@forms)))
1057 (put 'mm-with-unibyte-buffer 'lisp-indent-function 0)
1058 (put 'mm-with-unibyte-buffer 'edebug-form-spec '(body))
1059
1060 (defmacro mm-with-multibyte-buffer (&rest forms)
1061   "Create a temporary buffer, and evaluate FORMS there like `progn'.
1062 Use multibyte mode for this."
1063   `(let ((default-enable-multibyte-characters t))
1064      (with-temp-buffer ,@forms)))
1065 (put 'mm-with-multibyte-buffer 'lisp-indent-function 0)
1066 (put 'mm-with-multibyte-buffer 'edebug-form-spec '(body))
1067
1068 (defmacro mm-with-unibyte-current-buffer (&rest forms)
1069   "Evaluate FORMS with current buffer temporarily made unibyte.
1070 Also bind `default-enable-multibyte-characters' to nil.
1071 Equivalent to `progn' in XEmacs
1072
1073 NOTE: Use this macro with caution in multibyte buffers (it is not
1074 worth using this macro in unibyte buffers of course).  Use of
1075 `(set-buffer-multibyte t)', which is run finally, is generally
1076 harmful since it is likely to modify existing data in the buffer.
1077 For instance, it converts \"\\300\\255\" into \"\\255\" in
1078 Emacs 23 (unicode)."
1079   (let ((multibyte (make-symbol "multibyte"))
1080         (buffer (make-symbol "buffer")))
1081     `(if mm-emacs-mule
1082          (let ((,multibyte enable-multibyte-characters)
1083                (,buffer (current-buffer)))
1084            (unwind-protect
1085                (let (default-enable-multibyte-characters)
1086                  (set-buffer-multibyte nil)
1087                  ,@forms)
1088              (set-buffer ,buffer)
1089              (set-buffer-multibyte ,multibyte)))
1090        (let (default-enable-multibyte-characters)
1091          ,@forms))))
1092 (put 'mm-with-unibyte-current-buffer 'lisp-indent-function 0)
1093 (put 'mm-with-unibyte-current-buffer 'edebug-form-spec '(body))
1094
1095 (defmacro mm-with-unibyte (&rest forms)
1096   "Eval the FORMS with the default value of `enable-multibyte-characters' nil."
1097   `(let (default-enable-multibyte-characters)
1098      ,@forms))
1099 (put 'mm-with-unibyte 'lisp-indent-function 0)
1100 (put 'mm-with-unibyte 'edebug-form-spec '(body))
1101
1102 (defmacro mm-with-multibyte (&rest forms)
1103   "Eval the FORMS with the default value of `enable-multibyte-characters' t."
1104   `(let ((default-enable-multibyte-characters t))
1105      ,@forms))
1106 (put 'mm-with-multibyte 'lisp-indent-function 0)
1107 (put 'mm-with-multibyte 'edebug-form-spec '(body))
1108
1109 (defun mm-find-charset-region (b e)
1110   "Return a list of Emacs charsets in the region B to E."
1111   (cond
1112    ((and (mm-multibyte-p)
1113          (fboundp 'find-charset-region))
1114     ;; Remove composition since the base charsets have been included.
1115     ;; Remove eight-bit-*, treat them as ascii.
1116     (let ((css (find-charset-region b e)))
1117       (dolist (cs
1118                '(composition eight-bit-control eight-bit-graphic control-1)
1119                css)
1120         (setq css (delq cs css)))))
1121    (t
1122     ;; We are in a unibyte buffer or XEmacs non-mule, so we futz around a bit.
1123     (save-excursion
1124       (save-restriction
1125         (narrow-to-region b e)
1126         (goto-char (point-min))
1127         (skip-chars-forward "\0-\177")
1128         (if (eobp)
1129             '(ascii)
1130           (let (charset)
1131             (setq charset
1132                   (and (boundp 'current-language-environment)
1133                        (car (last (assq 'charset
1134                                         (assoc current-language-environment
1135                                                language-info-alist))))))
1136             (if (eq charset 'ascii) (setq charset nil))
1137             (or charset
1138                 (setq charset
1139                       (car (last (assq mail-parse-charset
1140                                        mm-mime-mule-charset-alist)))))
1141             (list 'ascii (or charset 'latin-iso8859-1)))))))))
1142
1143 (defun mm-auto-mode-alist ()
1144   "Return an `auto-mode-alist' with only the .gz (etc) thingies."
1145   (let ((alist auto-mode-alist)
1146         out)
1147     (while alist
1148       (when (listp (cdar alist))
1149         (push (car alist) out))
1150       (pop alist))
1151     (nreverse out)))
1152
1153 (defvar mm-inhibit-file-name-handlers
1154   '(jka-compr-handler image-file-handler epa-file-handler)
1155   "A list of handlers doing (un)compression (etc) thingies.")
1156
1157 (defun mm-insert-file-contents (filename &optional visit beg end replace
1158                                          inhibit)
1159   "Like `insert-file-contents', but only reads in the file.
1160 A buffer may be modified in several ways after reading into the buffer due
1161 to advanced Emacs features, such as file-name-handlers, format decoding,
1162 `find-file-hooks', etc.
1163 If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'.
1164   This function ensures that none of these modifications will take place."
1165   (let* ((format-alist nil)
1166          (auto-mode-alist (if inhibit nil (mm-auto-mode-alist)))
1167          (default-major-mode 'fundamental-mode)
1168          (enable-local-variables nil)
1169          (after-insert-file-functions nil)
1170          (enable-local-eval nil)
1171          (inhibit-file-name-operation (if inhibit
1172                                           'insert-file-contents
1173                                         inhibit-file-name-operation))
1174          (inhibit-file-name-handlers
1175           (if inhibit
1176               (append mm-inhibit-file-name-handlers
1177                       inhibit-file-name-handlers)
1178             inhibit-file-name-handlers))
1179          (ffh (if (boundp 'find-file-hook)
1180                   'find-file-hook
1181                 'find-file-hooks))
1182          (val (symbol-value ffh)))
1183     (set ffh nil)
1184     (unwind-protect
1185         (insert-file-contents filename visit beg end replace)
1186       (set ffh val))))
1187
1188 (defun mm-append-to-file (start end filename &optional codesys inhibit)
1189   "Append the contents of the region to the end of file FILENAME.
1190 When called from a function, expects three arguments,
1191 START, END and FILENAME.  START and END are buffer positions
1192 saying what text to write.
1193 Optional fourth argument specifies the coding system to use when
1194 encoding the file.
1195 If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'."
1196   (let ((coding-system-for-write
1197          (or codesys mm-text-coding-system-for-write
1198              mm-text-coding-system))
1199         (inhibit-file-name-operation (if inhibit
1200                                          'append-to-file
1201                                        inhibit-file-name-operation))
1202         (inhibit-file-name-handlers
1203          (if inhibit
1204              (append mm-inhibit-file-name-handlers
1205                      inhibit-file-name-handlers)
1206            inhibit-file-name-handlers)))
1207     (write-region start end filename t 'no-message)
1208     (message "Appended to %s" filename)))
1209
1210 (defun mm-write-region (start end filename &optional append visit lockname
1211                               coding-system inhibit)
1212
1213   "Like `write-region'.
1214 If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'."
1215   (let ((coding-system-for-write
1216          (or coding-system mm-text-coding-system-for-write
1217              mm-text-coding-system))
1218         (inhibit-file-name-operation (if inhibit
1219                                          'write-region
1220                                        inhibit-file-name-operation))
1221         (inhibit-file-name-handlers
1222          (if inhibit
1223              (append mm-inhibit-file-name-handlers
1224                      inhibit-file-name-handlers)
1225            inhibit-file-name-handlers)))
1226     (write-region start end filename append visit lockname)))
1227
1228 (autoload 'gmm-write-region "gmm-utils")
1229
1230 ;; It is not a MIME function, but some MIME functions use it.
1231 (if (and (fboundp 'make-temp-file)
1232          (ignore-errors
1233            (let ((def (symbol-function 'make-temp-file)))
1234              (and (byte-code-function-p def)
1235                   (setq def (if (fboundp 'compiled-function-arglist)
1236                                 ;; XEmacs
1237                                 (eval (list 'compiled-function-arglist def))
1238                               (aref def 0)))
1239                   (>= (length def) 4)
1240                   (eq (nth 3 def) 'suffix)))))
1241     (defalias 'mm-make-temp-file 'make-temp-file)
1242   ;; Stolen (and modified for XEmacs) from Emacs 22.
1243   (defun mm-make-temp-file (prefix &optional dir-flag suffix)
1244     "Create a temporary file.
1245 The returned file name (created by appending some random characters at the end
1246 of PREFIX, and expanding against `temporary-file-directory' if necessary),
1247 is guaranteed to point to a newly created empty file.
1248 You can then use `write-region' to write new data into the file.
1249
1250 If DIR-FLAG is non-nil, create a new empty directory instead of a file.
1251
1252 If SUFFIX is non-nil, add that at the end of the file name."
1253     (let ((umask (default-file-modes))
1254           file)
1255       (unwind-protect
1256           (progn
1257             ;; Create temp files with strict access rights.  It's easy to
1258             ;; loosen them later, whereas it's impossible to close the
1259             ;; time-window of loose permissions otherwise.
1260             (set-default-file-modes 448)
1261             (while (condition-case err
1262                        (progn
1263                          (setq file
1264                                (make-temp-name
1265                                 (expand-file-name
1266                                  prefix
1267                                  (if (fboundp 'temp-directory)
1268                                      ;; XEmacs
1269                                      (temp-directory)
1270                                    temporary-file-directory))))
1271                          (if suffix
1272                              (setq file (concat file suffix)))
1273                          (if dir-flag
1274                              (make-directory file)
1275                            ;; NOTE: This is unsafe if Emacs 20
1276                            ;; users and XEmacs users don't use
1277                            ;; a secure temp directory.
1278                            (gmm-write-region "" nil file nil 'silent
1279                                              nil 'excl))
1280                          nil)
1281                      (file-already-exists t)
1282                      ;; The XEmacs version of `make-directory' issues
1283                      ;; `file-error'.
1284                      (file-error (or (and (featurep 'xemacs)
1285                                           (file-exists-p file))
1286                                      (signal (car err) (cdr err)))))
1287               ;; the file was somehow created by someone else between
1288               ;; `make-temp-name' and `write-region', let's try again.
1289               nil)
1290             file)
1291         ;; Reset the umask.
1292         (set-default-file-modes umask)))))
1293
1294 (defun mm-image-load-path (&optional package)
1295   (let (dir result)
1296     (dolist (path load-path (nreverse result))
1297       (when (and path
1298                  (file-directory-p
1299                   (setq dir (concat (file-name-directory
1300                                      (directory-file-name path))
1301                                     "etc/images/" (or package "gnus/")))))
1302         (push dir result))
1303       (push path result))))
1304
1305 ;; Fixme: This doesn't look useful where it's used.
1306 (if (fboundp 'detect-coding-region)
1307     (defun mm-detect-coding-region (start end)
1308       "Like `detect-coding-region' except returning the best one."
1309       (let ((coding-systems
1310              (detect-coding-region start end)))
1311         (or (car-safe coding-systems)
1312             coding-systems)))
1313   (defun mm-detect-coding-region (start end)
1314     (let ((point (point)))
1315       (goto-char start)
1316       (skip-chars-forward "\0-\177" end)
1317       (prog1
1318           (if (eq (point) end) 'ascii (mm-guess-charset))
1319         (goto-char point)))))
1320
1321 (declare-function mm-detect-coding-region "mm-util" (start end))
1322
1323 (if (fboundp 'coding-system-get)
1324     (defun mm-detect-mime-charset-region (start end)
1325       "Detect MIME charset of the text in the region between START and END."
1326       (let ((cs (mm-detect-coding-region start end)))
1327         (or (coding-system-get cs :mime-charset)
1328             (coding-system-get cs 'mime-charset))))
1329   (defun mm-detect-mime-charset-region (start end)
1330     "Detect MIME charset of the text in the region between START and END."
1331     (let ((cs (mm-detect-coding-region start end)))
1332       cs)))
1333
1334 (eval-when-compile
1335   (unless (fboundp 'coding-system-to-mime-charset)
1336     (defalias 'coding-system-to-mime-charset 'ignore)))
1337
1338 (defun mm-coding-system-to-mime-charset (coding-system)
1339   "Return the MIME charset corresponding to CODING-SYSTEM.
1340 To make this function work with XEmacs, the APEL package is required."
1341   (when coding-system
1342     (or (and (fboundp 'coding-system-get)
1343              (or (coding-system-get coding-system :mime-charset)
1344                  (coding-system-get coding-system 'mime-charset)))
1345         (and (featurep 'xemacs)
1346              (or (and (fboundp 'coding-system-to-mime-charset)
1347                       (not (eq (symbol-function 'coding-system-to-mime-charset)
1348                                'ignore)))
1349                  (and (condition-case nil
1350                           (require 'mcharset)
1351                         (error nil))
1352                       (fboundp 'coding-system-to-mime-charset)))
1353              (coding-system-to-mime-charset coding-system)))))
1354
1355 (eval-when-compile
1356   (require 'jka-compr))
1357
1358 (defun mm-decompress-buffer (filename &optional inplace force)
1359   "Decompress buffer's contents, depending on jka-compr.
1360 Only when FORCE is t or `auto-compression-mode' is enabled and FILENAME
1361 agrees with `jka-compr-compression-info-list', decompression is done.
1362 Signal an error if FORCE is neither nil nor t and compressed data are
1363 not decompressed because `auto-compression-mode' is disabled.
1364 If INPLACE is nil, return decompressed data or nil without modifying
1365 the buffer.  Otherwise, replace the buffer's contents with the
1366 decompressed data.  The buffer's multibyteness must be turned off."
1367   (when (and filename
1368              (if force
1369                  (prog1 t (require 'jka-compr))
1370                (and (fboundp 'jka-compr-installed-p)
1371                     (jka-compr-installed-p))))
1372     (let ((info (jka-compr-get-compression-info filename)))
1373       (when info
1374         (unless (or (memq force (list nil t))
1375                     (jka-compr-installed-p))
1376           (error ""))
1377         (let ((prog (jka-compr-info-uncompress-program info))
1378               (args (jka-compr-info-uncompress-args info))
1379               (msg (format "%s %s..."
1380                            (jka-compr-info-uncompress-message info)
1381                            filename))
1382               (err-file (jka-compr-make-temp-name))
1383               (cur (current-buffer))
1384               (coding-system-for-read mm-binary-coding-system)
1385               (coding-system-for-write mm-binary-coding-system)
1386               retval err-msg)
1387           (message "%s" msg)
1388           (mm-with-unibyte-buffer
1389             (insert-buffer-substring cur)
1390             (condition-case err
1391                 (progn
1392                   (unless (memq (apply 'call-process-region
1393                                        (point-min) (point-max)
1394                                        prog t (list t err-file) nil args)
1395                                 jka-compr-acceptable-retval-list)
1396                     (erase-buffer)
1397                     (insert (mapconcat
1398                              'identity
1399                              (delete "" (split-string
1400                                          (prog2
1401                                              (insert-file-contents err-file)
1402                                              (buffer-string)
1403                                            (erase-buffer))))
1404                              " ")
1405                             "\n")
1406                     (setq err-msg
1407                           (format "Error while executing \"%s %s < %s\""
1408                                   prog (mapconcat 'identity args " ")
1409                                   filename)))
1410                   (setq retval (buffer-string)))
1411               (error
1412                (setq err-msg (error-message-string err)))))
1413           (when (file-exists-p err-file)
1414             (ignore-errors (jka-compr-delete-temp-file err-file)))
1415           (when inplace
1416             (unless err-msg
1417               (delete-region (point-min) (point-max))
1418               (insert retval))
1419             (setq retval nil))
1420           (message "%s" (or err-msg (concat msg "done")))
1421           retval)))))
1422
1423 (eval-when-compile
1424   (unless (fboundp 'coding-system-name)
1425     (defalias 'coding-system-name 'ignore))
1426   (unless (fboundp 'find-file-coding-system-for-read-from-filename)
1427     (defalias 'find-file-coding-system-for-read-from-filename 'ignore))
1428   (unless (fboundp 'find-operation-coding-system)
1429     (defalias 'find-operation-coding-system 'ignore)))
1430
1431 (defun mm-find-buffer-file-coding-system (&optional filename)
1432   "Find coding system used to decode the contents of the current buffer.
1433 This function looks for the coding system magic cookie or examines the
1434 coding system specified by `file-coding-system-alist' being associated
1435 with FILENAME which defaults to `buffer-file-name'.  Data compressed by
1436 gzip, bzip2, etc. are allowed."
1437   (unless filename
1438     (setq filename buffer-file-name))
1439   (save-excursion
1440     (let ((decomp (unless ;; No worth to examine charset of tar files.
1441                       (and filename
1442                            (string-match
1443                             "\\.\\(?:tar\\.[^.]+\\|tbz\\|tgz\\)\\'"
1444                             filename))
1445                     (mm-decompress-buffer filename nil t))))
1446       (when decomp
1447         (set-buffer (let (default-enable-multibyte-characters)
1448                       (generate-new-buffer " *temp*")))
1449         (insert decomp)
1450         (setq filename (file-name-sans-extension filename)))
1451       (goto-char (point-min))
1452       (prog1
1453           (cond
1454            ((boundp 'set-auto-coding-function) ;; Emacs
1455             (if filename
1456                 (or (funcall (symbol-value 'set-auto-coding-function)
1457                              filename (- (point-max) (point-min)))
1458                     (car (find-operation-coding-system 'insert-file-contents
1459                                                        filename)))
1460               (let (auto-coding-alist)
1461                 (condition-case nil
1462                     (funcall (symbol-value 'set-auto-coding-function)
1463                              nil (- (point-max) (point-min)))
1464                   (error nil)))))
1465            ((and (featurep 'xemacs) (featurep 'file-coding)) ;; XEmacs
1466             (let ((case-fold-search t)
1467                   (end (point-at-eol))
1468                   codesys start)
1469               (or
1470                (and (re-search-forward "-\\*-+[\t ]*" end t)
1471                     (progn
1472                       (setq start (match-end 0))
1473                       (re-search-forward "[\t ]*-+\\*-" end t))
1474                     (progn
1475                       (setq end (match-beginning 0))
1476                       (goto-char start)
1477                       (or (looking-at "coding:[\t ]*\\([^\t ;]+\\)")
1478                           (re-search-forward
1479                            "[\t ;]+coding:[\t ]*\\([^\t ;]+\\)"
1480                            end t)))
1481                     (find-coding-system (setq codesys
1482                                               (intern (match-string 1))))
1483                     codesys)
1484                (and (re-search-forward "^[\t ]*;+[\t ]*Local[\t ]+Variables:"
1485                                        nil t)
1486                     (progn
1487                       (setq start (match-end 0))
1488                       (re-search-forward "^[\t ]*;+[\t ]*End:" nil t))
1489                     (progn
1490                       (setq end (match-beginning 0))
1491                       (goto-char start)
1492                       (re-search-forward
1493                        "^[\t ]*;+[\t ]*coding:[\t ]*\\([^\t\n\r ]+\\)"
1494                        end t))
1495                     (find-coding-system (setq codesys
1496                                               (intern (match-string 1))))
1497                     codesys)
1498                (and (progn
1499                       (goto-char (point-min))
1500                       (setq case-fold-search nil)
1501                       (re-search-forward "^;;;coding system: "
1502                                          ;;(+ (point-min) 3000) t))
1503                                          nil t))
1504                     (looking-at "[^\t\n\r ]+")
1505                     (find-coding-system
1506                      (setq codesys (intern (match-string 0))))
1507                     codesys)
1508                (and filename
1509                     (setq codesys
1510                           (find-file-coding-system-for-read-from-filename
1511                            filename))
1512                     (coding-system-name (coding-system-base codesys)))))))
1513         (when decomp
1514           (kill-buffer (current-buffer)))))))
1515
1516 (provide 'mm-util)
1517
1518 ;; arch-tag: 94dc5388-825d-4fd1-bfa5-2100aa351238
1519 ;;; mm-util.el ends here