summaryrefslogtreecommitdiff
path: root/lisp/international
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/international')
-rw-r--r--lisp/international/characters.el16
-rw-r--r--lisp/international/kinsoku.el6
-rw-r--r--lisp/international/mule-cmds.el45
-rw-r--r--lisp/international/mule-diag.el9
-rw-r--r--lisp/international/mule.el2
-rw-r--r--lisp/international/quail.el10
-rw-r--r--lisp/international/rfc1843.el131
-rw-r--r--lisp/international/ucs-normalize.el45
-rw-r--r--lisp/international/utf7.el236
9 files changed, 446 insertions, 54 deletions
diff --git a/lisp/international/characters.el b/lisp/international/characters.el
index ab058a5df52..5085e637e39 100644
--- a/lisp/international/characters.el
+++ b/lisp/international/characters.el
@@ -194,6 +194,7 @@ with L, LRE, or LRO Unicode bidi character type.")
(dolist (l '(katakana-jisx0201 japanese-jisx0208 japanese-jisx0212
japanese-jisx0213-1 japanese-jisx0213-2
+ japanese-jisx0213.2004-1
cp932-2-byte))
(map-charset-chars #'modify-category-entry l ?j))
@@ -622,16 +623,19 @@ with L, LRE, or LRO Unicode bidi character type.")
(set-case-syntax-pair ?Ʊ ?ʊ tbl)
(set-case-syntax-pair ?Ʋ ?ʋ tbl)
(set-case-syntax-pair ?Ʒ ?ʒ tbl)
+ ;; We use set-downcase-syntax below, since we want upcase of dž
+ ;; return DŽ, not Dž, and the same for the rest.
(set-case-syntax-pair ?DŽ ?dž tbl)
- (set-case-syntax-pair ?Dž ?dž tbl)
+ (set-downcase-syntax ?Dž ?dž tbl)
(set-case-syntax-pair ?LJ ?lj tbl)
- (set-case-syntax-pair ?Lj ?lj tbl)
+ (set-downcase-syntax ?Lj ?lj tbl)
(set-case-syntax-pair ?NJ ?nj tbl)
- (set-case-syntax-pair ?Nj ?nj tbl)
+ (set-downcase-syntax ?Nj ?nj tbl)
;; 01F0; F; 006A 030C; # LATIN SMALL LETTER J WITH CARON
+
(set-case-syntax-pair ?DZ ?dz tbl)
- (set-case-syntax-pair ?Dz ?dz tbl)
+ (set-downcase-syntax ?Dz ?dz tbl)
(set-case-syntax-pair ?Ƕ ?ƕ tbl)
(set-case-syntax-pair ?Ƿ ?ƿ tbl)
(set-case-syntax-pair ?Ⱥ ?ⱥ tbl)
@@ -1382,10 +1386,10 @@ Setup char-width-table appropriate for non-CJK language environment."
(aset char-acronym-table i (car c0-acronyms))
(setq c0-acronyms (cdr c0-acronyms))))
-(let ((c1-acronyms '("XXX" "XXX" "BPH" "NBH" "IND" "NEL" "SSA" "ESA"
+(let ((c1-acronyms '("PAD" "HOP" "BPH" "NBH" "IND" "NEL" "SSA" "ESA"
"HTS" "HTJ" "VTS" "PLD" "PLU" "R1" "SS2" "SS1"
"DCS" "PU1" "PU2" "STS" "CCH" "MW" "SPA" "EPA"
- "SOS" "XXX" "SC1" "CSI" "ST" "OSC" "PM" "APC")))
+ "SOS" "SGCI" "SC1" "CSI" "ST" "OSC" "PM" "APC")))
(dotimes (i 32)
(aset char-acronym-table (+ #x0080 i) (car c1-acronyms))
(setq c1-acronyms (cdr c1-acronyms))))
diff --git a/lisp/international/kinsoku.el b/lisp/international/kinsoku.el
index e9609f493b7..f5824d486bf 100644
--- a/lisp/international/kinsoku.el
+++ b/lisp/international/kinsoku.el
@@ -104,10 +104,10 @@ The value 0 means there's no limitation.")
;; JISX0201 Katakana
"(I"(B"
;; Japanese JISX0208
- "$B!F!H!J!L!N!P!R!T!V!X!Z!k!l!m!n!w!x(B\
-$A!.!0#"#(!2!4!6!8!:!<!>!c!d!e#@!f!l(B"
+ "$B!F!H!J!L!N!P!R!T!V!X!Z!k!l!m!n!w!x(B"
;; Chinese GB2312
- "$A(E(F(G(H(I(J(K(L(M(N(O(P(Q(R(S(T(U(V(W(X(Y(h(B\
+ "$A!.!0#"#(!2!4!6!8!:!<!>!c!d!e#@!f!l(B\
+$A(E(F(G(H(I(J(K(L(M(N(O(P(Q(R(S(T(U(V(W(X(Y(h(B\
\$(0!>!@!B!D!F!H!J!L!N!P!R!T!V!X!Z!\!^!`!b(B"
;; Chinese BIG5
"$(0!d!f!h!j!k!q!p"i"j"k"n"x$u$v$w$x$y$z${(B\
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index 1ec7456c9e1..7672edc0443 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -2235,7 +2235,7 @@ See `set-language-info-alist' for use in programs."
("br" . "Latin-1") ; Breton
("bs" . "Latin-2") ; Bosnian
("byn" . "UTF-8") ; Bilin; Blin
- ("ca" . "Latin-1") ; Catalan
+ ("ca" "Catalan" iso-8859-1) ; Catalan
; co Corsican
("cs" "Czech" iso-8859-2)
("cy" "Welsh" iso-8859-14)
@@ -2980,6 +2980,27 @@ on encoding."
(let ((char (assoc name ucs-names)))
(when char (format " (%c)" (cdr char)))))
+(defun char-from-name (string &optional ignore-case)
+ "Return a character as a number from its Unicode name STRING.
+If optional IGNORE-CASE is non-nil, ignore case in STRING.
+Return nil if STRING does not name a character."
+ (or (cdr (assoc-string string (ucs-names) ignore-case))
+ (let ((minus (string-match-p "-[0-9A-F]+\\'" string)))
+ (when minus
+ ;; Parse names like "VARIATION SELECTOR-17" and "CJK
+ ;; COMPATIBILITY IDEOGRAPH-F900" that are not in ucs-names.
+ (ignore-errors
+ (let* ((case-fold-search ignore-case)
+ (vs (string-match-p "\\`VARIATION SELECTOR-" string))
+ (minus-num (string-to-number (substring string minus)
+ (if vs 10 16)))
+ (vs-offset (if vs (if (< minus-num -16) #xE00EF #xFDFF) 0))
+ (code (- vs-offset minus-num))
+ (name (get-char-code-property code 'name)))
+ (when (eq t (compare-strings string nil nil name nil nil
+ ignore-case))
+ code)))))))
+
(defun read-char-by-name (prompt)
"Read a character by its Unicode name or hex number string.
Display PROMPT and read a string that represents a character by its
@@ -2993,9 +3014,11 @@ preceded by an asterisk `*' and use completion, it will show all
the characters whose names include that substring, not necessarily
at the beginning of the name.
-This function also accepts a hexadecimal number of Unicode code
-point or a number in hash notation, e.g. #o21430 for octal,
-#x2318 for hex, or #10r8984 for decimal."
+Accept a name like \"CIRCULATION FUNCTION\", a hexadecimal
+number like \"2A10\", or a number in hash notation (e.g.,
+\"#x2a10\" for hex, \"10r10768\" for decimal, or \"#o25020\" for
+octal). Treat otherwise-ambiguous strings like \"BED\" (U+1F6CF)
+as names, not numbers."
(let* ((enable-recursive-minibuffers t)
(completion-ignore-case t)
(input
@@ -3008,13 +3031,13 @@ point or a number in hash notation, e.g. #o21430 for octal,
(category . unicode-name))
(complete-with-action action (ucs-names) string pred)))))
(char
- (cond
- ((string-match-p "\\`[0-9a-fA-F]+\\'" input)
- (string-to-number input 16))
- ((string-match-p "\\`#" input)
- (read input))
- (t
- (cdr (assoc-string input (ucs-names) t))))))
+ (cond
+ ((char-from-name input t))
+ ((string-match-p "\\`[0-9a-fA-F]+\\'" input)
+ (ignore-errors (string-to-number input 16)))
+ ((string-match-p "\\`#\\([bBoOxX]\\|[0-9]+[rR]\\)[0-9a-zA-Z]+\\'"
+ input)
+ (ignore-errors (read input))))))
(unless (characterp char)
(error "Invalid character"))
char))
diff --git a/lisp/international/mule-diag.el b/lisp/international/mule-diag.el
index fbb0e0cb96f..f543083b8c5 100644
--- a/lisp/international/mule-diag.el
+++ b/lisp/international/mule-diag.el
@@ -204,13 +204,6 @@ Character sets for defining other charsets, or for backward compatibility
"Obsolete.")
(make-obsolete-variable 'non-iso-charset-alist "no longer relevant." "23.1")
-(defun decode-codepage-char (codepage code)
- "Decode a character that has code CODE in CODEPAGE.
-Return a decoded character string. Each CODEPAGE corresponds to a
-coding system cpCODEPAGE."
- (declare (obsolete decode-char "23.1"))
- (decode-char (intern (format "cp%d" codepage)) code))
-
;; A variable to hold charset input history.
(defvar charset-history nil)
@@ -1121,7 +1114,7 @@ system which uses fontsets)."
(insert "\n\n")
(if window-system
- (let ((font (cdr (assq 'font (frame-parameters)))))
+ (let ((font (frame-parameter nil 'font)))
(insert "The font and fontset of the selected frame are:\n"
" font: " font "\n"
" fontset: " (face-attribute 'default :fontset) "\n"))
diff --git a/lisp/international/mule.el b/lisp/international/mule.el
index 08d37b45a3d..0761e688684 100644
--- a/lisp/international/mule.el
+++ b/lisp/international/mule.el
@@ -1873,7 +1873,7 @@ files.")
(defun auto-coding-alist-lookup (filename)
"Return the coding system specified by `auto-coding-alist' for FILENAME."
(let ((alist auto-coding-alist)
- (case-fold-search (memq system-type '(windows-nt ms-dos cygwin)))
+ (case-fold-search (file-name-case-insensitive-p filename))
coding-system)
(while (and alist (not coding-system))
(if (string-match (car (car alist)) filename)
diff --git a/lisp/international/quail.el b/lisp/international/quail.el
index f5e390278ca..320d783d410 100644
--- a/lisp/international/quail.el
+++ b/lisp/international/quail.el
@@ -1333,7 +1333,15 @@ If STR has `advice' text property, append the following special event:
(defun quail-input-method (key)
(if (or buffer-read-only
- overriding-terminal-local-map
+ (and overriding-terminal-local-map
+ ;; If the overriding map is `universal-argument-map', that
+ ;; must mean the user has pressed 'C-u KEY'. If KEY has a
+ ;; binding in `universal-argument-map' just return
+ ;; (list KEY), otherwise act as if there was no
+ ;; overriding map.
+ (or (not (eq (cadr overriding-terminal-local-map)
+ universal-argument-map))
+ (lookup-key overriding-terminal-local-map (vector key))))
overriding-local-map)
(list key)
(quail-setup-overlays (quail-conversion-keymap))
diff --git a/lisp/international/rfc1843.el b/lisp/international/rfc1843.el
new file mode 100644
index 00000000000..508629fb062
--- /dev/null
+++ b/lisp/international/rfc1843.el
@@ -0,0 +1,131 @@
+;;; rfc1843.el --- HZ (rfc1843) decoding
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
+;; Keywords: news HZ HZ+ mail i18n
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Test:
+;; (rfc1843-decode-string "~{<:Ky2;S{#,NpJ)l6HK!#~}")
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(defvar rfc1843-word-regexp
+ "~\\({\\([\041-\167][\041-\176]\\| \\)+\\)\\(~}\\|$\\)")
+
+(defvar rfc1843-word-regexp-strictly
+ "~\\({\\([\041-\167][\041-\176]\\)+\\)\\(~}\\|$\\)")
+
+(defvar rfc1843-hzp-word-regexp
+ "~\\({\\([\041-\167][\041-\176]\\| \\)+\\|\
+[<>]\\([\041-\175][\041-\176]\\| \\)+\\)\\(~}\\|$\\)")
+
+(defvar rfc1843-hzp-word-regexp-strictly
+ "~\\({\\([\041-\167][\041-\176]\\)+\\|\
+[<>]\\([\041-\175][\041-\176]\\)+\\)\\(~}\\|$\\)")
+
+(defcustom rfc1843-decode-loosely nil
+ "Loosely check HZ encoding if non-nil.
+When it is set non-nil, only buffers or strings with strictly
+HZ-encoded are decoded."
+ :type 'boolean
+ :group 'mime)
+
+(defcustom rfc1843-decode-hzp t
+ "HZ+ decoding support if non-nil.
+HZ+ specification (also known as HZP) is to provide a standardized
+7-bit representation of mixed Big5, GB, and ASCII text for convenient
+e-mail transmission, news posting, etc.
+The document of HZ+ 0.78 specification can be found at
+ftp://ftp.math.psu.edu/pub/simpson/chinese/hzp/hzp.doc"
+ :type 'boolean
+ :group 'mime)
+
+(defcustom rfc1843-newsgroups-regexp "chinese\\|hz"
+ "Regexp of newsgroups in which might be HZ encoded."
+ :type 'string
+ :group 'mime)
+
+(defun rfc1843-decode-region (from to)
+ "Decode HZ in the region between FROM and TO."
+ (interactive "r")
+ (let (str firstc)
+ (save-excursion
+ (goto-char from)
+ (if (or rfc1843-decode-loosely
+ (re-search-forward (if rfc1843-decode-hzp
+ rfc1843-hzp-word-regexp-strictly
+ rfc1843-word-regexp-strictly) to t))
+ (save-restriction
+ (narrow-to-region from to)
+ (goto-char (point-min))
+ (while (re-search-forward (if rfc1843-decode-hzp
+ rfc1843-hzp-word-regexp
+ rfc1843-word-regexp) (point-max) t)
+ (setq str (buffer-substring-no-properties
+ (match-beginning 1)
+ (match-end 1)))
+ (setq firstc (aref str 0))
+ (insert (decode-coding-string
+ (rfc1843-decode
+ (prog1
+ (substring str 1)
+ (delete-region (match-beginning 0) (match-end 0)))
+ firstc)
+ (if (eq firstc ?{) 'cn-gb-2312 'cn-big5))))
+ (goto-char (point-min))
+ (while (search-forward "~" (point-max) t)
+ (cond ((eq (char-after) ?\n)
+ (delete-char -1)
+ (delete-char 1))
+ ((eq (char-after) ?~)
+ (delete-char 1)))))))))
+
+(defun rfc1843-decode-string (string)
+ "Decode HZ STRING and return the results."
+ (let ((m enable-multibyte-characters))
+ (with-temp-buffer
+ (when m
+ (set-buffer-multibyte 'to))
+ (insert string)
+ (inline
+ (rfc1843-decode-region (point-min) (point-max)))
+ (buffer-string))))
+
+(defun rfc1843-decode (word &optional firstc)
+ "Decode HZ WORD and return it."
+ (let ((i -1) (s (substring word 0)) v)
+ (if (or (not firstc) (eq firstc ?{))
+ (while (< (incf i) (length s))
+ (if (eq (setq v (aref s i)) ? ) nil
+ (aset s i (+ 128 v))))
+ (while (< (incf i) (length s))
+ (if (eq (setq v (aref s i)) ? ) nil
+ (setq v (+ (* 94 v) (aref s (1+ i)) -3135))
+ (aset s i (+ (/ v 157) (if (eq firstc ?<) 201 161)))
+ (setq v (% v 157))
+ (aset s (incf i) (+ v (if (< v 63) 64 98))))))
+ s))
+
+(provide 'rfc1843)
+
+;;; rfc1843.el ends here
diff --git a/lisp/international/ucs-normalize.el b/lisp/international/ucs-normalize.el
index 29cd042eee9..b2bc622858d 100644
--- a/lisp/international/ucs-normalize.el
+++ b/lisp/international/ucs-normalize.el
@@ -263,7 +263,7 @@ Note that Hangul are excluded.")
(defvar ucs-normalize-combining-chars-regexp nil
"Regular expression to match sequence of combining characters.")
(setq ucs-normalize-combining-chars-regexp
- (eval-when-compile (concat (regexp-opt (mapcar 'char-to-string combining-chars)) "+")))
+ (eval-when-compile (concat (regexp-opt-charset combining-chars) "+")))
(declare-function decomposition-translation-alist "ucs-normalize"
(decomposition-function))
@@ -396,20 +396,22 @@ If COMPOSITION-PREDICATE is not given, then do nothing."
It includes Singletons, CompositionExclusions, and Non-Starter
decomposition."
(let (entries decomposition composition)
- (mapc
- (lambda (start-end)
- (cl-do ((i (car start-end) (+ i 1))) ((> i (cdr start-end)))
- (setq decomposition
- (string-to-list
- (with-temp-buffer
- (insert i)
- (translate-region 1 2 decomposition-translation)
- (buffer-string))))
- (setq composition
- (ucs-normalize-block-compose-chars decomposition composition-predicate))
- (when (not (equal composition (list i)))
- (setq entries (cons i entries)))))
- check-range)
+ (with-temp-buffer
+ (mapc
+ (lambda (start-end)
+ (cl-do ((i (car start-end) (+ i 1))) ((> i (cdr start-end)))
+ (setq decomposition
+ (string-to-list
+ (progn
+ (erase-buffer)
+ (insert i)
+ (translate-region 1 2 decomposition-translation)
+ (buffer-string))))
+ (setq composition
+ (ucs-normalize-block-compose-chars decomposition composition-predicate))
+ (when (not (equal composition (list i)))
+ (setq entries (cons i entries)))))
+ check-range))
;;(remove-duplicates
(append entries
ucs-normalize-composition-exclusions
@@ -431,7 +433,7 @@ decomposition."
(setq hfs-nfc-quick-check-list (quick-check-list 'ucs-normalize-hfs-nfd-table t ))
(defun quick-check-list-to-regexp (quick-check-list)
- (regexp-opt (mapcar 'char-to-string (append quick-check-list combining-chars))))
+ (regexp-opt-charset (append quick-check-list combining-chars)))
(defun quick-check-decomposition-list-to-regexp (quick-check-list)
(concat (quick-check-list-to-regexp quick-check-list) "\\|[가-힣]"))
@@ -613,14 +615,9 @@ COMPOSITION-PREDICATE will be used to compose region."
(- (point-max) (point-min)))))
;; Pre-write conversion for `utf-8-hfs'.
-(defun ucs-normalize-hfs-nfd-pre-write-conversion (from to)
- (let ((old-buf (current-buffer)))
- (set-buffer (generate-new-buffer " *temp*"))
- (if (stringp from)
- (insert from)
- (insert-buffer-substring old-buf from to))
- (ucs-normalize-HFS-NFD-region (point-min) (point-max))
- nil))
+;; _from and _to are legacy arguments (see `define-coding-system').
+(defun ucs-normalize-hfs-nfd-pre-write-conversion (_from _to)
+ (ucs-normalize-HFS-NFD-region (point-min) (point-max)))
;;; coding-system definition
(define-coding-system 'utf-8-hfs
diff --git a/lisp/international/utf7.el b/lisp/international/utf7.el
new file mode 100644
index 00000000000..bd04eba2fae
--- /dev/null
+++ b/lisp/international/utf7.el
@@ -0,0 +1,236 @@
+;;; utf7.el --- UTF-7 encoding/decoding for Emacs -*-coding: utf-8;-*-
+
+;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
+
+;; Author: Jon K Hellan <hellan@acm.org>
+;; Maintainer: bugs@gnus.org
+;; Keywords: mail
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; UTF-7 - A Mail-Safe Transformation Format of Unicode - RFC 2152
+;; This is a transformation format of Unicode that contains only 7-bit
+;; ASCII octets and is intended to be readable by humans in the limiting
+;; case that the document consists of characters from the US-ASCII
+;; repertoire.
+;; In short, runs of characters outside US-ASCII are encoded as base64
+;; inside delimiters.
+;; A variation of UTF-7 is specified in IMAP 4rev1 (RFC 2060) as the way
+;; to represent characters outside US-ASCII in mailbox names in IMAP.
+;; This library supports both variants, but the IMAP variation was the
+;; reason I wrote it.
+;; The routines convert UTF-7 -> UTF-16 (16 bit encoding of Unicode)
+;; -> current character set, and vice versa.
+;; However, until Emacs supports Unicode, the only Emacs character set
+;; supported here is ISO-8859.1, which can trivially be converted to/from
+;; Unicode.
+;; When decoding results in a character outside the Emacs character set,
+;; an error is thrown. It is up to the application to recover.
+
+;; UTF-7 should be done by providing a coding system. Mule-UCS does
+;; already, but I don't know if it does the IMAP version and it's not
+;; clear whether that should really be a coding system. The UTF-16
+;; part of the conversion can be done with coding systems available
+;; with Mule-UCS or some versions of Emacs. Unfortunately these were
+;; done wrongly (regarding handling of byte-order marks and how the
+;; variants were named), so we don't have a consistent name for the
+;; necessary coding system. The code below doesn't seem to DTRT
+;; generally. E.g.:
+;;
+;; (utf7-encode "a+£")
+;; => "a+ACsAow-"
+;;
+;; $ echo "a+£"|iconv -f utf-8 -t utf-7
+;; a+-+AKM
+;;
+;; -- fx
+
+
+;;; Code:
+
+(require 'base64)
+(eval-when-compile (require 'cl))
+(require 'mm-util)
+
+(defconst utf7-direct-encoding-chars " -%'-*,-[]-}"
+ "Character ranges which do not need escaping in UTF-7.")
+
+(defconst utf7-imap-direct-encoding-chars
+ (concat utf7-direct-encoding-chars "+\\~")
+ "Character ranges which do not need escaping in the IMAP variant of UTF-7.")
+
+(defconst utf7-utf-16-coding-system
+ (cond ((mm-coding-system-p 'utf-16-be-no-signature) ; Mule-UCS
+ 'utf-16-be-no-signature)
+ ((and (mm-coding-system-p 'utf-16-be) ; Emacs
+ ;; Avoid versions with BOM.
+ (= 2 (length (encode-coding-string "a" 'utf-16-be))))
+ 'utf-16-be)
+ ((mm-coding-system-p 'utf-16-be-nosig) ; ?
+ 'utf-16-be-nosig))
+ "Coding system which encodes big endian UTF-16 without a BOM signature.")
+
+(defsubst utf7-imap-get-pad-length (len modulus)
+ "Return required length of padding for IMAP modified base64 fragment."
+ (mod (- len) modulus))
+
+(defun utf7-encode-internal (&optional for-imap)
+ "Encode text in (temporary) buffer as UTF-7.
+Use IMAP modification if FOR-IMAP is non-nil."
+ (let ((start (point-min))
+ (end (point-max)))
+ (narrow-to-region start end)
+ (goto-char start)
+ (let* ((esc-char (if for-imap ?& ?+))
+ (direct-encoding-chars
+ (if for-imap utf7-imap-direct-encoding-chars
+ utf7-direct-encoding-chars))
+ (not-direct-encoding-chars (concat "^" direct-encoding-chars)))
+ (while (not (eobp))
+ (skip-chars-forward direct-encoding-chars)
+ (unless (eobp)
+ (insert esc-char)
+ (let ((p (point))
+ (fc (following-char))
+ (run-length
+ (skip-chars-forward not-direct-encoding-chars)))
+ (if (and (= fc esc-char)
+ (= run-length 1)) ; Lone esc-char?
+ (delete-char -1) ; Now there's one too many
+ (utf7-fragment-encode p (point) for-imap))
+ (insert "-")))))))
+
+(defun utf7-fragment-encode (start end &optional for-imap)
+ "Encode text from START to END in buffer as UTF-7 escape fragment.
+Use IMAP modification if FOR-IMAP is non-nil."
+ (save-restriction
+ (let* ((buf (current-buffer))
+ (base (with-temp-buffer
+ (set-buffer-multibyte nil)
+ (insert-buffer-substring buf start end)
+ (funcall (utf7-get-u16char-converter 'to-utf-16))
+ (base64-encode-region (point-min) (point-max))
+ (buffer-string))))
+ (narrow-to-region start end)
+ (delete-region (point-min) (point-max))
+ (insert base))
+ (goto-char (point-min))
+ (let ((pm (point-max)))
+ (when for-imap
+ (while (search-forward "/" nil t)
+ (replace-match ",")))
+ (skip-chars-forward "^= \t\n" pm)
+ (delete-region (point) pm))))
+
+(defun utf7-decode-internal (&optional for-imap)
+ "Decode UTF-7 text in (temporary) buffer.
+Use IMAP modification if FOR-IMAP is non-nil."
+ (let ((start (point-min))
+ (end (point-max)))
+ (goto-char start)
+ (let* ((esc-pattern (concat "^" (char-to-string (if for-imap ?& ?+))))
+ (base64-chars (concat "A-Za-z0-9+"
+ (char-to-string (if for-imap ?, ?/)))))
+ (while (not (eobp))
+ (skip-chars-forward esc-pattern)
+ (unless (eobp)
+ (forward-char)
+ (let ((p (point))
+ (run-length (skip-chars-forward base64-chars)))
+ (when (and (not (eobp)) (= (following-char) ?-))
+ (delete-char 1))
+ (unless (= run-length 0) ; Encoded lone esc-char?
+ (save-excursion
+ (utf7-fragment-decode p (point) for-imap)
+ (goto-char p)
+ (delete-char -1)))))))))
+
+(defun utf7-fragment-decode (start end &optional for-imap)
+ "Decode base64 encoded fragment from START to END of UTF-7 text in buffer.
+Use IMAP modification if FOR-IMAP is non-nil."
+ (save-restriction
+ (narrow-to-region start end)
+ (when for-imap
+ (goto-char start)
+ (while (search-forward "," nil 'move-to-end) (replace-match "/")))
+ (let ((pl (utf7-imap-get-pad-length (- end start) 4)))
+ (insert (make-string pl ?=))
+ (base64-decode-region start (+ end pl)))
+ (funcall (utf7-get-u16char-converter 'from-utf-16))))
+
+(defun utf7-get-u16char-converter (which-way)
+ "Return a function to convert between UTF-16 and current character set."
+ (if utf7-utf-16-coding-system
+ (if (eq which-way 'to-utf-16)
+ (lambda ()
+ (encode-coding-region (point-min) (point-max)
+ utf7-utf-16-coding-system))
+ (lambda ()
+ (decode-coding-region (point-min) (point-max)
+ utf7-utf-16-coding-system)))
+ ;; Add test to check if we are really Latin-1.
+ (if (eq which-way 'to-utf-16)
+ 'utf7-latin1-u16-char-converter
+ 'utf7-u16-latin1-char-converter)))
+
+(defun utf7-latin1-u16-char-converter ()
+ "Convert latin 1 (ISO-8859.1) characters to 16 bit Unicode.
+Characters are converted to raw byte pairs in narrowed buffer."
+ (encode-coding-region (point-min) (point-max) 'iso-8859-1)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (insert 0)
+ (forward-char)))
+
+(defun utf7-u16-latin1-char-converter ()
+ "Convert 16 bit Unicode characters to latin 1 (ISO-8859.1).
+Characters are in raw byte pairs in narrowed buffer."
+ (goto-char (point-min))
+ (while (not (eobp))
+ (if (= 0 (following-char))
+ (delete-char 1)
+ (error "Unable to convert from Unicode"))
+ (forward-char))
+ (decode-coding-region (point-min) (point-max) 'iso-8859-1)
+ (mm-enable-multibyte))
+
+;;;###autoload
+(defun utf7-encode (string &optional for-imap)
+ "Encode UTF-7 STRING. Use IMAP modification if FOR-IMAP is non-nil."
+ (if (and (coding-system-p 'utf-7) (coding-system-p 'utf-7-imap))
+ ;; Emacs 23 with proper support for IMAP
+ (encode-coding-string string (if for-imap 'utf-7-imap 'utf-7))
+ (mm-with-multibyte-buffer
+ (insert string)
+ (utf7-encode-internal for-imap)
+ (buffer-string))))
+
+(defun utf7-decode (string &optional for-imap)
+ "Decode UTF-7 STRING. Use IMAP modification if FOR-IMAP is non-nil."
+ (if (and (coding-system-p 'utf-7) (coding-system-p 'utf-7-imap))
+ ;; Emacs 23 with proper support for IMAP
+ (decode-coding-string string (if for-imap 'utf-7-imap 'utf-7))
+ (mm-with-unibyte-buffer
+ (insert string)
+ (utf7-decode-internal for-imap)
+ (mm-enable-multibyte)
+ (buffer-string))))
+
+(provide 'utf7)
+
+;;; utf7.el ends here