2001-11-01 18:00:00 ShengHuo ZHU <zsh@cs.rochester.edu>
[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, -9 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 
250   '((iso-8859-1 "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE")
251     (iso-8859-9 "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE\xD0\xDD\xDE\xF0\xFD\xFE"))
252   "Coding systems (inconvertible characters) to ISO-8859-15.")
253
254 (defvar mm-iso-8859-x-to-15-table
255   (and (fboundp 'coding-system-p)
256        (mm-coding-system-p 'iso-8859-15)
257        (mapcar 
258         (lambda (cs)
259           (if (mm-coding-system-p (car cs))
260               (let ((c (string-to-char 
261                         (decode-coding-string "\341" (car cs)))))
262                 (cons (char-charset c)
263                       (cons
264                        (- (string-to-char 
265                            (decode-coding-string "\341" 'iso-8859-15)) c)
266                        (string-to-list (decode-coding-string (car (cdr cs)) 
267                                                              (car cs))))))
268             '(gnus-charset 0)))
269         mm-iso-8859-15-compatible))
270   "A table of the difference character between ISO-8859-X and ISO-8859-15.")
271
272 ;;; Internal variables:
273
274 ;;; Functions:
275
276 (defun mm-mule-charset-to-mime-charset (charset)
277   "Return the MIME charset corresponding to the given Mule CHARSET."
278   (if (fboundp 'find-coding-systems-for-charsets)
279       (let (mime)
280         (dolist (cs (find-coding-systems-for-charsets (list charset)))
281           (unless mime
282             (when cs
283               (setq mime (coding-system-get cs 'mime-charset)))))
284         mime)
285     (let ((alist mm-mime-mule-charset-alist)
286           out)
287       (while alist
288         (when (memq charset (cdar alist))
289           (setq out (caar alist)
290                 alist nil))
291         (pop alist))
292       out)))
293
294 (defun mm-charset-to-coding-system (charset &optional lbt)
295   "Return coding-system corresponding to CHARSET.
296 CHARSET is a symbol naming a MIME charset.
297 If optional argument LBT (`unix', `dos' or `mac') is specified, it is
298 used as the line break code type of the coding system."
299   (when (stringp charset)
300     (setq charset (intern (downcase charset))))
301   (when lbt
302     (setq charset (intern (format "%s-%s" charset lbt))))
303   (cond
304    ((null charset)
305     charset)
306    ;; Running in a non-MULE environment.
307    ((null (mm-get-coding-system-list))
308     charset)
309    ;; ascii
310    ((eq charset 'us-ascii)
311     'ascii)
312    ;; Check to see whether we can handle this charset.  (This depends
313    ;; on there being some coding system matching each `mime-charset'
314    ;; property defined, as there should be.)
315    ((and (mm-coding-system-p charset)
316 ;;; Doing this would potentially weed out incorrect charsets.
317 ;;;      charset
318 ;;;      (eq charset (coding-system-get charset 'mime-charset))
319          )
320     charset)
321    ;; Translate invalid charsets.
322    ((mm-coding-system-p (setq charset
323                            (cdr (assq charset
324                                       mm-charset-synonym-alist))))
325     charset)
326    ;; Last resort: search the coding system list for entries which
327    ;; have the right mime-charset in case the canonical name isn't
328    ;; defined (though it should be).
329    ((let (cs)
330       ;; mm-get-coding-system-list returns a list of cs without lbt.
331       ;; Do we need -lbt?
332       (dolist (c (mm-get-coding-system-list))
333         (if (and (null cs)
334                  (eq charset (coding-system-get c 'mime-charset)))
335             (setq cs c)))
336       cs))))
337
338 (defsubst mm-replace-chars-in-string (string from to)
339   (mm-subst-char-in-string from to string))
340
341 (eval-and-compile
342   (defvar mm-emacs-mule (and (not (featurep 'xemacs))
343                              (boundp 'default-enable-multibyte-characters)
344                              default-enable-multibyte-characters
345                              (fboundp 'set-buffer-multibyte))
346     "Emacs mule.")
347   
348   (defvar mm-mule4-p (and mm-emacs-mule
349                           (fboundp 'charsetp)
350                           (not (charsetp 'eight-bit-control)))
351     "Mule version 4.")
352
353   (if mm-emacs-mule
354       (defun mm-enable-multibyte ()
355         "Set the multibyte flag of the current buffer.
356 Only do this if the default value of `enable-multibyte-characters' is
357 non-nil.  This is a no-op in XEmacs."
358         (set-buffer-multibyte t))
359     (defalias 'mm-enable-multibyte 'ignore))
360
361   (if mm-emacs-mule
362       (defun mm-disable-multibyte ()
363         "Unset the multibyte flag of in the current buffer.
364 This is a no-op in XEmacs."
365         (set-buffer-multibyte nil))
366     (defalias 'mm-disable-multibyte 'ignore))
367
368   (if mm-mule4-p
369       (defun mm-enable-multibyte-mule4  ()
370         "Enable multibyte in the current buffer.
371 Only used in Emacs Mule 4."
372         (set-buffer-multibyte t))
373     (defalias 'mm-enable-multibyte-mule4 'ignore))
374   
375   (if mm-mule4-p
376       (defun mm-disable-multibyte-mule4 ()
377         "Disable multibyte in the current buffer.
378 Only used in Emacs Mule 4."
379         (set-buffer-multibyte nil))
380     (defalias 'mm-disable-multibyte-mule4 'ignore)))
381
382 (defun mm-preferred-coding-system (charset)
383   ;; A typo in some Emacs versions.
384   (or (get-charset-property charset 'prefered-coding-system)
385       (get-charset-property charset 'preferred-coding-system)))
386
387 (defun mm-charset-after (&optional pos)
388   "Return charset of a character in current buffer at position POS.
389 If POS is nil, it defauls to the current point.
390 If POS is out of range, the value is nil.
391 If the charset is `composition', return the actual one."
392   (let ((char (char-after pos)) charset)
393     (if (< (mm-char-int char) 128)
394         (setq charset 'ascii)
395       ;; charset-after is fake in some Emacsen.
396       (setq charset (and (fboundp 'char-charset) (char-charset char)))
397       (if (eq charset 'composition)
398           (let ((p (or pos (point))))
399             (cadr (find-charset-region p (1+ p))))
400         (if (and charset (not (memq charset '(ascii eight-bit-control
401                                                     eight-bit-graphic))))
402             charset
403           (or
404            mail-parse-mule-charset ;; cached mule-charset
405            (progn
406              (setq mail-parse-mule-charset
407                    (and (boundp 'current-language-environment)
408                         (car (last
409                               (assq 'charset
410                                     (assoc current-language-environment
411                                            language-info-alist))))))
412              (if (or (not mail-parse-mule-charset)
413                      (eq mail-parse-mule-charset 'ascii))
414                  (setq mail-parse-mule-charset
415                        (or (car (last (assq mail-parse-charset
416                                             mm-mime-mule-charset-alist)))
417                            ;; Fixme: don't fix that!
418                            'latin-iso8859-1)))
419              mail-parse-mule-charset)))))))
420
421 (defun mm-mime-charset (charset)
422   "Return the MIME charset corresponding to the given Mule CHARSET."
423   (if (eq charset 'unknown)
424       (error "The message contains non-printable characters, please use attachment"))
425   (if (and (fboundp 'coding-system-get) (fboundp 'get-charset-property))
426       ;; This exists in Emacs 20.
427       (or
428        (and (mm-preferred-coding-system charset)
429             (coding-system-get
430              (mm-preferred-coding-system charset) 'mime-charset))
431        (and (eq charset 'ascii)
432             'us-ascii)
433        (mm-preferred-coding-system charset)
434        (mm-mule-charset-to-mime-charset charset))
435     ;; This is for XEmacs.
436     (mm-mule-charset-to-mime-charset charset)))
437
438 (defun mm-delete-duplicates (list)
439   "Simple  substitute for CL `delete-duplicates', testing with `equal'."
440   (let (result head)
441     (while list
442       (setq head (car list))
443       (setq list (delete head list))
444       (setq result (cons head result)))
445     (nreverse result)))
446
447 ;; It's not clear whether this is supposed to mean the global or local
448 ;; setting.  I think it's used inconsistently.  -- fx
449 (defsubst mm-multibyte-p ()
450   "Say whether multibyte is enabled."
451   (if (and (not (featurep 'xemacs))
452            (boundp 'enable-multibyte-characters))
453       enable-multibyte-characters
454     (featurep 'mule)))
455
456 (defun mm-iso-8859-x-to-15-region (&optional b e)
457   (if (fboundp 'char-charset)
458       (let (charset item c inconvertible)
459         (save-restriction
460           (if e (narrow-to-region b e))
461           (goto-char (point-min))
462           (skip-chars-forward "\0-\177")
463           (while (not (eobp))
464             (cond 
465              ((not (setq item (assq (char-charset (setq c (char-after))) 
466                                     mm-iso-8859-x-to-15-table)))
467               (forward-char))
468              ((memq c (cdr (cdr item)))
469               (setq inconvertible t)
470               (forward-char))
471              (t
472               (insert (prog1 (+ c (car (cdr item))) (delete-char 1))))
473             (skip-chars-forward "\0-\177"))))
474         (not inconvertible))))
475
476 (defun mm-find-mime-charset-region (b e &optional hack-charsets)
477   "Return the MIME charsets needed to encode the region between B and E.
478 Nil means ASCII, a single-element list represents an appropriate MIME
479 charset, and a longer list means no appropriate charset."
480   (let (charsets)
481     ;; The return possibilities of this function are a mess...
482     (or (and (mm-multibyte-p)
483              (fboundp 'find-coding-systems-region)
484              ;; Find the mime-charset of the most preferred coding
485              ;; system that has one.
486              (let ((systems (find-coding-systems-region b e)))
487                ;; Fixme: The `mime-charset' (`x-ctext') of `compound-text'
488                ;; is not in the IANA list.
489                (setq systems (delq 'compound-text systems))
490                (unless (equal systems '(undecided))
491                  (while systems
492                    (let ((cs (coding-system-get (pop systems) 'mime-charset)))
493                      (if cs
494                          (setq systems nil
495                                charsets (list cs))))))
496                charsets))
497         ;; Otherwise we're not multibyte, XEmacs or a single coding
498         ;; system won't cover it.
499         (setq charsets 
500               (mm-delete-duplicates
501                (mapcar 'mm-mime-charset
502                        (delq 'ascii
503                              (mm-find-charset-region b e))))))
504     (if (and (memq 'iso-8859-15 charsets)
505              (memq 'iso-8859-15 hack-charsets)
506              (save-excursion (mm-iso-8859-x-to-15-region b e)))
507         (mapcar (lambda (x) (setq charsets (delq (car x) charsets)))
508                 mm-iso-8859-15-compatible))
509     (if (and (memq 'iso-2022-jp-2 charsets)
510              (memq 'iso-2022-jp-2 hack-charsets))
511         (setq charsets (delq 'iso-2022-jp charsets)))
512     charsets))
513
514 (defmacro mm-with-unibyte-buffer (&rest forms)
515   "Create a temporary buffer, and evaluate FORMS there like `progn'.
516 Use unibyte mode for this."
517   `(let (default-enable-multibyte-characters)
518      (with-temp-buffer ,@forms)))
519 (put 'mm-with-unibyte-buffer 'lisp-indent-function 0)
520 (put 'mm-with-unibyte-buffer 'edebug-form-spec '(body))
521
522 (defmacro mm-with-unibyte-current-buffer (&rest forms)
523   "Evaluate FORMS with current current buffer temporarily made unibyte.
524 Also bind `default-enable-multibyte-characters' to nil.
525 Equivalent to `progn' in XEmacs"
526   (let ((multibyte (make-symbol "multibyte"))
527         (buffer (make-symbol "buffer")))
528     `(if mm-emacs-mule 
529          (let ((,multibyte enable-multibyte-characters)
530                (,buffer (current-buffer)))
531            (unwind-protect
532                (let (default-enable-multibyte-characters)
533                  (set-buffer-multibyte nil)
534                  ,@forms)
535              (set-buffer ,buffer)
536              (set-buffer-multibyte ,multibyte)))
537        (let (default-enable-multibyte-characters)
538          ,@forms))))
539 (put 'mm-with-unibyte-current-buffer 'lisp-indent-function 0)
540 (put 'mm-with-unibyte-current-buffer 'edebug-form-spec '(body))
541
542 (defmacro mm-with-unibyte-current-buffer-mule4 (&rest forms)
543   "Evaluate FORMS there like `progn' in current buffer.
544 Mule4 only."
545   (let ((multibyte (make-symbol "multibyte"))
546         (buffer (make-symbol "buffer")))
547     `(if mm-mule4-p
548          (let ((,multibyte enable-multibyte-characters)
549                (,buffer (current-buffer)))
550            (unwind-protect
551                (let (default-enable-multibyte-characters)
552                  (set-buffer-multibyte nil)
553                  ,@forms)
554              (set-buffer ,buffer)
555              (set-buffer-multibyte ,multibyte)))
556        (let (default-enable-multibyte-characters)
557          ,@forms))))
558 (put 'mm-with-unibyte-current-buffer-mule4 'lisp-indent-function 0)
559 (put 'mm-with-unibyte-current-buffer-mule4 'edebug-form-spec '(body))
560
561 (defmacro mm-with-unibyte (&rest forms)
562   "Eval the FORMS with the default value of `enable-multibyte-characters' nil, ."
563   `(let (default-enable-multibyte-characters)
564      ,@forms))
565 (put 'mm-with-unibyte 'lisp-indent-function 0)
566 (put 'mm-with-unibyte 'edebug-form-spec '(body))
567
568 (defun mm-find-charset-region (b e)
569   "Return a list of Emacs charsets in the region B to E."
570   (cond
571    ((and (mm-multibyte-p)
572          (fboundp 'find-charset-region))
573     ;; Remove composition since the base charsets have been included.
574     ;; Remove eight-bit-*, treat them as ascii.
575     (let ((css (find-charset-region b e)))
576       (mapcar (lambda (cs) (setq css (delq cs css)))
577               '(composition eight-bit-control eight-bit-graphic
578                             control-1))
579       css))
580    (t
581     ;; We are in a unibyte buffer or XEmacs non-mule, so we futz around a bit.
582     (save-excursion
583       (save-restriction
584         (narrow-to-region b e)
585         (goto-char (point-min))
586         (skip-chars-forward "\0-\177")
587         (if (eobp)
588             '(ascii)
589           (let (charset)
590             (setq charset
591                   (and (boundp 'current-language-environment)
592                        (car (last (assq 'charset
593                                         (assoc current-language-environment
594                                                language-info-alist))))))
595             (if (eq charset 'ascii) (setq charset nil))
596             (or charset
597                 (setq charset
598                       (car (last (assq mail-parse-charset
599                                        mm-mime-mule-charset-alist)))))
600             (list 'ascii (or charset 'latin-iso8859-1)))))))))
601
602 (if (fboundp 'shell-quote-argument)
603     (defalias 'mm-quote-arg 'shell-quote-argument)
604   (defun mm-quote-arg (arg)
605     "Return a version of ARG that is safe to evaluate in a shell."
606     (let ((pos 0) new-pos accum)
607       ;; *** bug: we don't handle newline characters properly
608       (while (setq new-pos (string-match "[]*[;!'`\"$\\& \t{} |()<>]" arg pos))
609         (push (substring arg pos new-pos) accum)
610         (push "\\" accum)
611         (push (list (aref arg new-pos)) accum)
612         (setq pos (1+ new-pos)))
613       (if (= pos 0)
614           arg
615         (apply 'concat (nconc (nreverse accum) (list (substring arg pos))))))))
616
617 (defun mm-auto-mode-alist ()
618   "Return an `auto-mode-alist' with only the .gz (etc) thingies."
619   (let ((alist auto-mode-alist)
620         out)
621     (while alist
622       (when (listp (cdar alist))
623         (push (car alist) out))
624       (pop alist))
625     (nreverse out)))
626
627 (defvar mm-inhibit-file-name-handlers
628   '(jka-compr-handler image-file-handler)
629   "A list of handlers doing (un)compression (etc) thingies.")
630
631 (defun mm-insert-file-contents (filename &optional visit beg end replace
632                                          inhibit)
633   "Like `insert-file-contents', q.v., but only reads in the file.
634 A buffer may be modified in several ways after reading into the buffer due
635 to advanced Emacs features, such as file-name-handlers, format decoding,
636 find-file-hooks, etc.
637 If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers.
638   This function ensures that none of these modifications will take place."
639   (let ((format-alist nil)
640         (auto-mode-alist (if inhibit nil (mm-auto-mode-alist)))
641         (default-major-mode 'fundamental-mode)
642         (enable-local-variables nil)
643         (after-insert-file-functions nil)
644         (enable-local-eval nil)
645         (find-file-hooks nil)
646         (inhibit-file-name-operation (if inhibit
647                                          'insert-file-contents
648                                        inhibit-file-name-operation))
649         (inhibit-file-name-handlers
650          (if inhibit
651              (append mm-inhibit-file-name-handlers
652                      inhibit-file-name-handlers)
653            inhibit-file-name-handlers)))
654     (insert-file-contents filename visit beg end replace)))
655
656 (defun mm-append-to-file (start end filename &optional codesys inhibit)
657   "Append the contents of the region to the end of file FILENAME.
658 When called from a function, expects three arguments,
659 START, END and FILENAME.  START and END are buffer positions
660 saying what text to write.
661 Optional fourth argument specifies the coding system to use when
662 encoding the file.
663 If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers."
664   (let ((coding-system-for-write
665          (or codesys mm-text-coding-system-for-write
666              mm-text-coding-system))
667         (inhibit-file-name-operation (if inhibit
668                                          'append-to-file
669                                        inhibit-file-name-operation))
670         (inhibit-file-name-handlers
671          (if inhibit
672              (append mm-inhibit-file-name-handlers
673                      inhibit-file-name-handlers)
674            inhibit-file-name-handlers)))
675     (append-to-file start end filename)))
676
677 (defun mm-write-region (start end filename &optional append visit lockname
678                               coding-system inhibit)
679
680   "Like `write-region'.
681 If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers."
682   (let ((coding-system-for-write
683          (or coding-system mm-text-coding-system-for-write
684              mm-text-coding-system))
685         (inhibit-file-name-operation (if inhibit
686                                          'write-region
687                                        inhibit-file-name-operation))
688         (inhibit-file-name-handlers
689          (if inhibit
690              (append mm-inhibit-file-name-handlers
691                      inhibit-file-name-handlers)
692            inhibit-file-name-handlers)))
693     (write-region start end filename append visit lockname)))
694
695 (defun mm-image-load-path (&optional package)
696   (let (dir result)
697     (dolist (path load-path (nreverse result))
698       (if (file-directory-p
699            (setq dir (concat (file-name-directory
700                               (directory-file-name path))
701                              "etc/" (or package "gnus/"))))
702           (push dir result))
703       (push path result))))
704
705 (provide 'mm-util)
706
707 ;;; mm-util.el ends here