10eb69b0e1430643734f01819d0fceeafd1014e0
[gnus] / lisp / mm-util.el
1 ;;; mm-util.el --- Utility functions for Mule and low level things
2 ;; Copyright (C) 1998, 1999, 2000, 2001 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 (eval-when-compile (require 'cl))
28 (require 'mail-prsvr)
29
30 (eval-and-compile
31   (mapcar
32    (lambda (elem)
33      (let ((nfunc (intern (format "mm-%s" (car elem)))))
34        (if (fboundp (car elem))
35            (defalias nfunc (car elem))
36          (defalias nfunc (cdr elem)))))
37    '((decode-coding-string . (lambda (s a) s))
38      (encode-coding-string . (lambda (s a) s))
39      (encode-coding-region . ignore)
40      (coding-system-list . ignore)
41      (decode-coding-region . ignore)
42      (char-int . identity)
43      (device-type . ignore)
44      (coding-system-equal . equal)
45      (annotationp . ignore)
46      (set-buffer-file-coding-system . ignore)
47      (make-char
48       . (lambda (charset int)
49           (int-to-char int)))
50      (read-charset
51       . (lambda (prompt)
52           "Return a charset."
53           (intern
54            (completing-read
55             prompt
56             (mapcar (lambda (e) (list (symbol-name (car e))))
57                     mm-mime-mule-charset-alist)
58             nil t))))
59      (subst-char-in-string
60       . (lambda (from to string) ;; stolen (and renamed) from nnheader.el
61           "Replace characters in STRING from FROM to TO."
62           (let ((string (substring string 0)) ;Copy string.
63                 (len (length string))
64                 (idx 0))
65             ;; Replace all occurrences of FROM with TO.
66             (while (< idx len)
67               (when (= (aref string idx) from)
68                 (aset string idx to))
69               (setq idx (1+ idx)))
70             string)))
71      (string-as-unibyte . identity)
72      (string-as-multibyte . identity)
73      (multibyte-string-p . ignore))))
74
75 (eval-and-compile
76   (defalias 'mm-char-or-char-int-p
77     (cond
78      ((fboundp 'char-or-char-int-p) 'char-or-char-int-p)
79      ((fboundp 'char-valid-p) 'char-valid-p)
80      (t 'identity))))
81
82 (eval-and-compile
83   (defalias 'mm-read-coding-system
84     (cond
85      ((fboundp 'read-coding-system)
86       (if (and (featurep 'xemacs)
87                (<= (string-to-number emacs-version) 21.1))
88           (lambda (prompt &optional default-coding-system)
89             (read-coding-system prompt))
90         'read-coding-system))
91      (t (lambda (prompt &optional default-coding-system)
92           "Prompt the user for a coding system."
93           (completing-read
94            prompt (mapcar (lambda (s) (list (symbol-name (car s))))
95                           mm-mime-mule-charset-alist)))))))
96
97 (defvar mm-coding-system-list nil)
98 (defun mm-get-coding-system-list ()
99   "Get the coding system list."
100   (or mm-coding-system-list
101       (setq mm-coding-system-list (mm-coding-system-list))))
102
103 (defun mm-coding-system-p (sym)
104   "Return non-nil if SYM is a coding system."
105   (or (and (fboundp 'coding-system-p) (coding-system-p sym))
106       (memq sym (mm-get-coding-system-list))))
107
108 (defvar mm-charset-synonym-alist
109   `(
110     ;; Perfectly fine?  A valid MIME name, anyhow.
111     ,@(unless (mm-coding-system-p 'big5)
112        '((big5 . cn-big5)))
113     ;; Not in XEmacs, but it's not a proper MIME charset anyhow.
114     ,@(unless (mm-coding-system-p 'x-ctext)
115        '((x-ctext . ctext)))
116     ;; Apparently not defined in Emacs 20, but is a valid MIME name.
117     ,@(unless (mm-coding-system-p 'gb2312)
118        '((gb2312 . cn-gb-2312)))
119     ;; ISO-8859-15 is very similar to ISO-8859-1.
120     ,@(unless (mm-coding-system-p 'iso-8859-15) ; Emacs 21 defines it.
121        '((iso-8859-15 . iso-8859-1)))
122     ;; Windows-1252 is actually a superset of Latin-1.  See also
123     ;; `gnus-article-dumbquotes-map'.
124     ,@(unless (mm-coding-system-p 'windows-1252)        
125        (if (mm-coding-system-p 'cp1252)
126            '((windows-1252 . cp1252))
127          '((windows-1252 . iso-8859-1))))
128     ;; Windows-1250 is a variant of Latin-2 heavily used by Microsoft
129     ;; Outlook users in Czech republic. Use this to allow reading of their
130     ;; e-mails. cp1250 should be defined by M-x codepage-setup.
131     ,@(if (and (not (mm-coding-system-p 'windows-1250))
132                (mm-coding-system-p 'cp1250))
133           '((windows-1250 . cp1250)))
134     )
135   "A mapping from invalid charset names to the real charset names.")
136
137 (defvar mm-binary-coding-system
138   (cond
139    ((mm-coding-system-p 'binary) 'binary)
140    ((mm-coding-system-p 'no-conversion) 'no-conversion)
141    (t nil))
142   "100% binary coding system.")
143
144 (defvar mm-text-coding-system
145   (or (if (memq system-type '(windows-nt ms-dos ms-windows))
146           (and (mm-coding-system-p 'raw-text-dos) 'raw-text-dos)
147         (and (mm-coding-system-p 'raw-text) 'raw-text))
148       mm-binary-coding-system)
149   "Text-safe coding system (For removing ^M).")
150
151 (defvar mm-text-coding-system-for-write nil
152   "Text coding system for write.")
153
154 (defvar mm-auto-save-coding-system
155   (cond
156    ((mm-coding-system-p 'emacs-mule)
157     (if (memq system-type '(windows-nt ms-dos ms-windows))
158         (if (mm-coding-system-p 'emacs-mule-dos)
159             'emacs-mule-dos mm-binary-coding-system)
160       'emacs-mule))
161    ((mm-coding-system-p 'escape-quoted) 'escape-quoted)
162    (t mm-binary-coding-system))
163   "Coding system of auto save file.")
164
165 (defvar mm-universal-coding-system mm-auto-save-coding-system
166   "The universal Coding system.")
167
168 ;; Fixme: some of the cars here aren't valid MIME charsets.  That
169 ;; should only matter with XEmacs, though.
170 (defvar mm-mime-mule-charset-alist
171   `((us-ascii ascii)
172     (iso-8859-1 latin-iso8859-1)
173     (iso-8859-2 latin-iso8859-2)
174     (iso-8859-3 latin-iso8859-3)
175     (iso-8859-4 latin-iso8859-4)
176     (iso-8859-5 cyrillic-iso8859-5)
177     ;; Non-mule (X)Emacs uses the last mule-charset for 8bit characters.
178     ;; The fake mule-charset, gnus-koi8-r, tells Gnus that the default
179     ;; charset is koi8-r, not iso-8859-5.
180     (koi8-r cyrillic-iso8859-5 gnus-koi8-r)
181     (iso-8859-6 arabic-iso8859-6)
182     (iso-8859-7 greek-iso8859-7)
183     (iso-8859-8 hebrew-iso8859-8)
184     (iso-8859-9 latin-iso8859-9)
185     (iso-8859-14 latin-iso8859-14)
186     (iso-8859-15 latin-iso8859-15)
187     (viscii vietnamese-viscii-lower)
188     (iso-2022-jp latin-jisx0201 japanese-jisx0208 japanese-jisx0208-1978)
189     (euc-kr korean-ksc5601)
190     (gb2312 chinese-gb2312)
191     (big5 chinese-big5-1 chinese-big5-2)
192     (tibetan tibetan)
193     (thai-tis620 thai-tis620)
194     (iso-2022-7bit ethiopic arabic-1-column arabic-2-column)
195     (iso-2022-jp-2 latin-iso8859-1 greek-iso8859-7
196                    latin-jisx0201 japanese-jisx0208-1978
197                    chinese-gb2312 japanese-jisx0208
198                    korean-ksc5601 japanese-jisx0212
199                    katakana-jisx0201)
200     (iso-2022-int-1 latin-iso8859-1 greek-iso8859-7
201                     latin-jisx0201 japanese-jisx0208-1978
202                     chinese-gb2312 japanese-jisx0208
203                     korean-ksc5601 japanese-jisx0212
204                     chinese-cns11643-1 chinese-cns11643-2)
205     (iso-2022-int-1 latin-iso8859-1 latin-iso8859-2
206                     cyrillic-iso8859-5 greek-iso8859-7
207                     latin-jisx0201 japanese-jisx0208-1978
208                     chinese-gb2312 japanese-jisx0208
209                     korean-ksc5601 japanese-jisx0212
210                     chinese-cns11643-1 chinese-cns11643-2
211                     chinese-cns11643-3 chinese-cns11643-4
212                     chinese-cns11643-5 chinese-cns11643-6
213                     chinese-cns11643-7)
214     ,(if (or (not (fboundp 'charsetp)) ;; non-Mule case
215              (charsetp 'unicode-a)
216              (not (mm-coding-system-p 'mule-utf-8)))
217          '(utf-8 unicode-a unicode-b unicode-c unicode-d unicode-e)
218        ;; If we have utf-8 we're in Mule 5+.
219        (append '(utf-8)
220                (delete 'ascii
221                        (coding-system-get 'mule-utf-8 'safe-charsets)))))
222   "Alist of MIME-charset/MULE-charsets.")
223
224 ;; Correct by construction, but should be unnecessary:
225 ;; XEmacs hates it.
226 (when (and (not (featurep 'xemacs))
227            (fboundp 'coding-system-list)
228            (fboundp 'sort-coding-systems))
229   (setq mm-mime-mule-charset-alist
230         (apply
231          'nconc
232          (mapcar
233           (lambda (cs)
234             (when (and (coding-system-get cs 'mime-charset)
235                        (not (eq t (coding-system-get cs 'safe-charsets))))
236               (list (cons (coding-system-get cs 'mime-charset)
237                           (delq 'ascii
238                                 (coding-system-get cs 'safe-charsets))))))
239           (sort-coding-systems (coding-system-list 'base-only))))))
240
241 (defvar mm-hack-charsets '(iso-8859-15 iso-2022-jp-2)
242   "A list of special charsets when encoding.
243 The each element could be one of the following:
244 `iso-8859-15'    convert ISO-8859-1, -2 to ISO-8859-15 if ISO-8859-15 exists.
245 `iso-2022-jp-2'  convert ISO-2022-jp to ISO-2022-jp-2 if ISO-2022-jp-2 exists."
246 )
247
248 ;; FIXME: what the value should be?
249 (defvar mm-iso-8859-15-compatible '(iso-8859-1 iso-8859-2)
250   "Coding systems that are compatible with iso-8859-15.")
251
252 (defvar mm-iso-8859-x-to-15-table
253   (and (fboundp 'coding-system-p)
254        (mm-coding-system-p 'iso-8859-15)
255        (mapcar (lambda (cs)
256                  (if (mm-coding-system-p cs)
257                      (let ((c (string-to-char
258                                (decode-coding-string "\341" cs))))
259                        (cons (char-charset c)
260                              (- (string-to-char
261                                  (decode-coding-string "\341" 'iso-8859-15))
262                                 c)))
263                    '(gnus-charset . 0)))
264                mm-iso-8859-15-compatible))
265   "A table of the difference character between ISO-8859-X and ISO-8859-15.")
266
267 ;;; Internal variables:
268
269 ;;; Functions:
270
271 (defun mm-mule-charset-to-mime-charset (charset)
272   "Return the MIME charset corresponding to the given Mule CHARSET."
273   (if (fboundp 'find-coding-systems-for-charsets)
274       (let (mime)
275         (dolist (cs (find-coding-systems-for-charsets (list charset)))
276           (unless mime
277             (when cs
278               (setq mime (coding-system-get cs 'mime-charset)))))
279         mime)
280     (let ((alist mm-mime-mule-charset-alist)
281           out)
282       (while alist
283         (when (memq charset (cdar alist))
284           (setq out (caar alist)
285                 alist nil))
286         (pop alist))
287       out)))
288
289 (defun mm-charset-to-coding-system (charset &optional lbt)
290   "Return coding-system corresponding to CHARSET.
291 CHARSET is a symbol naming a MIME charset.
292 If optional argument LBT (`unix', `dos' or `mac') is specified, it is
293 used as the line break code type of the coding system."
294   (when (stringp charset)
295     (setq charset (intern (downcase charset))))
296   (when lbt
297     (setq charset (intern (format "%s-%s" charset lbt))))
298   (cond
299    ((null charset)
300     charset)
301    ;; Running in a non-MULE environment.
302    ((null (mm-get-coding-system-list))
303     charset)
304    ;; ascii
305    ((eq charset 'us-ascii)
306     'ascii)
307    ;; Check to see whether we can handle this charset.  (This depends
308    ;; on there being some coding system matching each `mime-charset'
309    ;; property defined, as there should be.)
310    ((and (mm-coding-system-p charset)
311 ;;; Doing this would potentially weed out incorrect charsets.
312 ;;;      charset
313 ;;;      (eq charset (coding-system-get charset 'mime-charset))
314          )
315     charset)
316    ;; Translate invalid charsets.
317    ((mm-coding-system-p (setq charset
318                            (cdr (assq charset
319                                       mm-charset-synonym-alist))))
320     charset)
321    ;; Last resort: search the coding system list for entries which
322    ;; have the right mime-charset in case the canonical name isn't
323    ;; defined (though it should be).
324    ((let (cs)
325       ;; mm-get-coding-system-list returns a list of cs without lbt.
326       ;; Do we need -lbt?
327       (dolist (c (mm-get-coding-system-list))
328         (if (and (null cs)
329                  (eq charset (coding-system-get c 'mime-charset)))
330             (setq cs c)))
331       cs))))
332
333 (defsubst mm-replace-chars-in-string (string from to)
334   (mm-subst-char-in-string from to string))
335
336 (eval-and-compile
337   (defvar mm-emacs-mule (and (not (featurep 'xemacs))
338                              (boundp 'default-enable-multibyte-characters)
339                              default-enable-multibyte-characters
340                              (fboundp 'set-buffer-multibyte))
341     "Emacs mule.")
342   
343   (defvar mm-mule4-p (and mm-emacs-mule
344                           (fboundp 'charsetp)
345                           (not (charsetp 'eight-bit-control)))
346     "Mule version 4.")
347
348   (if mm-emacs-mule
349       (defun mm-enable-multibyte ()
350         "Set the multibyte flag of the current buffer.
351 Only do this if the default value of `enable-multibyte-characters' is
352 non-nil.  This is a no-op in XEmacs."
353         (set-buffer-multibyte t))
354     (defalias 'mm-enable-multibyte 'ignore))
355
356   (if mm-emacs-mule
357       (defun mm-disable-multibyte ()
358         "Unset the multibyte flag of in the current buffer.
359 This is a no-op in XEmacs."
360         (set-buffer-multibyte nil))
361     (defalias 'mm-disable-multibyte 'ignore))
362
363   (if mm-mule4-p
364       (defun mm-enable-multibyte-mule4  ()
365         "Enable multibyte in the current buffer.
366 Only used in Emacs Mule 4."
367         (set-buffer-multibyte t))
368     (defalias 'mm-enable-multibyte-mule4 'ignore))
369   
370   (if mm-mule4-p
371       (defun mm-disable-multibyte-mule4 ()
372         "Disable multibyte in the current buffer.
373 Only used in Emacs Mule 4."
374         (set-buffer-multibyte nil))
375     (defalias 'mm-disable-multibyte-mule4 'ignore)))
376
377 (defun mm-preferred-coding-system (charset)
378   ;; A typo in some Emacs versions.
379   (or (get-charset-property charset 'prefered-coding-system)
380       (get-charset-property charset 'preferred-coding-system)))
381
382 (defun mm-charset-after (&optional pos)
383   "Return charset of a character in current buffer at position POS.
384 If POS is nil, it defauls to the current point.
385 If POS is out of range, the value is nil.
386 If the charset is `composition', return the actual one."
387   (let ((char (char-after pos)) charset)
388     (if (< (mm-char-int char) 128)
389         (setq charset 'ascii)
390       ;; charset-after is fake in some Emacsen.
391       (setq charset (and (fboundp 'char-charset) (char-charset char)))
392       (if (eq charset 'composition)
393           (let ((p (or pos (point))))
394             (cadr (find-charset-region p (1+ p))))
395         (if (and charset (not (memq charset '(ascii eight-bit-control
396                                                     eight-bit-graphic))))
397             charset
398           (or
399            mail-parse-mule-charset ;; cached mule-charset
400            (progn
401              (setq mail-parse-mule-charset
402                    (and (boundp 'current-language-environment)
403                         (car (last
404                               (assq 'charset
405                                     (assoc current-language-environment
406                                            language-info-alist))))))
407              (if (or (not mail-parse-mule-charset)
408                      (eq mail-parse-mule-charset 'ascii))
409                  (setq mail-parse-mule-charset
410                        (or (car (last (assq mail-parse-charset
411                                             mm-mime-mule-charset-alist)))
412                            ;; Fixme: don't fix that!
413                            'latin-iso8859-1)))
414              mail-parse-mule-charset)))))))
415
416 (defun mm-mime-charset (charset)
417   "Return the MIME charset corresponding to the given Mule CHARSET."
418   (if (eq charset 'unknown)
419       (error "The message contains non-printable characters, please use attachment"))
420   (if (and (fboundp 'coding-system-get) (fboundp 'get-charset-property))
421       ;; This exists in Emacs 20.
422       (or
423        (and (mm-preferred-coding-system charset)
424             (coding-system-get
425              (mm-preferred-coding-system charset) 'mime-charset))
426        (and (eq charset 'ascii)
427             'us-ascii)
428        (mm-preferred-coding-system charset)
429        (mm-mule-charset-to-mime-charset charset))
430     ;; This is for XEmacs.
431     (mm-mule-charset-to-mime-charset charset)))
432
433 (defun mm-delete-duplicates (list)
434   "Simple  substitute for CL `delete-duplicates', testing with `equal'."
435   (let (result head)
436     (while list
437       (setq head (car list))
438       (setq list (delete head list))
439       (setq result (cons head result)))
440     (nreverse result)))
441
442 ;; It's not clear whether this is supposed to mean the global or local
443 ;; setting.  I think it's used inconsistently.  -- fx
444 (defsubst mm-multibyte-p ()
445   "Say whether multibyte is enabled."
446   (if (and (not (featurep 'xemacs))
447            (boundp 'enable-multibyte-characters))
448       enable-multibyte-characters
449     (featurep 'mule)))
450
451 (defun mm-iso-8859-x-to-15-region (&optional b e)
452   (let (charset item)
453     (save-restriction
454       (if e (narrow-to-region b e))
455       (goto-char (point-min))
456       (skip-chars-forward "\0-\177")
457       (while (not (eobp))
458         (cond 
459          ((setq item (assq (charset-after) mm-iso-8859-x-to-15-table))
460           (insert (prog1
461                       (+ (char-after) (cdr item))
462                     (delete-char 1))))
463          (t (forward-char)))
464         (skip-chars-forward "\0-\177")))))
465
466 (defun mm-find-mime-charset-region (b e &optional hack-charsets)
467   "Return the MIME charsets needed to encode the region between B and E.
468 Nil means ASCII, a single-element list represents an appropriate MIME
469 charset, and a longer list means no appropriate charset."
470   (let (charsets)
471     ;; The return possibilities of this function are a mess...
472     (or (and (mm-multibyte-p)
473              (fboundp 'find-coding-systems-region)
474              ;; Find the mime-charset of the most preferred coding
475              ;; system that has one.
476              (let ((systems (find-coding-systems-region b e)))
477                ;; Fixme: The `mime-charset' (`x-ctext') of `compound-text'
478                ;; is not in the IANA list.
479                (setq systems (delq 'compound-text systems))
480                (unless (equal systems '(undecided))
481                  (while systems
482                    (let ((cs (coding-system-get (pop systems) 'mime-charset)))
483                      (if cs
484                          (setq systems nil
485                                charsets (list cs))))))
486                charsets))
487         ;; Otherwise we're not multibyte, XEmacs or a single coding
488         ;; system won't cover it.
489         (setq charsets 
490               (mm-delete-duplicates
491                (mapcar 'mm-mime-charset
492                        (delq 'ascii
493                              (mm-find-charset-region b e))))))
494     (when (and (memq 'iso-8859-15 charsets)
495              (memq 'iso-8859-15 hack-charsets))
496       (save-excursion
497         (mm-iso-8859-x-to-15-region b e))
498       (mapcar (lambda (x)
499                 (setq charsets (delq x charsets)))
500               mm-iso-8859-15-compatible))
501     (if (and (memq 'iso-2022-jp-2 charsets)
502              (memq 'iso-2022-jp-2 hack-charsets))
503         (setq charsets (delq 'iso-2022-jp charsets))
504           charsets)))
505
506 (defmacro mm-with-unibyte-buffer (&rest forms)
507   "Create a temporary buffer, and evaluate FORMS there like `progn'.
508 Use unibyte mode for this."
509   `(let (default-enable-multibyte-characters)
510      (with-temp-buffer ,@forms)))
511 (put 'mm-with-unibyte-buffer 'lisp-indent-function 0)
512 (put 'mm-with-unibyte-buffer 'edebug-form-spec '(body))
513
514 (defmacro mm-with-unibyte-current-buffer (&rest forms)
515   "Evaluate FORMS with current current buffer temporarily made unibyte.
516 Also bind `default-enable-multibyte-characters' to nil.
517 Equivalent to `progn' in XEmacs"
518   (let ((multibyte (make-symbol "multibyte"))
519         (buffer (make-symbol "buffer")))
520     `(if mm-emacs-mule 
521          (let ((,multibyte enable-multibyte-characters)
522                (,buffer (current-buffer)))
523            (unwind-protect
524                (let (default-enable-multibyte-characters)
525                  (set-buffer-multibyte nil)
526                  ,@forms)
527              (set-buffer ,buffer)
528              (set-buffer-multibyte ,multibyte)))
529        (let (default-enable-multibyte-characters)
530          ,@forms))))
531 (put 'mm-with-unibyte-current-buffer 'lisp-indent-function 0)
532 (put 'mm-with-unibyte-current-buffer 'edebug-form-spec '(body))
533
534 (defmacro mm-with-unibyte-current-buffer-mule4 (&rest forms)
535   "Evaluate FORMS there like `progn' in current buffer.
536 Mule4 only."
537   (let ((multibyte (make-symbol "multibyte"))
538         (buffer (make-symbol "buffer")))
539     `(if mm-mule4-p
540          (let ((,multibyte enable-multibyte-characters)
541                (,buffer (current-buffer)))
542            (unwind-protect
543                (let (default-enable-multibyte-characters)
544                  (set-buffer-multibyte nil)
545                  ,@forms)
546              (set-buffer ,buffer)
547              (set-buffer-multibyte ,multibyte)))
548        (let (default-enable-multibyte-characters)
549          ,@forms))))
550 (put 'mm-with-unibyte-current-buffer-mule4 'lisp-indent-function 0)
551 (put 'mm-with-unibyte-current-buffer-mule4 'edebug-form-spec '(body))
552
553 (defmacro mm-with-unibyte (&rest forms)
554   "Eval the FORMS with the default value of `enable-multibyte-characters' nil, ."
555   `(let (default-enable-multibyte-characters)
556      ,@forms))
557 (put 'mm-with-unibyte 'lisp-indent-function 0)
558 (put 'mm-with-unibyte 'edebug-form-spec '(body))
559
560 (defun mm-find-charset-region (b e)
561   "Return a list of Emacs charsets in the region B to E."
562   (cond
563    ((and (mm-multibyte-p)
564          (fboundp 'find-charset-region))
565     ;; Remove composition since the base charsets have been included.
566     ;; Remove eight-bit-*, treat them as ascii.
567     (let ((css (find-charset-region b e)))
568       (mapcar (lambda (cs) (setq css (delq cs css)))
569               '(composition eight-bit-control eight-bit-graphic
570                             control-1))
571       css))
572    (t
573     ;; We are in a unibyte buffer or XEmacs non-mule, so we futz around a bit.
574     (save-excursion
575       (save-restriction
576         (narrow-to-region b e)
577         (goto-char (point-min))
578         (skip-chars-forward "\0-\177")
579         (if (eobp)
580             '(ascii)
581           (let (charset)
582             (setq charset
583                   (and (boundp 'current-language-environment)
584                        (car (last (assq 'charset
585                                         (assoc current-language-environment
586                                                language-info-alist))))))
587             (if (eq charset 'ascii) (setq charset nil))
588             (or charset
589                 (setq charset
590                       (car (last (assq mail-parse-charset
591                                        mm-mime-mule-charset-alist)))))
592             (list 'ascii (or charset 'latin-iso8859-1)))))))))
593
594 (if (fboundp 'shell-quote-argument)
595     (defalias 'mm-quote-arg 'shell-quote-argument)
596   (defun mm-quote-arg (arg)
597     "Return a version of ARG that is safe to evaluate in a shell."
598     (let ((pos 0) new-pos accum)
599       ;; *** bug: we don't handle newline characters properly
600       (while (setq new-pos (string-match "[]*[;!'`\"$\\& \t{} |()<>]" arg pos))
601         (push (substring arg pos new-pos) accum)
602         (push "\\" accum)
603         (push (list (aref arg new-pos)) accum)
604         (setq pos (1+ new-pos)))
605       (if (= pos 0)
606           arg
607         (apply 'concat (nconc (nreverse accum) (list (substring arg pos))))))))
608
609 (defun mm-auto-mode-alist ()
610   "Return an `auto-mode-alist' with only the .gz (etc) thingies."
611   (let ((alist auto-mode-alist)
612         out)
613     (while alist
614       (when (listp (cdar alist))
615         (push (car alist) out))
616       (pop alist))
617     (nreverse out)))
618
619 (defvar mm-inhibit-file-name-handlers
620   '(jka-compr-handler image-file-handler)
621   "A list of handlers doing (un)compression (etc) thingies.")
622
623 (defun mm-insert-file-contents (filename &optional visit beg end replace
624                                          inhibit)
625   "Like `insert-file-contents', q.v., but only reads in the file.
626 A buffer may be modified in several ways after reading into the buffer due
627 to advanced Emacs features, such as file-name-handlers, format decoding,
628 find-file-hooks, etc.
629 If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers.
630   This function ensures that none of these modifications will take place."
631   (let ((format-alist nil)
632         (auto-mode-alist (if inhibit nil (mm-auto-mode-alist)))
633         (default-major-mode 'fundamental-mode)
634         (enable-local-variables nil)
635         (after-insert-file-functions nil)
636         (enable-local-eval nil)
637         (find-file-hooks nil)
638         (inhibit-file-name-operation (if inhibit
639                                          'insert-file-contents
640                                        inhibit-file-name-operation))
641         (inhibit-file-name-handlers
642          (if inhibit
643              (append mm-inhibit-file-name-handlers
644                      inhibit-file-name-handlers)
645            inhibit-file-name-handlers)))
646     (insert-file-contents filename visit beg end replace)))
647
648 (defun mm-append-to-file (start end filename &optional codesys inhibit)
649   "Append the contents of the region to the end of file FILENAME.
650 When called from a function, expects three arguments,
651 START, END and FILENAME.  START and END are buffer positions
652 saying what text to write.
653 Optional fourth argument specifies the coding system to use when
654 encoding the file.
655 If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers."
656   (let ((coding-system-for-write
657          (or codesys mm-text-coding-system-for-write
658              mm-text-coding-system))
659         (inhibit-file-name-operation (if inhibit
660                                          'append-to-file
661                                        inhibit-file-name-operation))
662         (inhibit-file-name-handlers
663          (if inhibit
664              (append mm-inhibit-file-name-handlers
665                      inhibit-file-name-handlers)
666            inhibit-file-name-handlers)))
667     (append-to-file start end filename)))
668
669 (defun mm-write-region (start end filename &optional append visit lockname
670                               coding-system inhibit)
671
672   "Like `write-region'.
673 If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers."
674   (let ((coding-system-for-write
675          (or coding-system mm-text-coding-system-for-write
676              mm-text-coding-system))
677         (inhibit-file-name-operation (if inhibit
678                                          'write-region
679                                        inhibit-file-name-operation))
680         (inhibit-file-name-handlers
681          (if inhibit
682              (append mm-inhibit-file-name-handlers
683                      inhibit-file-name-handlers)
684            inhibit-file-name-handlers)))
685     (write-region start end filename append visit lockname)))
686
687 (defun mm-image-load-path (&optional package)
688   (let (dir result)
689     (dolist (path load-path (nreverse result))
690       (if (file-directory-p
691            (setq dir (concat (file-name-directory
692                               (directory-file-name path))
693                              "etc/" (or package "gnus/"))))
694           (push dir result))
695       (push path result))))
696
697 (provide 'mm-util)
698
699 ;;; mm-util.el ends here