1;;; ethio-util.el --- utilities for Ethiopic -*- coding: iso-2022-7bit; -*-
2
3;; Copyright (C) 1997, 1998, 2002, 2003, 2004, 2005, 2006, 2007
4;;   Free Software Foundation, Inc.
5;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
6;;   National Institute of Advanced Industrial Science and Technology (AIST)
7;;   Registration Number H14PRO021
8
9;; Keywords: mule, multilingual, Ethiopic
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation; either version 2, or (at your option)
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs; see the file COPYING.  If not, write to the
25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26;; Boston, MA 02110-1301, USA.
27
28;; Author: TAKAHASHI Naoto <ntakahas@m17n.org>
29
30;;; Commentary:
31
32;;; Code:
33
34(defvar rmail-current-message)
35(defvar rmail-message-vector)
36
37;; Information for exiting Ethiopic environment.
38(defvar exit-ethiopic-environment-data nil)
39
40;;;###autoload
41(defun setup-ethiopic-environment-internal ()
42  (let ((key-bindings '((" " . ethio-insert-space)
43			([?\S- ] . ethio-insert-ethio-space)
44			([?\C-'] . ethio-gemination)
45
46			;; these old bindings conflict
47			;; with Emacs' binding policy
48
49			;; ([f2] . ethio-toggle-space)
50			;; ([S-f2] . ethio-replace-space) ; as requested
51			;; ([f3] . ethio-toggle-punctuation)
52			;; ([f4] . ethio-sera-to-fidel-buffer)
53			;; ([S-f4] . ethio-sera-to-fidel-region)
54			;; ([C-f4] . ethio-sera-to-fidel-mail-or-marker)
55			;; ([f5] . ethio-fidel-to-sera-buffer)
56			;; ([S-f5] . ethio-fidel-to-sera-region)
57			;; ([C-f5] . ethio-fidel-to-sera-mail-or-marker)
58			;; ([f6] . ethio-modify-vowel)
59			;; ([f7] . ethio-replace-space)
60			;; ([f8] . ethio-input-special-character)
61
62			;; this is the rewritten bindings
63
64			([f3] . ethio-fidel-to-sera-buffer)
65			([S-f3] . ethio-fidel-to-sera-region)
66			([C-f3] . ethio-fidel-to-sera-mail-or-marker)
67			([f4] . ethio-sera-to-fidel-buffer)
68			([S-f4] . ethio-sera-to-fidel-region)
69			([C-f4] . ethio-sera-to-fidel-mail-or-marker)
70			([S-f5] . ethio-toggle-punctuation)
71			([S-f6] . ethio-modify-vowel)
72			([S-f7] . ethio-replace-space)
73			([S-f8] . ethio-input-special-character)
74			([C-f9] . ethio-toggle-space)
75			([S-f9] . ethio-replace-space) ; as requested
76			))
77	kb)
78    (while key-bindings
79      (setq kb (car (car key-bindings)))
80      (setq exit-ethiopic-environment-data
81	    (cons (cons kb (global-key-binding kb))
82		  exit-ethiopic-environment-data))
83      (global-set-key kb (cdr (car key-bindings)))
84      (setq key-bindings (cdr key-bindings))))
85
86  (add-hook 'quail-activate-hook 'ethio-select-a-translation)
87  (add-hook 'find-file-hook 'ethio-find-file)
88  (add-hook 'write-file-functions 'ethio-write-file)
89  (add-hook 'after-save-hook 'ethio-find-file))
90
91(defun exit-ethiopic-environment ()
92  "Exit Ethiopic language environment."
93  (while exit-ethiopic-environment-data
94    (global-set-key (car (car exit-ethiopic-environment-data))
95		    (cdr (car exit-ethiopic-environment-data)))
96    (setq exit-ethiopic-environment-data
97	  (cdr exit-ethiopic-environment-data)))
98
99  (remove-hook 'quail-activate-hook 'ethio-select-a-translation)
100  (remove-hook 'find-file-hook 'ethio-find-file)
101  (remove-hook 'write-file-functions 'ethio-write-file)
102  (remove-hook 'after-save-hook 'ethio-find-file))
103
104;;
105;; ETHIOPIC UTILITY FUNCTIONS
106;;
107
108;; If the filename ends in ".sera", editing is done in fidel
109;; but file I/O is done in SERA.
110;;
111;; If the filename ends in ".java", editing is done in fidel
112;; but file I/O is done in the \uXXXX style, where XXXX is
113;; the Unicode codepoint for the Ethiopic character.
114;;
115;; If the filename ends in ".tex", editing is done in fidel
116;; but file I/O is done in EthioTeX format.
117;;
118;; To automatically convert Ethiopic text to SERA format when sending mail,
119;;   (add-hook 'mail-send-hook 'ethio-fidel-to-sera-mail)
120;;
121;; To automatically convert SERA format to Ethiopic when receiving mail,
122;;   (add-hook 'rmail-show-message-hook 'ethio-sera-to-fidel-mail)
123;;
124;; To automatically convert Ethiopic text to SERA format when posting news,
125;;   (add-hook 'news-inews-hook 'ethio-fidel-to-sera-mail)
126
127;;
128;; users' preference
129;;
130
131(defvar ethio-primary-language 'tigrigna
132  "*Symbol that defines the primary language in SERA --> FIDEL conversion.
133The value should be one of: `tigrigna', `amharic' or `english'.")
134
135(defvar ethio-secondary-language 'english
136  "*Symbol that defines the secondary language in SERA --> FIDEL conversion.
137The value should be one of: `tigrigna', `amharic' or `english'.")
138
139(defvar ethio-use-colon-for-colon nil
140  "*Non-nil means associate ASCII colon with Ethiopic colon.
141If nil, associate ASCII colon with Ethiopic word separator, i.e., two
142vertically stacked dots.  All SERA <--> FIDEL converters refer this
143variable.")
144
145(defvar ethio-use-three-dot-question nil
146  "*Non-nil means associate ASCII question mark with Ethiopic old style question mark (three vertically stacked dots).
147If nil, associate ASCII question mark with Ethiopic stylized question
148mark.  All SERA <--> FIDEL converters refer this variable.")
149
150(defvar ethio-quote-vowel-always nil
151  "*Non-nil means always put an apostrophe before an isolated vowel (except at word initial) in FIDEL --> SERA conversion.
152If nil, put an apostrophe only between a sixth-form consonant and an
153isolated vowel.")
154
155(defvar ethio-W-sixth-always nil
156  "*Non-nil means convert the Wu-form of a 12-form consonant to \"W'\" instead of \"Wu\" in FIDEL --> SERA conversion.")
157
158(defvar ethio-numeric-reduction 0
159  "*Degree of reduction in converting Ethiopic digits into Arabic digits.
160Should be 0, 1 or 2.
161For example, ({10}{9}{100}{80}{7}) is converted into:
162    `10`9`100`80`7  if `ethio-numeric-reduction' is 0,
163    `109100807	    if `ethio-numeric-reduction' is 1,
164    `10900807	    if `ethio-numeric-reduction' is 2.")
165
166(defvar ethio-implicit-period-conversion t
167  "*Non-nil means replacing the Ethiopic dot at the end of an Ethiopic sentence
168with an Ethiopic full stop.")
169
170(defvar ethio-java-save-lowercase nil
171  "*Non-nil means save Ethiopic characters in lowercase hex numbers to Java files.
172If nil, use uppercases.")
173
174;;
175;; SERA to FIDEL
176;;
177
178(defconst ethio-sera-to-fidel-table
179  [
180   nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
181   nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
182;;; SP
183   (" "
184    (?: (if ethio-use-colon-for-colon " $(3$l(B" "$(3$h(B")
185	(32 (if ethio-use-colon-for-colon " $(3$l(B " "$(3$h(B"))
186	(?- " $(3$m(B")
187	(?: " $(3$i(B")
188	(?| (if ethio-use-colon-for-colon " $(3$l(B|" " $(3$h(B|")
189	    (?: " $(3$o(B"))))
190
191;;; !   "   #   $   %   &    '
192   nil nil nil nil nil nil ("" (?' "$(3%s(B"))
193;;; (   )   *   +    ,      -               .
194   nil nil nil nil ("$(3$j(B") ("-" (?: "$(3$l(B")) ("$(3%u(B")
195;;;  /   0   1   2   3   4   5   6   7   8   9
196    nil nil nil nil nil nil nil nil nil nil nil
197;;; :
198   ((if ethio-use-colon-for-colon "$(3$l(B" "$(3$h(B")
199    (32 (if ethio-use-colon-for-colon "$(3$l(B " "$(3$h(B"))
200    (?- "$(3$m(B")
201    (?: "$(3$i(B")
202    (?| (if ethio-use-colon-for-colon "$(3$l(B|" "$(3$h(B|")
203	(?: "$(3$o(B")))
204;;;  ;      <              =    >
205   ("$(3$k(B") ("<" (?< "$(3%v(B")) nil (">" (?> "$(3%w(B"))
206;;; ?
207   ((if ethio-use-three-dot-question "$(3$n(B" "$(3%x(B"))
208;;; @
209    nil
210;;; A
211   ("$(3"f(B" (?2 "$(3#8(B"))
212;;; B
213   ("$(3"((B" (?e "$(3"#(B") (?u "$(3"$(B") (?i "$(3"%(B") (?a "$(3"&(B") (?E "$(3"'(B") (?o "$(3")(B")
214         (?W "$(3%b(B" (?e "$(3%2(B") (?u "$(3%b(B") (?i "$(3%B(B") (?a "$(3"*(B") (?E "$(3%R(B")))
215;;; C
216   ("$(3$4(B" (?e "$(3$/(B") (?u "$(3$0(B") (?i "$(3$1(B") (?a "$(3$2(B") (?E "$(3$3(B") (?o "$(3$5(B")
217         (?W "$(3$6(B" (?a "$(3$6(B")
218                  (?e "$(3$4%n(B") (?u "$(3$4%r(B") (?i "$(3$4%o(B") (?E "$(3$4%q(B")))
219;;; D
220   ("$(3#b(B" (?e "$(3#](B") (?u "$(3#^(B") (?i "$(3#_(B") (?a "$(3#`(B") (?E "$(3#a(B") (?o "$(3#c(B")
221         (?W "$(3#d(B" (?a "$(3#d(B")
222                  (?e "$(3#b%n(B") (?u "$(3#b%r(B") (?i "$(3#b%o(B") (?E "$(3#b%q(B")))
223;;; E
224   ("$(3"g(B" (?2 "$(3#9(B"))
225;;; F
226   ("$(3$T(B" (?e "$(3$O(B") (?u "$(3$P(B") (?i "$(3$Q(B") (?a "$(3$R(B") (?E "$(3$S(B") (?o "$(3$U(B")
227         (?W "$(3%d(B" (?e "$(3%4(B") (?u "$(3%d(B") (?i "$(3%D(B") (?a "$(3$V(B") (?E "$(3%T(B"))
228	 (?Y "$(3$a(B" (?a "$(3$a(B")))
229;;; G
230   ("$(3$$(B" (?e "$(3#}(B") (?u "$(3#~(B") (?i "$(3$!(B") (?a "$(3$"(B") (?E "$(3$#(B") (?o "$(3$%(B")
231         (?W "$(3%c(B" (?e "$(3%3(B") (?u "$(3%c(B") (?i "$(3%C(B") (?a "$(3$&(B") (?E "$(3%S(B")))
232;;; H
233   ("$(3!6(B" (?e "$(3!1(B") (?u "$(3!2(B") (?i "$(3!3(B") (?a "$(3!4(B") (?E "$(3!5(B") (?o "$(3!7(B")
234         (?W "$(3!8(B" (?a "$(3!8(B")
235                  (?e "$(3!6%n(B") (?u "$(3!6%r(B") (?i "$(3!6%o(B") (?E "$(3!6%q(B")))
236;;; I
237   ("$(3"h(B" (?2 "$(3#:(B"))
238;;; J
239   ("$(3#j(B" (?e "$(3#e(B") (?u "$(3#f(B") (?i "$(3#g(B") (?a "$(3#h(B") (?E "$(3#i(B") (?o "$(3#k(B")
240         (?W "$(3#l(B" (?a "$(3#l(B")
241	          (?e "$(3#j%n(B") (?u "$(3#j%r(B") (?i "$(3#j%o(B") (?E "$(3#j%q(B")))
242;;; K
243   ("$(3#"(B" (?e "$(3"{(B") (?u "$(3"|(B") (?i "$(3"}(B") (?a "$(3"~(B") (?E "$(3#!(B") (?o "$(3##(B")
244         (?W "$(3#*(B" (?e "$(3#%(B") (?u "$(3#*(B") (?i "$(3#'(B") (?a "$(3#((B") (?E "$(3#)(B")))
245;;; L
246   ("$(3!.(B" (?e "$(3!)(B") (?u "$(3!*(B") (?i "$(3!+(B") (?a "$(3!,(B") (?E "$(3!-(B") (?o "$(3!/(B")
247         (?W "$(3!0(B" (?a "$(3!0(B")
248                  (?e "$(3!.%n(B") (?u "$(3!.%r(B") (?i "$(3!.%o(B") (?E "$(3!.%q(B")))
249;;; M
250   ("$(3!>(B" (?e "$(3!9(B") (?u "$(3!:(B") (?i "$(3!;(B") (?a "$(3!<(B") (?E "$(3!=(B") (?o "$(3!?(B")
251         (?W "$(3%a(B" (?e "$(3%1(B") (?u "$(3%a(B") (?i "$(3%A(B") (?a "$(3!@(B") (?E "$(3%Q(B"))
252	 (?Y "$(3$_(B" (?a "$(3$_(B")))
253;;; N
254   ("$(3"`(B" (?e "$(3"[(B") (?u "$(3"\(B") (?i "$(3"](B") (?a "$(3"^(B") (?E "$(3"_(B") (?o "$(3"a(B")
255         (?W "$(3"b(B" (?a "$(3"b(B")
256                  (?e "$(3"`%n(B") (?u "$(3"`%r(B") (?i "$(3"`%o(B") (?E "$(3"`%q(B")))
257;;; O
258   ("$(3"i(B" (?2 "$(3#;(B"))
259;;; P
260   ("$(3$<(B" (?e "$(3$7(B") (?u "$(3$8(B") (?i "$(3$9(B") (?a "$(3$:(B") (?E "$(3$;(B") (?o "$(3$=(B")
261         (?W "$(3$>(B" (?a "$(3$>(B")
262                  (?e "$(3$<%n(B") (?u "$(3$<%r(B") (?i "$(3$<%o(B") (?E "$(3$<%q(B")))
263;;; Q
264   ("$(3!v(B" (?e "$(3!q(B") (?u "$(3!r(B") (?i "$(3!s(B") (?a "$(3!t(B") (?E "$(3!u(B") (?o "$(3!w(B")
265         (?W "$(3!~(B" (?e "$(3!y(B") (?u "$(3!~(B") (?i "$(3!{(B") (?a "$(3!|(B") (?E "$(3!}(B")))
266;;; R
267   ("$(3!N(B" (?e "$(3!I(B") (?u "$(3!J(B") (?i "$(3!K(B") (?a "$(3!L(B") (?E "$(3!M(B") (?o "$(3!O(B")
268         (?W "$(3!P(B" (?a "$(3!P(B")
269	          (?e "$(3!N%n(B") (?u "$(3!N%r(B") (?i "$(3!N%o(B") (?E "$(3!N%q(B"))
270         (?Y "$(3$`(B" (?a "$(3$`(B")))
271;;; S
272   ("$(3$D(B" (?e "$(3$?(B") (?u "$(3$@(B") (?i "$(3$A(B") (?a "$(3$B(B") (?E "$(3$C(B") (?o "$(3$E(B")
273         (?W "$(3$F(B" (?a "$(3$F(B")
274                  (?e "$(3$D%n(B") (?u "$(3$D%r(B") (?i "$(3$D%o(B") (?E "$(3$D%q(B"))
275	 (?2 "$(3$L(B"
276	     (?e "$(3$G(B") (?u "$(3$H(B") (?i "$(3$I(B") (?a "$(3$J(B") (?E "$(3$K(B") (?o "$(3$M(B")
277	     (?W "$(3$F(B" (?a "$(3$F(B")
278		 (?e "$(3$L%n(B") (?u "$(3$L%r(B") (?i "$(3$L%o(B") (?E "$(3$L%q(B"))))
279;;; T
280   ("$(3$,(B" (?e "$(3$'(B") (?u "$(3$((B") (?i "$(3$)(B") (?a "$(3$*(B") (?E "$(3$+(B") (?o "$(3$-(B")
281         (?W "$(3$.(B" (?a "$(3$.(B")
282	          (?e "$(3$,%n(B") (?u "$(3$,%r(B") (?i "$(3$,%o(B") (?E "$(3$,%q(B")))
283;;; U
284   ("$(3"d(B" (?2 "$(3#6(B"))
285;;; V
286   ("$(3"0(B" (?e "$(3"+(B") (?u "$(3",(B") (?i "$(3"-(B") (?a "$(3".(B") (?E "$(3"/(B") (?o "$(3"1(B")
287         (?W "$(3"2(B" (?a "$(3"2(B")
288	          (?e "$(3"0%n(B") (?u "$(3"0%r(B") (?i "$(3"0%o(B") (?E "$(3"0%q(B")))
289;;; W
290   ("$(3%r(B" (?e "$(3%n(B") (?u "$(3%r(B") (?i "$(3%o(B") (?a "$(3%p(B") (?E "$(3%q(B"))
291;;; X
292   ("$(3%N(B" (?e "$(3%I(B") (?u "$(3%J(B") (?i "$(3%K(B") (?a "$(3%L(B") (?E "$(3%M(B") (?o "$(3%O(B"))
293;;; Y
294   ("$(3#R(B" (?e "$(3#M(B") (?u "$(3#N(B") (?i "$(3#O(B") (?a "$(3#P(B") (?E "$(3#Q(B") (?o "$(3#S(B")
295         (?W "$(3#T(B" (?a "$(3#T(B")
296	          (?e "$(3#R%n(B") (?u "$(3#R%r(B") (?i "$(3#R%o(B") (?E "$(3#R%q(B")))
297;;; Z
298   ("$(3#J(B" (?e "$(3#E(B") (?u "$(3#F(B") (?i "$(3#G(B") (?a "$(3#H(B") (?E "$(3#I(B") (?o "$(3#K(B")
299         (?W "$(3#L(B" (?a "$(3#L(B")
300	          (?e "$(3#J%n(B") (?u "$(3#J%r(B") (?i "$(3#J%o(B") (?E "$(3#J%q(B")))
301;;; [   \   ]   ^   _
302   nil nil nil nil nil
303;;; `
304   (""
305    (?: "$(3$h(B")
306    (?? (if ethio-use-three-dot-question "$(3%x(B" "$(3$n(B"))
307    (?! "$(3%t(B")
308    (?e "$(3#5(B") (?u "$(3#6(B") (?U "$(3#6(B") (?i "$(3#7(B") (?a "$(3#8(B") (?A "$(3#8(B")
309        (?E "$(3#9(B") (?I "$(3#:(B") (?o "$(3#;(B") (?O "$(3#;(B")
310    (?g "$(3%^(B"
311        (?e "$(3%Y(B") (?u "$(3%Z(B") (?i "$(3%[(B") (?a "$(3%\(B") (?E "$(3%](B") (?o "$(3%_(B"))
312    (?h "$(3"H(B"
313        (?e "$(3"C(B") (?u "$(3"D(B") (?i "$(3"E(B") (?a "$(3"F(B") (?E "$(3"G(B") (?o "$(3"I(B")
314	          (?W "$(3"P(B" (?e "$(3"K(B") (?u "$(3"P(B") (?i "$(3"M(B") (?a "$(3"N(B") (?E "$(3"O(B")))
315    (?k "$(3%>(B"
316        (?e "$(3%9(B") (?u "$(3%:(B") (?i "$(3%;(B") (?a "$(3%<(B") (?E "$(3%=(B") (?o "$(3%?(B"))
317    (?s "$(3!F(B"
318        (?e "$(3!A(B") (?u "$(3!B(B") (?i "$(3!C(B") (?a "$(3!D(B") (?E "$(3!E(B") (?o "$(3!G(B")
319	(?W "$(3!H(B" (?a "$(3!H(B")
320		   (?e "$(3!F%n(B") (?u "$(3!F%r(B") (?i "$(3!F%o(B") (?E "$(3!F%q(B")))
321    (?S "$(3$L(B"
322	(?e "$(3$G(B") (?u "$(3$H(B") (?i "$(3$I(B") (?a "$(3$J(B") (?E "$(3$K(B") (?o "$(3$M(B")
323	(?W "$(3$F(B" (?a "$(3$F(B")
324	         (?e "$(3$L%n(B") (?u "$(3$L%r(B") (?i "$(3$L%o(B") (?E "$(3$L%q(B")))
325    (?q "$(3%.(B" (?e "$(3%)(B") (?u "$(3%*(B") (?i "$(3%+(B") (?a "$(3%,(B") (?E "$(3%-(B") (?o "$(3%/(B")))
326;;; a
327   ("$(3"f(B" (?2 "$(3#8(B"))
328;;; b
329   ("$(3"((B" (?e "$(3"#(B") (?u "$(3"$(B") (?i "$(3"%(B") (?a "$(3"&(B") (?E "$(3"'(B") (?o "$(3")(B")
330         (?W "$(3%b(B" (?e "$(3%2(B") (?u "$(3%b(B") (?i "$(3%B(B") (?a "$(3"*(B") (?E "$(3%R(B")))
331;;; c
332   ("$(3"@(B" (?e "$(3";(B") (?u "$(3"<(B") (?i "$(3"=(B") (?a "$(3">(B") (?E "$(3"?(B") (?o "$(3"A(B")
333         (?W "$(3"B(B" (?a "$(3"B(B")
334	          (?e "$(3"@%n(B") (?u "$(3"@%r(B") (?i "$(3"@%o(B") (?E "$(3"@%q(B")))
335;;; d
336   ("$(3#Z(B" (?e "$(3#U(B") (?u "$(3#V(B") (?i "$(3#W(B") (?a "$(3#X(B") (?E "$(3#Y(B") (?o "$(3#[(B")
337         (?W "$(3#\(B" (?a "$(3#\(B")
338	          (?e "$(3#Z%o(B") (?u "$(3#Z%r(B") (?i "$(3#Z%p(B") (?E "$(3#Z%q(B")))
339;;; e
340   ("$(3"c(B" (?2 "$(3#5(B") (?a "$(3"j(B"))
341;;; f
342   ("$(3$T(B" (?e "$(3$O(B") (?u "$(3$P(B") (?i "$(3$Q(B") (?a "$(3$R(B") (?E "$(3$S(B") (?o "$(3$U(B")
343         (?W "$(3%d(B" (?e "$(3%4(B") (?u "$(3%d(B") (?i "$(3%D(B") (?a "$(3$V(B") (?E "$(3%T(B"))
344	 (?Y "$(3$a(B" (?a "$(3$a(B")))
345;;; g
346   ("$(3#r(B" (?e "$(3#m(B") (?u "$(3#n(B") (?i "$(3#o(B") (?a "$(3#p(B") (?E "$(3#q(B") (?o "$(3#s(B")
347         (?W "$(3#z(B" (?e "$(3#u(B") (?u "$(3#z(B") (?i "$(3#w(B") (?a "$(3#x(B") (?E "$(3#y(B"))
348         (?2 "$(3%^(B" (?e "$(3%Y(B") (?u "$(3%Z(B") (?i "$(3%[(B") (?a "$(3%\(B") (?E "$(3%](B") (?o "$(3%_(B")))
349;;; h
350   ("$(3!&(B" (?e "$(3!!(B") (?u "$(3!"(B") (?i "$(3!#(B") (?a "$(3!$(B") (?E "$(3!%(B") (?o "$(3!'(B")
351         (?W "$(3"P(B" (?e "$(3"K(B") (?u "$(3"P(B") (?i "$(3"M(B") (?a "$(3"N(B") (?E "$(3"O(B"))
352	 (?2 "$(3"H(B" (?e "$(3"C(B") (?u "$(3"D(B") (?i "$(3"E(B") (?a "$(3"F(B") (?E "$(3"G(B") (?o "$(3"I(B")
353	          (?W "$(3"P(B" (?e "$(3"K(B") (?u "$(3"P(B") (?i "$(3"M(B") (?a "$(3"N(B") (?E "$(3"O(B"))))
354;;; i
355   ("$(3"e(B" (?2 "$(3#7(B"))
356;;; j
357   ("$(3#j(B" (?e "$(3#e(B") (?u "$(3#f(B") (?i "$(3#g(B") (?a "$(3#h(B") (?E "$(3#i(B") (?o "$(3#k(B")
358         (?W "$(3#l(B" (?a "$(3#l(B")
359	          (?e "$(3#j%n(B") (?u "$(3#j%r(B") (?i "$(3#j%o(B") (?E "$(3#j%q(B")))
360;;; k
361   ("$(3"p(B" (?e "$(3"k(B") (?u "$(3"l(B") (?i "$(3"m(B") (?a "$(3"n(B") (?E "$(3"o(B") (?o "$(3"q(B")
362         (?W "$(3"x(B" (?e "$(3"s(B") (?u "$(3"x(B") (?i "$(3"u(B") (?a "$(3"v(B") (?E "$(3"w(B"))
363	 (?2 "$(3%>(B" (?e "$(3%9(B") (?u "$(3%:(B") (?i "$(3%;(B") (?a "$(3%<(B") (?E "$(3%=(B") (?o "$(3%?(B")))
364;;; l
365   ("$(3!.(B" (?e "$(3!)(B") (?u "$(3!*(B") (?i "$(3!+(B") (?a "$(3!,(B") (?E "$(3!-(B") (?o "$(3!/(B")
366         (?W "$(3!0(B" (?a "$(3!0(B")
367                  (?e "$(3!.%n(B") (?u "$(3!.%r(B") (?i "$(3!.%o(B") (?E "$(3!.%q(B")))
368;;; m
369   ("$(3!>(B" (?e "$(3!9(B") (?u "$(3!:(B") (?i "$(3!;(B") (?a "$(3!<(B") (?E "$(3!=(B") (?o "$(3!?(B")
370         (?W "$(3%a(B" (?e "$(3%1(B") (?u "$(3%a(B") (?i "$(3%A(B") (?a "$(3!@(B") (?E "$(3%Q(B"))
371	 (?Y "$(3$_(B" (?a "$(3$_(B")))
372;;; n
373   ("$(3"X(B" (?e "$(3"S(B") (?u "$(3"T(B") (?i "$(3"U(B") (?a "$(3"V(B") (?E "$(3"W(B") (?o "$(3"Y(B")
374         (?W "$(3"Z(B" (?a "$(3"Z(B")
375	          (?e "$(3"X%n(B") (?u "$(3"X%r(B") (?i "$(3"X%o(B") (?E "$(3"X%q(B")))
376;;; o
377   ("$(3"i(B" (?2 "$(3#;(B"))
378;;; p
379   ("$(3$\(B" (?e "$(3$W(B") (?u "$(3$X(B") (?i "$(3$Y(B") (?a "$(3$Z(B") (?E "$(3$[(B") (?o "$(3$](B")
380         (?W "$(3%e(B" (?e "$(3%5(B") (?u "$(3%e(B") (?i "$(3%E(B") (?a "$(3$^(B") (?E "$(3%U(B")))
381;;; q
382   ("$(3!f(B" (?e "$(3!a(B") (?u "$(3!b(B") (?i "$(3!c(B") (?a "$(3!d(B") (?E "$(3!e(B") (?o "$(3!g(B")
383         (?W "$(3!n(B" (?e "$(3!i(B") (?u "$(3!n(B") (?i "$(3!k(B") (?a "$(3!l(B") (?E "$(3!m(B"))
384         (?2 "$(3%.(B" (?e "$(3%)(B") (?u "$(3%*(B") (?i "$(3%+(B") (?a "$(3%,(B") (?E "$(3%-(B") (?o "$(3%/(B")))
385;;; r
386   ("$(3!N(B" (?e "$(3!I(B") (?u "$(3!J(B") (?i "$(3!K(B") (?a "$(3!L(B") (?E "$(3!M(B") (?o "$(3!O(B")
387         (?W "$(3!P(B" (?a "$(3!P(B")
388	          (?e "$(3!N%n(B") (?u "$(3!N%r(B") (?i "$(3!N%o(B") (?E "$(3!N%q(B"))
389         (?Y "$(3$`(B" (?a "$(3$`(B")))
390;;; s
391   ("$(3!V(B" (?e "$(3!Q(B") (?u "$(3!R(B") (?i "$(3!S(B") (?a "$(3!T(B") (?E "$(3!U(B") (?o "$(3!W(B")
392         (?W "$(3!X(B" (?a "$(3!X(B")
393	          (?e "$(3!V%n(B") (?u "$(3!V%r(B") (?i "$(3!V%o(B") (?E "$(3!V%q(B"))
394	 (?2 "$(3!F(B" (?e "$(3!A(B") (?u "$(3!B(B") (?i "$(3!C(B") (?a "$(3!D(B") (?E "$(3!E(B") (?o "$(3!G(B")
395		  (?W "$(3!H(B" (?a "$(3!H(B")
396		           (?e "$(3!F%n(B") (?u "$(3!F%r(B") (?i "$(3!F%o(B") (?E "$(3!F%q(B"))))
397;;; t
398   ("$(3"8(B" (?e "$(3"3(B") (?u "$(3"4(B") (?i "$(3"5(B") (?a "$(3"6(B") (?E "$(3"7(B") (?o "$(3"9(B")
399         (?W "$(3":(B" (?a "$(3":(B")
400	          (?e "$(3"8%n(B") (?u "$(3"8%r(B") (?i "$(3"8%o(B") (?E "$(3"8%q(B")))
401;;; u
402   ("$(3"d(B" (?2 "$(3#6(B"))
403;;; v
404   ("$(3"0(B" (?e "$(3"+(B") (?u "$(3",(B") (?i "$(3"-(B") (?a "$(3".(B") (?E "$(3"/(B") (?o "$(3"1(B")
405         (?W "$(3"2(B" (?a "$(3"2(B")
406	          (?e "$(3"0%n(B") (?u "$(3"0%r(B") (?i "$(3"0%o(B") (?E "$(3"0%q(B")))
407;;; w
408   ("$(3#2(B" (?e "$(3#-(B") (?u "$(3#.(B") (?i "$(3#/(B") (?a "$(3#0(B") (?E "$(3#1(B") (?o "$(3#3(B")
409         (?W "$(3%p(B" (?e "$(3%n(B") (?u "$(3%r(B") (?i "$(3%o(B") (?a "$(3%p(B") (?E "$(3%q(B")))
410;;; x
411   ("$(3!^(B" (?e "$(3!Y(B") (?u "$(3!Z(B") (?i "$(3![(B") (?a "$(3!\(B") (?E "$(3!](B") (?o "$(3!_(B")
412         (?W "$(3!`(B" (?a "$(3!`(B")
413	          (?e "$(3!^%n(B") (?u "$(3!^%r(B") (?i "$(3!^%o(B") (?E "$(3!^%q(B")))
414;;; y
415   ("$(3#R(B" (?e "$(3#M(B") (?u "$(3#N(B") (?i "$(3#O(B") (?a "$(3#P(B") (?E "$(3#Q(B") (?o "$(3#S(B")
416         (?W "$(3#T(B" (?a "$(3#T(B")
417	          (?e "$(3#R%n(B") (?u "$(3#R%r(B") (?i "$(3#R%o(B") (?E "$(3#R%q(B")))
418;;; z
419   ("$(3#B(B" (?e "$(3#=(B") (?u "$(3#>(B") (?i "$(3#?(B") (?a "$(3#@(B") (?E "$(3#A(B") (?o "$(3#C(B")
420         (?W "$(3#D(B" (?a "$(3#D(B")
421	          (?e "$(3#B%n(B") (?u "$(3#B%r(B") (?i "$(3#B%o(B") (?E "$(3#B%q(B")))
422;;; {   |   }   ~  DEL
423   nil nil nil nil nil
424   ])
425
426;; To avoid byte-compiler warnings.  It should never be set globally.
427(defvar ethio-sera-being-called-by-w3)
428;; This variable will be bound by some third-party package.
429(defvar sera-being-called-by-w3)
430
431;;;###autoload
432(defun ethio-sera-to-fidel-region (beg end &optional secondary force)
433  "Convert the characters in region from SERA to FIDEL.
434The variable `ethio-primary-language' specifies the primary language
435and `ethio-secondary-language' specifies the secondary.
436
437If the 3rd parameter SECONDARY is given and non-nil, assume the region
438begins with the secondary language; otherwise with the primary
439language.
440
441If the 4th parameter FORCE is given and non-nil, perform conversion
442even if the buffer is read-only.
443
444See also the descriptions of the variables
445`ethio-use-colon-for-colon' and
446`ethio-use-three-dot-question'."
447
448  (interactive "r\nP")
449  (save-restriction
450    (narrow-to-region beg end)
451    (ethio-sera-to-fidel-buffer secondary force)))
452
453;;;###autoload
454(defun ethio-sera-to-fidel-buffer (&optional secondary force)
455  "Convert the current buffer from SERA to FIDEL.
456
457The variable `ethio-primary-language' specifies the primary
458language and `ethio-secondary-language' specifies the secondary.
459
460If the 1st optional parameter SECONDARY is non-nil, assume the buffer
461begins with the secondary language; otherwise with the primary
462language.
463
464If the 2nd optional parametr FORCE is non-nil, perform conversion even if the
465buffer is read-only.
466
467See also the descriptions of the variables
468`ethio-use-colon-for-colon' and
469`ethio-use-three-dot-question'."
470
471  (interactive "P")
472
473  (if (and buffer-read-only
474	   (not force)
475	   (not (y-or-n-p "Buffer is read-only.  Force to convert? ")))
476      (error ""))
477
478  (let ((ethio-primary-language ethio-primary-language)
479	(ethio-secondary-language ethio-secondary-language)
480	(ethio-use-colon-for-colon ethio-use-colon-for-colon)
481	(ethio-use-three-dot-question ethio-use-three-dot-question)
482	;; The above four variables may be changed temporary
483	;; by tilde escapes during conversion.  So we bind them to other
484	;; variables but of the same names.
485	(buffer-read-only nil)
486	(case-fold-search nil)
487	current-language
488	next-language)
489
490    (setq current-language
491	  (if secondary
492	      ethio-secondary-language
493	    ethio-primary-language))
494
495    (goto-char (point-min))
496
497    (while (not (eobp))
498      (setq next-language
499	    (cond
500	     ((eq current-language 'english)
501	      (ethio-sera-to-fidel-english))
502	     ((eq current-language 'amharic)
503	      (ethio-sera-to-fidel-ethio 'amharic))
504	     ((eq current-language 'tigrigna)
505	      (ethio-sera-to-fidel-ethio 'tigrigna))
506	     (t				; we don't know what to do
507	      (ethio-sera-to-fidel-english))))
508
509      (setq current-language
510	    (cond
511
512	     ;; when language tag is explicitly specified
513	     ((not (eq next-language 'toggle))
514	      next-language)
515
516	     ;; found a toggle in a primary language section
517	     ((eq current-language ethio-primary-language)
518	      ethio-secondary-language)
519
520	     ;; found a toggle in a secondary, third, fourth, ...
521	     ;; language section
522	     (t
523	      ethio-primary-language))))
524
525    ;; If ethio-implicit-period-conversion is non-nil, the
526    ;; Ethiopic dot "$(3%u(B" at the end of an Ethiopic sentence is
527    ;; replaced with the Ethiopic full stop "$(3$i(B".
528    (if ethio-implicit-period-conversion
529	(progn
530	  (goto-char (point-min))
531	  (while (re-search-forward "\\([$(3!!(B-$(3$a%)(B-$(3%e%n(B-$(3%r%s(B]\\)$(3%u(B\\([ \t]\\)"
532				    nil t)
533	    (replace-match "\\1$(3$i(B\\2"))
534	  (goto-char (point-min))
535	  (while (re-search-forward "\\([$(3!!(B-$(3$a%)(B-$(3%e%n(B-$(3%r%s(B]\\)$(3%u(B$" nil t)
536	    (replace-match "\\1$(3$i(B"))))
537
538    ;; gemination
539    (goto-char (point-min))
540    (while (re-search-forward "\\ce$(3%s(B" nil 0)
541      (compose-region
542       (save-excursion (backward-char 2) (point))
543       (point)))
544    ))
545
546(defun ethio-sera-to-fidel-english nil
547  "Handle English section in SERA to FIDEL conversion.
548Conversion stops when a language switch is found.  Then delete that
549switch and return the name of the new language as a symbol."
550  (let ((new-language nil))
551
552    (while (and (not (eobp)) (null new-language))
553      (cond
554
555       ;; if no more "\", nothing to do.
556       ((not (search-forward "\\" nil 0)))
557
558       ;; hereafter point is put after a "\".
559       ;; first delete that "\", then check the following chars
560
561       ;; "\\" :  leave the second "\"
562       ((progn
563	  (delete-backward-char 1)
564	  (= (following-char) ?\\ ))
565	(forward-char 1))
566
567       ;; "\ " :  delete the following " "
568       ((= (following-char) 32)
569	(delete-char 1)
570	(setq new-language 'toggle))
571
572       ;; a language flag
573       ((setq new-language (ethio-process-language-flag)))
574
575       ;; just a "\" :  not special sequence.
576       (t
577	(setq new-language 'toggle))))
578
579    new-language))
580
581(defun ethio-sera-to-fidel-ethio (lang)
582  "Handle Ethiopic section in SERA to FIDEL conversion.
583Conversion stops when a language switch is found.  Then delete that
584switch and return the name of the new language as a symbol.
585
586The parameter LANG (symbol, either `amharic' or `tigrigna') affects
587the conversion of \"a\"."
588
589  (let ((new-language nil)
590	(verbatim nil)
591	start table table2 ch)
592
593    (setcar (aref ethio-sera-to-fidel-table ?a)
594	    (if (eq lang 'tigrigna) "$(3"f(B" "$(3"c(B"))
595
596    (while (and (not (eobp)) (null new-language))
597      (setq ch (following-char))
598      (cond
599
600       ;; skip from "<" to ">" (or from "&" to ";") if in w3-mode
601       ((and (or (= ch ?<) (= ch ?&))
602	     (or (and (boundp 'ethio-sera-being-called-by-w3)
603		      ethio-sera-being-called-by-w3)
604		 (and (boundp 'sera-being-called-by-w3)
605		      sera-being-called-by-w3)))
606	(search-forward (if (= ch ?<) ">" ";")
607			nil 0))
608
609       ;; leave non-ASCII characters as they are
610       ((>= ch 128)
611	(forward-char 1))
612
613       ;; ethiopic digits
614       ((looking-at "`[1-9][0-9]*")
615	(delete-char 1)
616	(ethio-convert-digit))
617
618       ;; if not seeing a "\", do sera to fidel conversion
619       ((/= ch ?\\ )
620	(setq start (point))
621	(forward-char 1)
622	(setq table (aref ethio-sera-to-fidel-table ch))
623	(while (setq table2 (cdr (assoc (following-char) table)))
624	  (setq table table2)
625	  (forward-char 1))
626	(if (setq ch (car table))
627	    (progn
628	      (delete-region start (point))
629	      (if (stringp ch)
630		  (insert ch)
631		(insert (eval ch))))))
632
633       ;; if control reaches here, we must be looking at a "\"
634
635       ;; verbatim mode
636       (verbatim
637	(if (looking-at "\\\\~! ?")
638
639	    ;; "\~!" or "\~! ".  switch to non-verbatim mode
640	    (progn
641	      (replace-match "")
642	      (setq verbatim nil))
643
644	  ;; "\" but not "\~!" nor "\~! ".  skip the current "\".
645	  (forward-char 1)))
646
647       ;; hereafter, non-verbatim mode and looking at a "\"
648       ;; first delete that "\", then check the following chars.
649
650       ;; "\ " : delete the following " "
651       ((progn
652	  (delete-char 1)
653	  (setq ch (following-char))
654	  (= ch 32))
655	(delete-char 1)
656	(setq new-language 'toggle))
657
658       ;; "\~!" or "\~! " : switch to verbatim mode
659       ((looking-at "~! ?")
660	(replace-match "")
661	(setq verbatim t))
662
663       ;; a language flag
664       ((setq new-language (ethio-process-language-flag)))
665
666       ;; "\~" but not "\~!" nor a language flag
667       ((= ch ?~)
668	(delete-char 1)
669	(ethio-tilde-escape))
670
671       ;; ASCII punctuation escape.  skip
672       ((looking-at "\\(,\\|\\.\\|;\\|:\\|'\\|`\\|\?\\|\\\\\\)+")
673	(goto-char (match-end 0)))
674
675       ;; "\", but not special sequence
676       (t
677	(setq new-language 'toggle))))
678
679    new-language))
680
681(defun ethio-process-language-flag nil
682  "Process a language flag of the form \"~lang\" or \"~lang1~lang2\".
683
684If looking at \"~lang1~lang2\", set `ethio-primary-language' and
685`ethio-une-secondary-language' based on \"lang1\" and \"lang2\".
686Then delete the language flag \"~lang1~lang2\" from the buffer.
687Return value is the new primary language.
688
689If looking at \"~lang\", delete that language flag \"~lang\" from the
690buffer and return that language.  In this case
691`ethio-primary-language' and `ethio-uni-secondary-language'
692are left unchanged.
693
694If an unsupported language flag is found, just return nil without
695changing anything."
696
697  (let (lang1 lang2)
698    (cond
699
700     ;; ~lang1~lang2
701     ((and (looking-at
702	    "~\\([a-z][a-z][a-z]?\\)~\\([a-z][a-z][a-z]?\\)[ \t\n\\]")
703	   (setq lang1
704		 (ethio-flag-to-language
705		  (buffer-substring (match-beginning 1) (match-end 1))))
706	   (setq lang2
707		 (ethio-flag-to-language
708		  (buffer-substring (match-beginning 2) (match-end 2)))))
709      (setq ethio-primary-language lang1
710	    ethio-secondary-language lang2)
711      (delete-region (point) (match-end 2))
712      (if (= (following-char) 32)
713	  (delete-char 1))
714      ethio-primary-language)
715
716     ;; ~lang
717     ((and (looking-at "~\\([a-z][a-z][a-z]?\\)[ \t\n\\]")
718	   (setq lang1
719		 (ethio-flag-to-language
720		  (buffer-substring (match-beginning 1) (match-end 1)))))
721      (delete-region (point) (match-end 1))
722      (if (= (following-char) 32)
723	  (delete-char 1))
724      lang1)
725
726     ;; otherwise
727     (t
728      nil))))
729
730(defun ethio-tilde-escape nil
731  "Handle a SERA tilde escape in Ethiopic section and delete it.
732Delete the escape even it is not recognized."
733
734  (let ((p (point)) command)
735    (skip-chars-forward "^ \t\n\\\\")
736    (setq command (buffer-substring p (point)))
737    (delete-region p (point))
738    (if (= (following-char) 32)
739	(delete-char 1))
740
741    (cond
742
743     ;; \~-:
744     ((string= command "-:")
745      (setq ethio-use-colon-for-colon t))
746
747     ;; \~`:
748     ((string= command "`:")
749      (setq ethio-use-colon-for-colon nil))
750
751     ;; \~?
752     ((string= command "?")
753      (setq ethio-use-three-dot-question nil))
754
755     ;; \~`|
756     ((string= command "`|")
757      (setq ethio-use-three-dot-question t))
758
759     ;; \~e
760     ((string= command "e")
761      (insert "$(3%j(B"))
762
763     ;; \~E
764     ((string= command "E")
765      (insert "$(3%k(B"))
766
767     ;; \~a
768     ((string= command "a")
769      (insert "$(3%l(B"))
770
771     ;; \~A
772     ((string= command "A")
773      (insert "$(3%m(B"))
774
775     ;; \~X
776     ((string= command "X")
777      (insert "$(3%i(B"))
778
779     ;; unsupported tilde escape
780     (t
781      nil))))
782
783(defun ethio-flag-to-language (flag)
784  (cond
785   ((or (string= flag "en") (string= flag "eng")) 'english)
786   ((or (string= flag "ti") (string= flag "tir")) 'tigrigna)
787   ((or (string= flag "am") (string= flag "amh")) 'amharic)
788   (t nil)))
789
790(defun ethio-convert-digit nil
791  "Convert Arabic digits to Ethiopic digits."
792  (let (ch z)
793    (while (and (>= (setq ch (following-char)) ?1)
794		(<= ch ?9))
795      (delete-char 1)
796
797      ;; count up following zeros
798      (setq z 0)
799      (while (= (following-char) ?0)
800	(delete-char 1)
801	(setq z (1+ z)))
802
803      (cond
804
805       ;; first digit is 10, 20, ..., or 90
806       ((= (mod z 2) 1)
807	(insert (aref [?$(3$y(B ?$(3$z(B ?$(3${(B ?$(3$|(B ?$(3$}(B ?$(3$~(B ?$(3%!(B ?$(3%"(B ?$(3%#(B] (- ch ?1)))
808	(setq z (1- z)))
809
810       ;; first digit is 2, 3, ..., or 9
811       ((/= ch ?1)
812	(insert (aref [?$(3$q(B ?$(3$r(B ?$(3$s(B ?$(3$t(B ?$(3$u(B ?$(3$v(B ?$(3$w(B ?$(3$x(B] (- ch ?2))))
813
814       ;; single 1
815       ((= z 0)
816	(insert "$(3$p(B")))
817
818      ;; 100
819      (if (= (mod z 4) 2)
820	  (insert "$(3%$(B"))
821
822      ;; 10000
823      (insert-char ?$(3%%(B (/ z 4)))))
824
825;;;###autoload
826(defun ethio-sera-to-fidel-mail-or-marker (&optional arg)
827  "Execute `ethio-sera-to-fidel-mail' or `ethio-sera-to-fidel-marker' depending on the current major mode.
828If in rmail-mode or in mail-mode, execute the former; otherwise latter."
829
830  (interactive "P")
831  (if (or (eq major-mode 'rmail-mode)
832	  (eq major-mode 'mail-mode))
833      (ethio-sera-to-fidel-mail (prefix-numeric-value arg))
834    (ethio-sera-to-fidel-marker arg)))
835
836;;;###autoload
837(defun ethio-sera-to-fidel-mail (&optional arg)
838  "Convert SERA to FIDEL to read/write mail and news.
839
840If the buffer contains the markers \"<sera>\" and \"</sera>\",
841convert the segments between them into FIDEL.
842
843If invoked interactively and there is no marker, convert the subject field
844and the body into FIDEL using `ethio-sera-to-fidel-region'."
845
846  (interactive "p")
847  (let ((buffer-read-only nil)
848	border)
849    (save-excursion
850
851      ;; follow RFC822 rules instead of looking for a fixed separator
852      (rfc822-goto-eoh)
853      (forward-line 1)
854      (setq border (point))
855
856      ;; note that the point is placed at the border
857      (if (or (re-search-forward "^<sera>$" nil t)
858	      (progn
859		(goto-char (point-min))
860		(re-search-forward "^Subject: <sera>" border t)))
861
862	  ;; there are markers
863	  (progn
864	    ;; we start with the body so that the border will not change
865	    ;; use "^<sera>\n" instead of "^<sera>$" not to leave a blank line
866	    (goto-char border)
867	    (while (re-search-forward "^<sera>\n" nil t)
868	      (replace-match "")
869	      (ethio-sera-to-fidel-region
870	       (point)
871	       (progn
872		 (if (re-search-forward "^</sera>\n" nil 0)
873		     (replace-match ""))
874		 (point))))
875	    ;; now process the subject
876	    (goto-char (point-min))
877	    (if (re-search-forward "^Subject: <sera>" border t)
878		(ethio-sera-to-fidel-region
879		 (progn (delete-backward-char 6) (point))
880		 (progn
881		   (if (re-search-forward "</sera>$" (line-end-position) 0)
882		       (replace-match ""))
883		   (point)))))
884
885	;; in case there are no marks but invoked interactively
886	(if arg
887	    (progn
888	      (ethio-sera-to-fidel-region border (point-max))
889	      (goto-char (point-min))
890	      (if (re-search-forward "^Subject: " border t)
891		  (ethio-sera-to-fidel-region (point) (line-end-position))))))
892
893      ;; adjust the rmail marker
894      (if (eq major-mode 'rmail-mode)
895	  (set-marker
896	   (aref rmail-message-vector (1+ rmail-current-message))
897	   (point-max))))))
898
899;;;###autoload
900(defun ethio-sera-to-fidel-marker (&optional force)
901  "Convert the regions surrounded by \"<sera>\" and \"</sera>\" from SERA to FIDEL.
902Assume that each region begins with `ethio-primary-language'.
903The markers \"<sera>\" and \"</sera>\" themselves are not deleted."
904  (interactive "P")
905  (if (and buffer-read-only
906	   (not force)
907	   (not (y-or-n-p "Buffer is read-only.  Force to convert? ")))
908      (error ""))
909  (save-excursion
910    (goto-char (point-min))
911    (while (re-search-forward "<sera>" nil t)
912      (ethio-sera-to-fidel-region
913       (point)
914       (if (re-search-forward "</sera>" nil t)
915	   (match-beginning 0)
916	 (point-max))
917       nil
918       'force))))
919
920;;
921;; FIDEL to SERA
922;;
923
924(defconst ethio-fidel-to-sera-map
925 [ "he"  "hu"  "hi"  "ha"  "hE"  "h"  "ho"    ""       ;;   0 - 7
926   "le"  "lu"  "li"  "la"  "lE"  "l"  "lo"  "lWa"      ;;   8
927   "He"  "Hu"  "Hi"  "Ha"  "HE"  "H"  "Ho"  "HWa"      ;;  16
928   "me"  "mu"  "mi"  "ma"  "mE"  "m"  "mo"  "mWa"      ;;  24
929  "`se" "`su" "`si" "`sa" "`sE" "`s" "`so" "`sWa"      ;;  32
930   "re"  "ru"  "ri"  "ra"  "rE"  "r"  "ro"  "rWa"      ;;  40
931   "se"  "su"  "si"  "sa"  "sE"  "s"  "so"  "sWa"      ;;  48
932   "xe"  "xu"  "xi"  "xa"  "xE"  "x"  "xo"  "xWa"      ;;  56
933   "qe"  "qu"  "qi"  "qa"  "qE"  "q"  "qo"    ""       ;;  64
934  "qWe"   ""  "qWi" "qWa" "qWE"  "qW'" ""     ""       ;;  72
935   "Qe"  "Qu"  "Qi"  "Qa"  "QE"  "Q"  "Qo"    ""       ;;  80
936  "QWe"   ""  "QWi" "QWa" "QWE"  "QW'" ""     ""       ;;  88
937   "be"  "bu"  "bi"  "ba"  "bE"  "b"  "bo"  "bWa"      ;;  96
938   "ve"  "vu"  "vi"  "va"  "vE"  "v"  "vo"  "vWa"      ;; 104
939   "te"  "tu"  "ti"  "ta"  "tE"  "t"  "to"  "tWa"      ;; 112
940   "ce"  "cu"  "ci"  "ca"  "cE"  "c"  "co"  "cWa"      ;; 120
941  "`he" "`hu" "`hi" "`ha" "`hE" "`h" "`ho"    ""       ;; 128
942  "hWe"   ""  "hWi" "hWa"  "hWE" "hW'" ""     ""       ;; 136
943   "ne"  "nu"  "ni"  "na"  "nE"  "n"  "no"  "nWa"      ;; 144
944   "Ne"  "Nu"  "Ni"  "Na"  "NE"  "N"  "No"  "NWa"      ;; 152
945    "e"   "u"   "i"   "A"   "E"  "I"   "o"   "ea"      ;; 160
946   "ke"  "ku"  "ki"  "ka"  "kE"  "k"  "ko"    ""       ;; 168
947  "kWe"   ""  "kWi" "kWa" "kWE"  "kW'" ""     ""       ;; 176
948   "Ke"  "Ku"  "Ki"  "Ka"  "KE"  "K"  "Ko"    ""       ;; 184
949  "KWe"   ""  "KWi" "KWa" "KWE"  "KW'" ""     ""       ;; 192
950   "we"  "wu"  "wi"  "wa"  "wE"  "w"  "wo"    ""       ;; 200
951   "`e"  "`u"  "`i"  "`a"  "`E" "`I"  "`o"    ""       ;; 208
952   "ze"  "zu"  "zi"  "za"  "zE"  "z"  "zo"  "zWa"      ;; 216
953   "Ze"  "Zu"  "Zi"  "Za"  "ZE"  "Z"  "Zo"  "ZWa"      ;; 224
954   "ye"  "yu"  "yi"  "ya"  "yE"  "y"  "yo"  "yWa"      ;; 232
955   "de"  "du"  "di"  "da"  "dE"  "d"  "do"  "dWa"      ;; 240
956   "De"  "Du"  "Di"  "Da"  "DE"  "D"  "Do"  "DWa"      ;; 248
957   "je"  "ju"  "ji"  "ja"  "jE"  "j"  "jo"  "jWa"      ;; 256
958   "ge"  "gu"  "gi"  "ga"  "gE"  "g"  "go"    ""       ;; 264
959  "gWe"   ""  "gWi" "gWa" "gWE" "gW'"  ""     ""       ;; 272
960   "Ge"  "Gu"  "Gi"  "Ga"  "GE"  "G"  "Go"  "GWa"      ;; 280
961   "Te"  "Tu"  "Ti"  "Ta"  "TE"  "T"  "To"  "TWa"      ;; 288
962   "Ce"  "Cu"  "Ci"  "Ca"  "CE"  "C"  "Co"  "CWa"      ;; 296
963   "Pe"  "Pu"  "Pi"  "Pa"  "PE"  "P"  "Po"  "PWa"      ;; 304
964   "Se"  "Su"  "Si"  "Sa"  "SE"  "S"  "So"  "SWa"      ;; 312
965  "`Se" "`Su" "`Si" "`Sa" "`SE" "`S" "`So"    ""       ;; 320
966   "fe"  "fu"  "fi"  "fa"  "fE"  "f"  "fo"  "fWa"      ;; 328
967   "pe"  "pu"  "pi"  "pa"  "pE"  "p"  "po"  "pWa"      ;; 336
968  "mYa" "rYa" "fYa"   ""    ""   ""    ""     ""       ;; 344
969   " "  " : "  "::"  ","   ";"  "-:"  ":-"   "`?"      ;; 352
970  ":|:"  "1"   "2"   "3"   "4"   "5"   "6"   "7"       ;; 360
971   "8"   "9"   "10"  "20"  "30"  "40" "50"   "60"      ;; 368
972   "70"  "80"  "90" "100" "10000" ""   ""     ""       ;; 376
973  "`qe" "`qu" "`qi" "`qa" "`qE" "`q" "`qo"    ""       ;; 384
974  "mWe" "bWe" "GWe" "fWe" "pWe"  ""    ""     ""       ;; 392
975  "`ke" "`ku" "`ki" "`ka" "`kE" "`k" "`ko"    ""       ;; 400
976  "mWi" "bWi" "GWi" "fWi" "pWi"  ""    ""     ""       ;; 408
977   "Xe"  "Xu"  "Xi"  "Xa"  "XE"  "X"  "Xo"    ""       ;; 416
978  "mWE" "bWE" "GWE" "fWE" "pWE"  ""    ""     ""       ;; 424
979  "`ge" "`gu" "`gi" "`ga" "`gE" "`g" "`go"    ""       ;; 432
980  "mW'" "bW'" "GW'" "fW'" "pW'"  ""    ""     ""       ;; 440
981  "\\~X " "\\~e " "\\~E " "\\~a " "\\~A " "wWe" "wWi" "wWa" ;; 448
982  "wWE" "wW'"  "''"  "`!"  "."  "<<"  ">>"   "?" ])    ;; 456
983
984(defun ethio-prefer-amharic-p nil
985  (or (eq ethio-primary-language 'amharic)
986      (and (not (eq ethio-primary-language 'tigrigna))
987	   (eq ethio-secondary-language 'amharic))))
988
989(defun ethio-language-to-flag (lang)
990  (cond
991   ((eq lang 'english) "eng")
992   ((eq lang 'tigrigna) "tir")
993   ((eq lang 'amharic) "amh")
994   (t "")))
995
996;;;###autoload
997(defun ethio-fidel-to-sera-region (begin end &optional secondary force)
998  "Replace all the FIDEL characters in the region to the SERA format.
999The variable `ethio-primary-language' specifies the primary
1000language and `ethio-secondary-language' specifies the secondary.
1001
1002If the 3dr parameter SECONDARY is given and non-nil, try to convert
1003the region so that it begins in the secondary language; otherwise with
1004the primary language.
1005
1006If the 4th parameter FORCE is given and non-nil, convert even if the
1007buffer is read-only.
1008
1009See also the descriptions of the variables
1010`ethio-use-colon-for-colon', `ethio-use-three-dot-question',
1011`ethio-quote-vowel-always' and `ethio-numeric-reduction'."
1012
1013  (interactive "r\nP")
1014  (save-restriction
1015    (narrow-to-region begin end)
1016    (ethio-fidel-to-sera-buffer secondary force)))
1017
1018;;;###autoload
1019(defun ethio-fidel-to-sera-buffer (&optional secondary force)
1020  "Replace all the FIDEL characters in the current buffer to the SERA format.
1021The variable `ethio-primary-language' specifies the primary
1022language and `ethio-secondary-language' specifies the secondary.
1023
1024If the 1st optional parameter SECONDARY is non-nil, try to convert the
1025region so that it begins in the secondary language; otherwise with the
1026primary language.
1027
1028If the 2nd optional parameter FORCE is non-nil, convert even if the
1029buffer is read-only.
1030
1031See also the descriptions of the variables
1032`ethio-use-colon-for-colon', `ethio-use-three-dot-question',
1033`ethio-quote-vowel-always' and `ethio-numeric-reduction'."
1034
1035  (interactive "P")
1036  (if (and buffer-read-only
1037	   (not force)
1038	   (not (y-or-n-p "Buffer is read-only.  Force to convert? ")))
1039      (error ""))
1040
1041  (let ((buffer-read-only nil)
1042	(case-fold-search nil)
1043	(lonec nil) ;; t means previous char was a lone consonant
1044	(fidel nil) ;; t means previous char was a FIDEL
1045	(digit nil) ;; t means previous char was an Ethiopic digit
1046	(flag (if (ethio-prefer-amharic-p) "\\~amh " "\\~tir "))
1047	mode ch)
1048
1049    ;; user's preference in transcription
1050    (if ethio-use-colon-for-colon
1051	(progn
1052	  (aset ethio-fidel-to-sera-map 353 "`:")
1053	  (aset ethio-fidel-to-sera-map 357 ":"))
1054      (aset ethio-fidel-to-sera-map 353 " : ")
1055      (aset ethio-fidel-to-sera-map 357 "-:"))
1056
1057    (if ethio-use-three-dot-question
1058	(progn
1059	  (aset ethio-fidel-to-sera-map 359 "?")
1060	  (aset ethio-fidel-to-sera-map 463 "`?"))
1061      (aset ethio-fidel-to-sera-map 359 "`?")
1062      (aset ethio-fidel-to-sera-map 463 "?"))
1063
1064    (mapcar
1065     '(lambda (x)
1066	(aset (aref ethio-fidel-to-sera-map x)
1067	      2
1068	      (if ethio-W-sixth-always ?' ?u)))
1069     '(77 93 141 181 197 277 440 441 442 443 444 457))
1070
1071    (if (ethio-prefer-amharic-p)
1072	(aset ethio-fidel-to-sera-map 160 "a")
1073      (aset ethio-fidel-to-sera-map 160 "e"))
1074    ;; end of user's preference
1075
1076    ;; first, decompose geminated characters
1077    (decompose-region (point-min) (point-max))
1078
1079    ;; main conversion routine
1080    (goto-char (point-min))
1081    (while (not (eobp))
1082      (setq ch (following-char))
1083
1084      (cond				; ethiopic, english, neutral
1085
1086       ;; ethiopic character.  must go to ethiopic mode, if not in it.
1087       ((eq (char-charset ch) 'ethiopic)
1088	(setq ch (ethio-char-to-ethiocode ch))
1089	(delete-char 1)
1090	(if (not (eq mode 'ethiopic))
1091	    (progn
1092	      (insert flag)
1093	      (setq mode 'ethiopic)))
1094
1095	(cond				; fidel, punc, digit
1096
1097	 ;; fidels
1098	 ((or (<= ch 346)		;  he - fYa
1099	      (and (>= ch 384) (<= ch 444)) ; `qe - pw
1100	      (and (>= ch 453) (<= ch 457))) ; wWe - wW
1101	  (if (and (memq ch '(160 161 162 163 164 166 167)) ; (e - ea)
1102		   (or lonec
1103		       (and ethio-quote-vowel-always
1104			    fidel)))
1105	      (insert "'"))
1106	  (insert (aref ethio-fidel-to-sera-map ch))
1107	  (setq lonec (ethio-lone-consonant-p ch)
1108		fidel t
1109		digit nil))
1110
1111	 ;; punctuations or icons
1112	 ((or (and (>= ch 353) (<= ch 360)) ;  : - :|:
1113	      (>= ch 458)		;  '' -  ?
1114	      (and (>= ch 448) (<= ch 452))) ;  \~X \~e \~E \~a \~A
1115	  (insert (aref ethio-fidel-to-sera-map ch))
1116	  (setq lonec nil
1117		fidel nil
1118		digit nil))
1119
1120	 ;; now CH must be an ethiopic digit
1121
1122	 ;; reduction = 0 or not preceded by Ethiopic number(s)
1123	 ((or (= ethio-numeric-reduction 0)
1124	      (not digit))
1125	  (insert "`" (aref ethio-fidel-to-sera-map ch))
1126	  (setq lonec nil
1127		fidel nil
1128		digit t))
1129
1130	 ;; reduction = 2 and following 10s, 100s, 10000s
1131	 ((and (= ethio-numeric-reduction 2)
1132	       (memq ch '(370 379 380)))
1133	  (insert (substring (aref ethio-fidel-to-sera-map ch) 1))
1134	  (setq lonec nil
1135		fidel nil
1136		digit t))
1137
1138	 ;; ordinary following digits
1139	 (t
1140	  (insert (aref ethio-fidel-to-sera-map ch))
1141	  (setq lonec nil
1142		fidel nil
1143		digit t))))
1144
1145       ;; english character.  must go to english mode, if not in it.
1146       ((or (and (>= ch ?a) (<= ch ?z))
1147	    (and (>= ch ?A) (<= ch ?Z)))
1148	(if (not (eq mode 'english))
1149	    (insert "\\~eng "))
1150	(forward-char 1)
1151	(setq mode 'english
1152	      lonec nil
1153	      fidel nil
1154	      digit nil))
1155
1156       ;; ch can appear both in ethiopic section and in english section.
1157       (t
1158
1159	;; we must decide the mode, if not decided yet
1160	(if (null mode)
1161	    (progn
1162	      (setq mode
1163		    (if secondary
1164			ethio-secondary-language
1165		      ethio-primary-language))
1166	      (if (eq mode 'english)
1167		  (insert "\\~eng ")
1168		(insert flag)
1169		(setq mode 'ethiopic)))) ; tigrigna & amharic --> ethiopic
1170
1171	(cond				; \ , eng-mode , punc , w3 , other
1172
1173	 ;; backslash is always quoted
1174	 ((= ch ?\\ )
1175	  (insert "\\")
1176	  (forward-char 1))
1177
1178	 ;; nothing to do if in english mode
1179	 ((eq mode 'english)
1180	  (forward-char 1))
1181
1182	 ;; now we must be in ethiopic mode and seeing a non-"\"
1183
1184	 ;; ascii punctuations in ethiopic mode
1185	 ((looking-at "[,.;:'`?]+")
1186	  (insert "\\")
1187	  (goto-char (1+ (match-end 0)))) ; because we inserted one byte (\)
1188
1189	 ;; skip from "<" to ">" (or from "&" to ";") if called from w3
1190	 ((and (or (= ch ?<) (= ch ?&))
1191	       (or (and (boundp 'ethio-sera-being-called-by-w3)
1192			ethio-sera-being-called-by-w3)
1193		   (and (boundp 'sera-being-called-by-w3)
1194			sera-being-called-by-w3)))
1195	  (search-forward (if (= ch ?<) ">" ";")
1196			  nil 0))
1197
1198	 ;; neutral character.  no need to quote.  just skip it.
1199	 (t
1200	  (forward-char 1)))
1201
1202	(setq lonec nil
1203	      fidel nil
1204	      digit nil)))
1205    ;; end of main conversion routine
1206    )))
1207
1208(defun ethio-lone-consonant-p (ethiocode)
1209  "If ETHIOCODE is an Ethiopic lone consonant, return t."
1210  (or (and (< ethiocode 344) (= (% ethiocode 8) 5))
1211
1212      ;;                     `q  `k   X  `g  mW  bW  GW  fW  pW  wW
1213      (memq ethiocode '(389 405 421 437 440 441 442 443 444 457))))
1214
1215;;;###autoload
1216(defun ethio-fidel-to-sera-mail-or-marker (&optional arg)
1217  "Execute `ethio-fidel-to-sera-mail' or `ethio-fidel-to-sera-marker' depending on the current major mode.
1218If in rmail-mode or in mail-mode, execute the former; otherwise latter."
1219
1220  (interactive "P")
1221  (if (or (eq major-mode 'rmail-mode)
1222	  (eq major-mode 'mail-mode))
1223      (ethio-fidel-to-sera-mail)
1224    (ethio-fidel-to-sera-marker arg)))
1225
1226;;;###autoload
1227(defun ethio-fidel-to-sera-mail nil
1228  "Convert FIDEL to SERA to read/write mail and news.
1229
1230If the body contains at least one Ethiopic character,
1231 1) insert the string \"<sera>\" at the beginning of the body,
1232 2) insert \"</sera>\" at the end of the body, and
1233 3) convert the body into SERA.
1234
1235The very same procedure applies to the subject field, too."
1236
1237  (interactive)
1238  (let ((buffer-read-only nil)
1239	border)
1240    (save-excursion
1241
1242      ;; follow RFC822 rules instead of looking for a fixed separator
1243      (rfc822-goto-eoh)
1244      (forward-line 1)
1245      (setq border (point))
1246
1247      ;; process body first not to change the border
1248      ;; note that the point is already at the border
1249      (if (re-search-forward "\\ce" nil t)
1250	  (progn
1251	    (ethio-fidel-to-sera-region border (point-max))
1252	    (goto-char border)
1253	    (insert "<sera>")
1254	    (goto-char (point-max))
1255	    (insert "</sera>")))
1256
1257      ;; process subject
1258      (goto-char (point-min))
1259      (if (re-search-forward "^Subject: " border t)
1260	  (let ((beg (point))
1261		(end (line-end-position)))
1262	    (if (re-search-forward "\\ce" end t)
1263		(progn
1264		  (ethio-fidel-to-sera-region beg end)
1265		  (goto-char beg)
1266		  (insert "<sera>")
1267		  (end-of-line)
1268		  (insert "</sera>")))))
1269
1270      ;; adjust the rmail marker
1271      (if (eq major-mode 'rmail-mode)
1272	  (set-marker
1273	   (aref rmail-message-vector (1+ rmail-current-message))
1274	   (point-max))))))
1275
1276;;;###autoload
1277(defun ethio-fidel-to-sera-marker (&optional force)
1278  "Convert the regions surrounded by \"<sera>\" and \"</sera>\" from FIDEL to SERA.
1279The markers \"<sera>\" and \"</sera>\" themselves are not deleted."
1280
1281  (interactive "P")
1282  (if (and buffer-read-only
1283	   (not force)
1284	   (not (y-or-n-p "Buffer is read-only.  Force to convert? ")))
1285      (error ""))
1286  (save-excursion
1287    (goto-char (point-min))
1288    (while (re-search-forward "<sera>" nil t)
1289      (ethio-fidel-to-sera-region
1290       (point)
1291       (if (re-search-forward "</sera>" nil t)
1292	   (match-beginning 0)
1293	 (point-max))
1294       nil
1295       'force))))
1296
1297;;
1298;; vowel modification
1299;;
1300
1301;;;###autoload
1302(defun ethio-modify-vowel nil
1303  "Modify the vowel of the FIDEL that is under the cursor."
1304  (interactive)
1305  (let ((ch (following-char))
1306	(composite nil)			; geminated or not
1307	newch base vowel modulo)
1308
1309    (cond
1310     ;; in case of gemination
1311     ((eq (char-charset ch) 'composition)
1312      (setq ch (string-to-char (char-to-string ch))
1313	    composite t))
1314     ;; neither gemination nor fidel
1315     ((not (eq (char-charset ch) 'ethiopic))
1316      (error "Not a valid character")))
1317
1318    ;; set frequently referred character features
1319    (setq ch     (ethio-char-to-ethiocode ch)
1320	  base   (* (/ ch 8) 8)
1321	  modulo (% ch 8))
1322
1323    (if (or (and (>= ch 344) (<= ch 380)) ;; mYa - `10000
1324	    (and (>= ch 448) (<= ch 452)) ;; \~X - \~A
1325	    (>= ch 458))		  ;; private punctuations
1326	(error "Not a valid character"))
1327
1328    (setq
1329     newch
1330     (cond
1331
1332      ;; first standalone vowels
1333      ((= base 160)
1334       (if (ethio-prefer-amharic-p)
1335	   (message "Modify vowel to: [auiAEIoW\"] ")
1336	 (message "Modify vowel to: [euiAEIoW\"] "))
1337       (setq vowel (read-char))
1338       (cond
1339	((= vowel ?e) 160)
1340	((= vowel ?u) 161)
1341	((= vowel ?i) 162)
1342	((= vowel ?A) 163)
1343	((= vowel ?E) 164)
1344	((= vowel ?I) 165)
1345	((= vowel ?o) 166)
1346	((= vowel ?W) 167)
1347	((= vowel ?a) (if (ethio-prefer-amharic-p) 160 163))
1348	((= vowel ?\") (setq composite t) ch)
1349	(t nil)))
1350
1351      ;; second standalone vowels
1352      ((= base 208)
1353       (message "Modify vowel to: [euiaEIo\"] ")
1354       (setq vowel (read-char))
1355       (cond
1356	((= vowel ?e) 208)
1357	((= vowel ?u) 209)
1358	((= vowel ?i) 210)
1359	((= vowel ?a) 211)
1360	((= vowel ?E) 212)
1361	((= vowel ?I) 213)
1362	((= vowel ?o) 214)
1363	((= vowel ?\") (setq composite t) ch)
1364	(t nil)))
1365
1366      ;; 12-form consonants, *W* form
1367      ((memq base '(72 88 136 176 192 272)) ; qW QW hW kW KW gW
1368       (message "Modify vowel to: [euiaE'\"] ")
1369       (setq vowel (read-char))
1370       (cond
1371	((= vowel ?e) base)
1372	((= vowel ?u) (+ base 5))
1373	((= vowel ?i) (+ base 2))
1374	((= vowel ?a) (+ base 3))
1375	((= vowel ?E) (+ base 4))
1376	((= vowel ?') (+ base 5))
1377	((= vowel ?\") (setq composite t) ch)
1378	(t nil)))
1379
1380      ;; extended 12-form consonants, mWa bWa GWa fWa pWa
1381      ((= ch 31)			; mWa
1382       (message "Modify vowel to: [euiaE'\"] ")
1383       (setq vowel (read-char))
1384       (cond
1385	((= vowel ?e) 392)
1386	((= vowel ?u) 440)
1387	((= vowel ?i) 408)
1388	((= vowel ?a) ch)
1389	((= vowel ?E) 424)
1390	((= vowel ?') 440)
1391	((= vowel ?\") (setq composite t) ch)
1392	(t nil)))
1393      ((= ch 103)			; bWa
1394       (message "Modify vowel to: [euiaE'\"] ")
1395       (setq vowel (read-char))
1396       (cond
1397	((= vowel ?e) 393)
1398	((= vowel ?u) 441)
1399	((= vowel ?i) 409)
1400	((= vowel ?a) ch)
1401	((= vowel ?E) 425)
1402	((= vowel ?') 441)
1403	((= vowel ?\") (setq composite t) ch)
1404	(t nil)))
1405      ((= ch 287)			; GWa
1406       (message "Modify vowel to: [euiaE'\"] ")
1407       (setq vowel (read-char))
1408       (cond
1409	((= vowel ?e) 394)
1410	((= vowel ?u) 442)
1411	((= vowel ?i) 410)
1412	((= vowel ?a) ch)
1413	((= vowel ?E) 426)
1414	((= vowel ?') 442)
1415	((= vowel ?\") (setq composite t) ch)
1416	(t nil)))
1417      ((= ch 335)			; fWa
1418       (message "Modify vowel to: [euiaE'\"] ")
1419       (setq vowel (read-char))
1420       (cond
1421	((= vowel ?e) 395)
1422	((= vowel ?u) 443)
1423	((= vowel ?i) 411)
1424	((= vowel ?a) ch)
1425	((= vowel ?E) 427)
1426	((= vowel ?') 443)
1427	((= vowel ?\") (setq composite t) ch)
1428	(t nil)))
1429      ((= ch 343)			; pWa
1430       (message "Modify vowel to: [euiaE'\"] ")
1431       (setq vowel (read-char))
1432       (cond
1433	((= vowel ?e) 396)
1434	((= vowel ?u) 444)
1435	((= vowel ?i) 412)
1436	((= vowel ?a) ch)
1437	((= vowel ?E) 428)
1438	((= vowel ?') 444)
1439	((= vowel ?\") (setq composite t) ch)
1440	(t nil)))
1441
1442      ;; extended 12-form consonatns, mW* bW* GW* fW* pW*
1443      ((memq base '(392 408 424 440))	; *We *Wi *WE *W
1444       (message "Modify vowel to: [eiEau'\"] ")
1445       (setq vowel (read-char))
1446       (cond
1447	((= vowel ?e) (+ 392 modulo))
1448	((= vowel ?i) (+ 408 modulo))
1449	((= vowel ?E) (+ 424 modulo))
1450	((= vowel ?a) (cond
1451		       ((= modulo 0)  31) ; mWa
1452		       ((= modulo 1) 103) ; bWa
1453		       ((= modulo 2) 287) ; GWa
1454		       ((= modulo 3) 335) ; fWa
1455		       ((= modulo 4) 343) ; pWa
1456		       (t nil)))	; never reach here
1457	((= vowel ?') (+ 440 modulo))
1458	((= vowel ?u) (+ 440 modulo))
1459	((= vowel ?\") (setq composite t) ch)
1460	(t nil)))
1461
1462      ((and (>= ch 453) (<= ch 457))	; wWe wWi wWa wWE wW
1463       (message "Modify vowel to: [eiaE'u\"] ")
1464       (setq vowel (read-char))
1465       (cond
1466	((= vowel ?e) 453)
1467	((= vowel ?i) 454)
1468	((= vowel ?a) 455)
1469	((= vowel ?E) 456)
1470	((= vowel ?') 457)
1471	((= vowel ?u) 457)
1472	((= vowel ?\") (setq composite t) ch)
1473	(t nil)))
1474
1475      ;; 7-form consonants, or
1476      ;; first 7 of 8-form consonants
1477      ((<= modulo 6)
1478       (message "Modify vowel to: [euiaE'o\"] ")
1479       (setq vowel (read-char))
1480       (cond
1481	((= vowel ?e) base)
1482	((= vowel ?u) (+ base 1))
1483	((= vowel ?i) (+ base 2))
1484	((= vowel ?a) (+ base 3))
1485	((= vowel ?E) (+ base 4))
1486	((= vowel ?') (+ base 5))
1487	((= vowel ?o) (+ base 6))
1488	((= vowel ?\") (setq composite t) ch)
1489	(t nil)))
1490
1491      ;; otherwise
1492      (t
1493       nil)))
1494
1495    (cond
1496
1497     ;; could not get new character
1498     ((null newch)
1499      (error "Invalid vowel"))
1500
1501     ;; vowel changed on a composite Fidel
1502     (composite
1503      (delete-char 1)
1504      (insert
1505       (compose-string
1506	(concat (char-to-string (ethio-ethiocode-to-char newch))	"$(3%s(B"))))
1507
1508     ;; simple vowel modification
1509     (t
1510      (delete-char 1)
1511      (insert (ethio-ethiocode-to-char newch))))))
1512
1513(defun ethio-ethiocode-to-char (ethiocode)
1514  (make-char
1515   'ethiopic
1516   (+ (/ ethiocode 94) 33)
1517   (+ (mod ethiocode 94) 33)))
1518
1519(defun ethio-char-to-ethiocode (ch)
1520  (and (eq (char-charset ch) 'ethiopic)
1521       (let ((char-components (split-char ch)))
1522	 (+ (* (- (nth 1 char-components) 33) 94)
1523	    (- (nth 2 char-components) 33)))))
1524
1525;;
1526;; space replacement
1527;;
1528
1529;;;###autoload
1530(defun ethio-replace-space (ch begin end)
1531  "Replace ASCII spaces with Ethiopic word separators in the region.
1532
1533In the specified region, replace word separators surrounded by two
1534Ethiopic characters, depending on the first parameter CH, which should
1535be 1, 2, or 3.
1536
1537If CH = 1, word separator will be replaced with an ASCII space.
1538If CH = 2, with two ASCII spaces.
1539If CH = 3, with the Ethiopic colon-like word separator.
1540
1541The second and third parameters BEGIN and END specify the region."
1542
1543  (interactive "*cReplace spaces to: 1 (sg col), 2 (dbl col), 3 (Ethiopic)\nr")
1544  (if (not (memq ch '(?1 ?2 ?3)))
1545      (error ""))
1546  (save-excursion
1547    (save-restriction
1548      (narrow-to-region begin end)
1549
1550      (cond
1551       ((= ch ?1)
1552	;; an Ethiopic word separator --> an ASCII space
1553	(goto-char (point-min))
1554	(while (search-forward "$(3$h(B" nil t)
1555	  (replace-match " " nil t))
1556
1557	;; two ASCII spaces between Ethiopic characters --> an ASCII space
1558	(goto-char (point-min))
1559	(while (re-search-forward "\\(\\ce\\)  \\(\\ce\\)" nil t)
1560	  (replace-match "\\1 \\2")
1561	  (goto-char (match-beginning 2))))
1562
1563       ((= ch ?2)
1564	;; An Ethiopic word separator --> two ASCII spaces
1565	(goto-char (point-min))
1566	(while (search-forward "$(3$h(B" nil t)
1567	  (replace-match "  "))
1568
1569	;; An ASCII space between Ethiopic characters --> two ASCII spaces
1570	(goto-char (point-min))
1571	(while (re-search-forward "\\(\\ce\\) \\(\\ce\\)" nil t)
1572	  (replace-match "\\1  \\2")
1573	  (goto-char (match-beginning 2))))
1574
1575       (t
1576	;; One or two ASCII spaces between Ethiopic characters
1577	;;   --> An Ethiopic word separator
1578	(goto-char (point-min))
1579	(while (re-search-forward "\\(\\ce\\)  ?\\(\\ce\\)" nil t)
1580	  (replace-match "\\1$(3$h(B\\2")
1581	  (goto-char (match-beginning 2)))
1582
1583	;; Three or more ASCII spaces between Ethiopic characters
1584	;;   --> An Ethiopic word separator + (N - 2) ASCII spaces
1585	(goto-char (point-min))
1586	(while (re-search-forward "\\(\\ce\\)  \\( *\\ce\\)" nil t)
1587	  (replace-match "\\1$(3$h(B\\2")
1588	  (goto-char (match-beginning 2))))))))
1589
1590;;
1591;; special icons
1592;;
1593
1594;;;###autoload
1595(defun ethio-input-special-character (arg)
1596  "Allow the user to input special characters."
1597  (interactive "*cInput number: 1.$(3%j(B  2.$(3%k(B  3.$(3%l(B  4.$(3%m(B  5.$(3%i(B")
1598  (cond
1599   ((= arg ?1)
1600    (insert "$(3%j(B"))
1601   ((= arg ?2)
1602    (insert "$(3%k(B"))
1603   ((= arg ?3)
1604    (insert "$(3%l(B"))
1605   ((= arg ?4)
1606    (insert "$(3%m(B"))
1607   ((= arg ?5)
1608    (insert "$(3%i(B"))
1609   (t
1610    (error ""))))
1611
1612;;
1613;; TeX support
1614;;
1615
1616(defconst ethio-fidel-to-tex-map
1617 [ "heG"  "huG"  "hiG"  "haG"  "hEG"   "hG"  "hoG"      ""     ;;   0 - 7
1618   "leG"  "luG"  "liG"  "laG"  "lEG"   "lG"  "loG"  "lWaG"     ;;   8
1619   "HeG"  "HuG"  "HiG"  "HaG"  "HEG"   "HG"  "HoG"  "HWaG"     ;;  16
1620   "meG"  "muG"  "miG"  "maG"  "mEG"   "mG"  "moG"  "mWaG"     ;;  24
1621  "sseG" "ssuG" "ssiG" "ssaG" "ssEG"  "ssG" "ssoG" "ssWaG"     ;;  32
1622   "reG"  "ruG"  "riG"  "raG"  "rEG"   "rG"  "roG"  "rWaG"     ;;  40
1623   "seG"  "suG"  "siG"  "saG"  "sEG"   "sG"  "soG"  "sWaG"     ;;  48
1624   "xeG"  "xuG"  "xiG"  "xaG"  "xEG"   "xG"  "xoG"  "xWaG"     ;;  56
1625   "qeG"  "quG"  "qiG"  "qaG"  "qEG"   "qG"  "qoG"      ""     ;;  64
1626  "qWeG"     "" "qWiG" "qWaG" "qWEG"  "qWG"     ""      ""     ;;  72
1627   "QeG"  "QuG"  "QiG"  "QaG"  "QEG"   "QG"  "QoG"      ""     ;;  80
1628  "QWeG"     "" "QWiG" "QWaG" "QWEG"  "QWG"     ""      ""     ;;  88
1629   "beG"  "buG"  "biG"  "baG"  "bEG"   "bG"  "boG"  "bWaG"     ;;  96
1630   "veG"  "vuG"  "viG"  "vaG"  "vEG"   "vG"  "voG"  "vWaG"     ;; 104
1631   "teG"  "tuG"  "tiG"  "taG"  "tEG"   "tG"  "toG"  "tWaG"     ;; 112
1632   "ceG"  "cuG"  "ciG"  "caG"  "cEG"   "cG"  "coG"  "cWaG"     ;; 120
1633  "hheG" "hhuG" "hhiG" "hhaG" "hhEG"  "hhG" "hhoG"      ""     ;; 128
1634  "hWeG"     "" "hWiG" "hWaG" "hWEG"  "hWG"     ""      ""     ;; 136
1635   "neG"  "nuG"  "niG"  "naG"  "nEG"   "nG"  "noG"  "nWaG"     ;; 144
1636   "NeG"  "NuG"  "NiG"  "NaG"  "NEG"   "NG"  "NoG"  "NWaG"     ;; 152
1637    "eG"   "uG"   "iG"   "AG"   "EG"   "IG"   "oG"   "eaG"     ;; 160
1638   "keG"  "kuG"  "kiG"  "kaG"  "kEG"   "kG"  "koG"      ""     ;; 168
1639  "kWeG"     "" "kWiG" "kWaG" "kWEG"  "kWG"     ""      ""     ;; 176
1640   "KeG"  "KuG"  "KiG"  "KaG"  "KEG"   "KG"  "KoG"      ""     ;; 184
1641  "KWeG"     "" "KWiG" "KWaG" "KWEG"  "KWG"     ""      ""     ;; 192
1642   "weG"  "wuG"  "wiG"  "waG"  "wEG"   "wG"  "woG"      ""     ;; 200
1643   "eeG"  "uuG"  "iiG"  "aaG"  "EEG"  "IIG"  "ooG"      ""     ;; 208
1644   "zeG"  "zuG"  "ziG"  "zaG"  "zEG"   "zG"  "zoG"  "zWaG"     ;; 216
1645   "ZeG"  "ZuG"  "ZiG"  "ZaG"  "ZEG"   "ZG"  "ZoG"  "ZWaG"     ;; 224
1646   "yeG"  "yuG"  "yiG"  "yaG"  "yEG"   "yG"  "yoG"  "yWaG"     ;; 232
1647   "deG"  "duG"  "diG"  "daG"  "dEG"   "dG" "doG"   "dWaG"     ;; 240
1648   "DeG"  "DuG"  "DiG"  "DaG"  "DEG"   "DG"  "DoG"  "DWaG"     ;; 248
1649   "jeG"  "juG"  "jiG"  "jaG"  "jEG"   "jG"  "joG"  "jWaG"     ;; 256
1650   "geG"  "guG"  "giG"  "gaG"  "gEG"   "gG"  "goG"     ""      ;; 264
1651  "gWeG"     "" "gWiG" "gWaG" "gWEG"  "gWG"     ""     ""      ;; 272
1652   "GeG"  "GuG"  "GiG"  "GaG"  "GEG"   "GG"  "GoG"  "GWaG"     ;; 280
1653   "TeG"  "TuG"  "TiG"  "TaG"  "TEG"   "TG"  "ToG"  "TWaG"     ;; 288
1654   "CeG"  "CuG"  "CiG"  "CaG"  "CEG"   "CG"  "CoG"  "CWaG"     ;; 296
1655   "PeG"  "PuG"  "PiG"  "PaG"  "PEG"   "PG"  "PoG"  "PWaG"     ;; 304
1656   "SeG"  "SuG"  "SiG"  "SaG"  "SEG"   "SG"  "SoG"  "SWaG"     ;; 312
1657  "SSeG" "SSuG" "SSiG" "SSaG" "SSEG"  "SSG" "SSoG"      ""     ;; 320
1658   "feG"  "fuG"  "fiG"  "faG"  "fEG"   "fG"  "foG"  "fWaG"     ;; 328
1659   "peG"  "puG"  "piG"  "paG"  "pEG"   "pG"  "poG"  "pWaG"     ;; 336
1660  "mYaG" "rYaG" "fYaG"     ""     ""     ""     ""      ""     ;; 344
1661      "" "spaceG" "periodG" "commaG"                           ;; 352
1662  "semicolonG" "colonG" "precolonG" "oldqmarkG"                ;; 356
1663  "pbreakG" "andG" "huletG" "sostG" "aratG" "amstG" "sadstG" "sabatG"  ;; 360
1664  "smntG" "zeteNG" "asrG" "heyaG" "selasaG" "arbaG" "hemsaG" "slsaG"   ;; 368
1665  "sebaG" "semanyaG" "zeTanaG" "metoG" "asrxiG" "" "" ""               ;; 376
1666  "qqeG" "qquG" "qqiG" "qqaG" "qqEG" "qqG" "qqoG"    ""      ;; 384
1667  "mWeG" "bWeG" "GWeG" "fWeG" "pWeG"    ""     ""    ""      ;; 392
1668  "kkeG" "kkuG" "kkiG" "kkaG" "kkEG" "kkG" "kkoG"    ""      ;; 400
1669  "mWiG" "bWiG" "GWiG" "fWiG" "pWiG"    ""     ""    ""      ;; 408
1670   "XeG"  "XuG" "GXiG"  "XaG"  "XEG"  "XG"  "XoG"    ""      ;; 416
1671  "mWEG" "bWEG" "GWEG" "fWEG" "pWEG"    ""     ""    ""      ;; 424
1672  "ggeG" "gguG" "ggiG" "ggaG" "ggEG" "ggG" "ggoG"    ""      ;; 432
1673   "mWG" "bWG"   "GWG"  "fWG"  "pWG"    ""     ""    ""      ;; 440
1674  "ornamentG" "flandG" "iflandG" "africaG"	                 ;; 448
1675  "iafricaG" "wWeG" "wWiG" "wWaG"                            ;; 452
1676  "wWEG"  "wWG" "" "slaqG" "dotG" "lquoteG" "rquoteG" "qmarkG" ])  ;; 456
1677
1678;;
1679;; To make tex-to-fidel mapping.
1680;; The following code makes
1681;;     (get 'ethio-tex-command-he 'ethio-fidel-char)  ==>  ?$(3!!(B
1682;; etc.
1683;;
1684
1685(let ((i 0) str)
1686  (while (< i (length ethio-fidel-to-tex-map))
1687    (setq str (aref ethio-fidel-to-tex-map i))
1688    (if (not (string= str ""))
1689	(put
1690	 (intern (concat "ethio-tex-command-" (aref ethio-fidel-to-tex-map i)))
1691	 'ethio-fidel-char
1692	 (ethio-ethiocode-to-char i)))
1693    (setq i (1+ i))))
1694
1695;;;###autoload
1696(defun ethio-fidel-to-tex-buffer nil
1697  "Convert each fidel characters in the current buffer into a fidel-tex command.
1698Each command is always surrounded by braces."
1699  (interactive)
1700  (let ((buffer-read-only nil))
1701
1702    ;; Isolated gemination marks need special treatement
1703    (goto-char (point-min))
1704    (while (search-forward "$(3%s(B" nil t)
1705      (replace-match "\\geminateG{}" t t))
1706
1707    ;; First, decompose geminations
1708    ;; Here we assume that each composed character consists of
1709    ;; one Ethiopic character and the Ethiopic gemination mark.
1710    (decompose-region (point-min) (point-max))
1711
1712    ;; Special treatment for geminated characters
1713    ;; The geminated character (la'') will be "\geminateG{\la}".
1714    (goto-char (point-min))
1715    (while (search-forward "$(3%s(B" nil t)
1716      (delete-backward-char 1)
1717      (backward-char 1)
1718      (insert "\\geminateG")
1719      (forward-char 1))
1720
1721    ;; Ethiopic characters to TeX macros
1722    (goto-char (point-min))
1723    (while (re-search-forward "\\ce" nil t)
1724      (insert
1725       "{\\"
1726       (aref ethio-fidel-to-tex-map
1727	     (prog1 (ethio-char-to-ethiocode (preceding-char))
1728	       (backward-delete-char 1)))
1729       "}"))
1730    (goto-char (point-min))
1731    (set-buffer-modified-p nil)))
1732
1733;;;###autoload
1734(defun ethio-tex-to-fidel-buffer nil
1735  "Convert fidel-tex commands in the current buffer into fidel chars."
1736  (interactive)
1737  (let ((buffer-read-only nil)
1738	(p) (ch))
1739
1740    ;; Special treatment for gemination
1741    ;; "\geminateG{\la}" or "\geminateG{{\la}}" will be "\la$(3%s(B"
1742    ;; "\geminateG{}" remains unchanged.
1743    (goto-char (point-min))
1744    (while (re-search-forward "\\\\geminateG{\\(\\\\[a-zA-Z]+\\)}" nil t)
1745      (replace-match "\\1$(3%s(B"))
1746
1747    ;; TeX macros to Ethiopic characters
1748    (goto-char (point-min))
1749    (while (search-forward "\\" nil t)
1750      (setq p (point))
1751      (skip-chars-forward "a-zA-Z")
1752      (setq ch
1753	    (get (intern (concat "ethio-tex-command-"
1754				 (buffer-substring p (point))))
1755		 'ethio-fidel-char))
1756      (if ch
1757	  (progn
1758	    (delete-region (1- p) (point)) ; don't forget the preceding "\"
1759	    (if (and (= (preceding-char) ?{)
1760		     (= (following-char) ?}))
1761		(progn
1762		  (backward-delete-char 1)
1763		  (delete-char 1)))
1764	    (insert ch))))
1765
1766    ;; compose geminated characters
1767    (goto-char (point-min))
1768    (while (re-search-forward "\\ce$(3%s(B" nil 0)
1769      (compose-region
1770       (save-excursion (backward-char 2) (point))
1771       (point)))
1772
1773    ;; Now it's time to convert isolated gemination marks.
1774    (goto-char (point-min))
1775    (while (search-forward "\\geminateG{}" nil t)
1776      (replace-match "$(3%s(B"))
1777
1778    (goto-char (point-min))
1779    (set-buffer-modified-p nil)))
1780
1781;;
1782;; Java support
1783;;
1784
1785;;;###autoload
1786(defun ethio-fidel-to-java-buffer nil
1787  "Convert Ethiopic characters into the Java escape sequences.
1788
1789Each escape sequence is of the form \\uXXXX, where XXXX is the
1790character's codepoint (in hex) in Unicode.
1791
1792If `ethio-java-save-lowercase' is non-nil, use [0-9a-f].
1793Otherwise, [0-9A-F]."
1794  (let ((ucode))
1795
1796    ;; first, decompose geminations
1797    (decompose-region (point-min) (point-max))
1798
1799    (goto-char (point-min))
1800    (while (re-search-forward "\\ce" nil t)
1801      (setq ucode (+ ?\x1200 (ethio-char-to-ethiocode (preceding-char))))
1802      (if (> ucode ?\x13bc)
1803	  (setq ucode (+ ucode 59952)))
1804      (delete-backward-char 1)
1805      (if ethio-java-save-lowercase
1806	  (insert (format "\\u%4x" ucode))
1807	(insert (upcase (format "\\u%4x" ucode)))))))
1808
1809;;;###autoload
1810(defun ethio-java-to-fidel-buffer nil
1811  "Convert the Java escape sequences into corresponding Ethiopic characters."
1812  (let ((ucode))
1813    (goto-char (point-min))
1814    (while (re-search-forward "\\\\u\\([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]\\)" nil t)
1815      (setq ucode
1816	    (read
1817	     (concat
1818	      "?\\x"
1819	      (buffer-substring (match-beginning 1) (match-end 1)))))
1820      (cond
1821       ((and (>= ucode ?\x1200) (<= ucode ?\x13bc))
1822	(replace-match "")
1823	(insert (ethio-ethiocode-to-char (- ucode ?\x1200))))
1824       ((and (>= ucode ?\xfdf1) (<= ucode ?\xfdff))
1825	(replace-match "")
1826	(insert (ethio-ethiocode-to-char (- ucode 64560))))
1827       (t
1828	nil)))
1829
1830    ;; gemination
1831    (goto-char (point-min))
1832    (while (re-search-forward "\\ce$(3%s(B" nil 0)
1833      (compose-region
1834       (save-excursion (backward-char 2) (point))
1835       (point)))
1836    ))
1837
1838;;
1839;; file I/O hooks
1840;;
1841
1842;;;###autoload
1843(defun ethio-find-file nil
1844  "Transcribe file content into Ethiopic depending on filename suffix."
1845  (cond
1846
1847   ((string-match "\\.sera$" (buffer-file-name))
1848    (save-excursion
1849      (ethio-sera-to-fidel-buffer nil 'force)
1850      (set-buffer-modified-p nil)))
1851
1852   ((string-match "\\.html$" (buffer-file-name))
1853    (let ((ethio-sera-being-called-by-w3 t))
1854      (save-excursion
1855	(ethio-sera-to-fidel-marker 'force)
1856	(goto-char (point-min))
1857	(while (re-search-forward "&[lr]aquote;" nil t)
1858	  (if (= (char-after (1+ (match-beginning 0))) ?l)
1859	      (replace-match "$(3%v(B")
1860	    (replace-match "$(3%w(B")))
1861	(set-buffer-modified-p nil))))
1862
1863   ((string-match "\\.tex$" (buffer-file-name))
1864    (save-excursion
1865      (ethio-tex-to-fidel-buffer)
1866      (set-buffer-modified-p nil)))
1867
1868   ((string-match "\\.java$" (buffer-file-name))
1869    (save-excursion
1870      (ethio-java-to-fidel-buffer)
1871      (set-buffer-modified-p nil)))
1872
1873   (t
1874    nil)))
1875
1876;;;###autoload
1877(defun ethio-write-file nil
1878  "Transcribe Ethiopic characters in ASCII depending on the file extension."
1879  (cond
1880
1881   ((string-match "\\.sera$" (buffer-file-name))
1882    (save-excursion
1883      (ethio-fidel-to-sera-buffer nil 'force)
1884      (goto-char (point-min))
1885      (ethio-record-user-preference)
1886      (set-buffer-modified-p nil)))
1887
1888   ((string-match "\\.html$" (buffer-file-name))
1889    (save-excursion
1890      (let ((ethio-sera-being-called-by-w3 t)
1891	    (lq (aref ethio-fidel-to-sera-map 461))
1892	    (rq (aref ethio-fidel-to-sera-map 462)))
1893	(aset ethio-fidel-to-sera-map 461 "&laquote;")
1894	(aset ethio-fidel-to-sera-map 462 "&raquote;")
1895	(ethio-fidel-to-sera-marker 'force)
1896	(goto-char (point-min))
1897	(if (search-forward "<sera>" nil t)
1898	    (ethio-record-user-preference))
1899	(aset ethio-fidel-to-sera-map 461 lq)
1900	(aset ethio-fidel-to-sera-map 462 rq)
1901	(set-buffer-modified-p nil))))
1902
1903   ((string-match "\\.tex$" (buffer-file-name))
1904    (save-excursion
1905      (ethio-fidel-to-tex-buffer)
1906      (set-buffer-modified-p nil)))
1907
1908   ((string-match "\\.java$" (buffer-file-name))
1909    (save-excursion
1910      (ethio-fidel-to-java-buffer)
1911      (set-buffer-modified-p nil)))
1912
1913   (t
1914    nil)))
1915
1916(defun ethio-record-user-preference nil
1917  (if (looking-at "\\\\~\\(tir?\\|amh?\\) ")
1918      (goto-char (match-end 0))
1919    (insert (if (ethio-prefer-amharic-p) "\\~amh " "\\~tir ")))
1920  (insert (if ethio-use-colon-for-colon "\\~-: " "\\~`: ")
1921	  (if ethio-use-three-dot-question "\\~`| " "\\~`? ")))
1922
1923;;
1924;; Ethiopic word separator vs. ASCII space
1925;;
1926
1927(defvar ethio-prefer-ascii-space t)
1928(make-variable-buffer-local 'ethio-prefer-ascii-space)
1929
1930(defun ethio-toggle-space nil
1931  "Toggle ASCII space and Ethiopic separator for keyboard input."
1932  (interactive)
1933  (setq ethio-prefer-ascii-space
1934	(not ethio-prefer-ascii-space))
1935  (if (equal current-input-method "ethiopic")
1936      (setq current-input-method-title (quail-title)))
1937  (force-mode-line-update))
1938
1939(defun ethio-insert-space (arg)
1940  "Insert ASCII spaces or Ethiopic word separators depending on context.
1941
1942If the current word separator (indicated in mode-line) is the ASCII space,
1943insert an ASCII space.  With ARG, insert that many ASCII spaces.
1944
1945If the current word separator is the colon-like Ethiopic word
1946separator and the point is preceded by `an Ethiopic punctuation mark
1947followed by zero or more ASCII spaces', then insert also an ASCII
1948space.  With ARG, insert that many ASCII spaces.
1949
1950Otherwise, insert a colon-like Ethiopic word separator.  With ARG, insert that
1951many Ethiopic word separators."
1952
1953  (interactive "*p")
1954  (cond
1955   (ethio-prefer-ascii-space
1956    (insert-char 32 arg))
1957   ((save-excursion
1958      (skip-chars-backward " ")
1959      (memq (preceding-char)
1960	    '(?$(3$h(B ?$(3$i(B ?$(3$j(B ?$(3$k(B ?$(3$l(B ?$(3$m(B ?$(3$n(B ?$(3$o(B ?$(3%t(B ?$(3%u(B ?$(3%v(B ?$(3%w(B ?$(3%x(B)))
1961    (insert-char 32 arg))
1962   (t
1963    (insert-char ?$(3$h(B arg))))
1964
1965(defun ethio-insert-ethio-space (arg)
1966  "Insert the Ethiopic word delimiter (the colon-like character).
1967With ARG, insert that many delimiters."
1968  (interactive "*p")
1969  (insert-char ?$(3$h(B arg))
1970
1971;;
1972;; Ethiopic punctuation vs. ASCII punctuation
1973;;
1974
1975(defvar ethio-prefer-ascii-punctuation nil)
1976(make-variable-buffer-local 'ethio-prefer-ascii-punctuation)
1977
1978(defun ethio-toggle-punctuation nil
1979  "Toggle Ethiopic punctuations and ASCII punctuations for keyboard input."
1980  (interactive)
1981  (setq ethio-prefer-ascii-punctuation
1982	(not ethio-prefer-ascii-punctuation))
1983  (let* ((keys '("." ".." "..." "," ",," ";" ";;" ":" "::" ":::" "*" "**"))
1984	 (puncs
1985	  (if ethio-prefer-ascii-punctuation
1986	      '(?. [".."] ["..."] ?, [",,"] ?\; [";;"] ?: ["::"] [":::"] ?* ["**"])
1987	    '(?$(3$i(B ?$(3%u(B ?. ?$(3$j(B ?, ?$(3$k(B ?\; ?$(3$h(B ?$(3$i(B ?: ?* ?$(3$o(B))))
1988    (while keys
1989      (quail-defrule (car keys) (car puncs) "ethiopic")
1990      (setq keys (cdr keys)
1991	    puncs (cdr puncs)))
1992    (if (equal current-input-method "ethiopic")
1993	(setq current-input-method-title (quail-title)))
1994    (force-mode-line-update)))
1995
1996;;
1997;; Gemination
1998;;
1999
2000(defun ethio-gemination nil
2001  "Compose the character before the point with the Ethiopic gemination mark.
2002If the character is already composed, decompose it and remove the gemination
2003mark."
2004  (interactive "*")
2005  (cond
2006   ((eq (char-charset (preceding-char)) 'ethiopic)
2007    (insert "$(3%s(B")
2008    (compose-region
2009     (save-excursion (backward-char 2) (point))
2010     (point))
2011    (forward-char 1))
2012   ((eq (char-charset (preceding-char)) 'leading-code-composition)
2013    (decompose-region
2014     (save-excursion (backward-char 1) (point))
2015     (point))
2016    (delete-backward-char 1))
2017   (t
2018    (error ""))))
2019
2020;;
2021(provide 'ethio-util)
2022
2023;;; arch-tag: c8feb3d6-39bf-4b0a-b6ef-26f03fbc8140
2024;;; ethio-util.el ends here
2025