(compute_trt_identity, compute_trt_shuffle): Add comments.
[bpt/emacs.git] / src / casetab.c
... / ...
CommitLineData
1/* GNU Emacs routines to deal with case tables.
2 Copyright (C) 1993, 1994 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
20
21/* Written by Howard Gayle. See chartab.c for details. */
22
23#include <config.h>
24#include "lisp.h"
25#include "buffer.h"
26#include "charset.h"
27
28Lisp_Object Qcase_table_p, Qcase_table;
29Lisp_Object Vascii_downcase_table, Vascii_upcase_table;
30Lisp_Object Vascii_canon_table, Vascii_eqv_table;
31
32static void compute_trt_inverse ();
33
34DEFUN ("case-table-p", Fcase_table_p, Scase_table_p, 1, 1, 0,
35 "Return t iff OBJECT is a case table.\n\
36See `set-case-table' for more information on these data structures.")
37 (object)
38 Lisp_Object object;
39{
40 Lisp_Object up, canon, eqv;
41
42 if (! CHAR_TABLE_P (object))
43 return Qnil;
44 if (! EQ (XCHAR_TABLE (object)->purpose, Qcase_table))
45 return Qnil;
46
47 up = XCHAR_TABLE (object)->extras[0];
48 canon = XCHAR_TABLE (object)->extras[1];
49 eqv = XCHAR_TABLE (object)->extras[2];
50
51 return ((NILP (up) || CHAR_TABLE_P (up))
52 && ((NILP (canon) && NILP (eqv))
53 || (CHAR_TABLE_P (canon)
54 && (NILP (eqv) || CHAR_TABLE_P (eqv))))
55 ? Qt : Qnil);
56}
57
58static Lisp_Object
59check_case_table (obj)
60 Lisp_Object obj;
61{
62 register Lisp_Object tem;
63
64 while (tem = Fcase_table_p (obj), NILP (tem))
65 obj = wrong_type_argument (Qcase_table_p, obj);
66 return (obj);
67}
68
69DEFUN ("current-case-table", Fcurrent_case_table, Scurrent_case_table, 0, 0, 0,
70 "Return the case table of the current buffer.")
71 ()
72{
73 return current_buffer->downcase_table;
74}
75
76DEFUN ("standard-case-table", Fstandard_case_table, Sstandard_case_table, 0, 0, 0,
77 "Return the standard case table.\n\
78This is the one used for new buffers.")
79 ()
80{
81 return Vascii_downcase_table;
82}
83
84static Lisp_Object set_case_table ();
85
86DEFUN ("set-case-table", Fset_case_table, Sset_case_table, 1, 1, 0,
87 "Select a new case table for the current buffer.\n\
88A case table is a char-table which maps characters\n\
89to their lower-case equivalents. It also has three \"extra\" slots\n\
90which may be additional char-tables or nil.\n\
91These slots are called UPCASE, CANONICALIZE and EQUIVALENCES.\n\
92UPCASE maps each character to its upper-case equivalent;\n\
93 if lower and upper case characters are in 1-1 correspondence,\n\
94 you may use nil and the upcase table will be deduced from DOWNCASE.\n\
95CANONICALIZE maps each character to a canonical equivalent;\n\
96 any two characters that are related by case-conversion have the same\n\
97 canonical equivalent character; it may be nil, in which case it is\n\
98 deduced from DOWNCASE and UPCASE.\n\
99EQUIVALENCES is a map that cyclicly permutes each equivalence class\n\
100 (of characters with the same canonical equivalent); it may be nil,\n\
101 in which case it is deduced from CANONICALIZE.")
102 (table)
103 Lisp_Object table;
104{
105 return set_case_table (table, 0);
106}
107
108DEFUN ("set-standard-case-table", Fset_standard_case_table, Sset_standard_case_table, 1, 1, 0,
109 "Select a new standard case table for new buffers.\n\
110See `set-case-table' for more info on case tables.")
111 (table)
112 Lisp_Object table;
113{
114 return set_case_table (table, 1);
115}
116
117static Lisp_Object
118set_case_table (table, standard)
119 Lisp_Object table;
120 int standard;
121{
122 Lisp_Object up, canon, eqv;
123
124 check_case_table (table);
125
126 up = XCHAR_TABLE (table)->extras[0];
127 canon = XCHAR_TABLE (table)->extras[1];
128 eqv = XCHAR_TABLE (table)->extras[2];
129
130 if (NILP (up))
131 {
132 up = Fmake_char_table (Qcase_table, Qnil);
133 compute_trt_inverse (table, up);
134 XCHAR_TABLE (table)->extras[0] = up;
135 }
136
137 if (NILP (canon))
138 {
139 register int i;
140 Lisp_Object *upvec = XCHAR_TABLE (up)->contents;
141 Lisp_Object *downvec = XCHAR_TABLE (table)->contents;
142
143 canon = Fmake_char_table (Qcase_table, Qnil);
144
145 /* Set up the CANON vector; for each character,
146 this sequence of upcasing and downcasing ought to
147 get the "preferred" lowercase equivalent. */
148 for (i = 0; i < CHAR_TABLE_SINGLE_BYTE_SLOTS; i++)
149 XCHAR_TABLE (canon)->contents[i] = downvec[upvec[downvec[i]]];
150 XCHAR_TABLE (table)->extras[1] = canon;
151 }
152
153 if (NILP (eqv))
154 {
155 eqv = Fmake_char_table (Qcase_table, Qnil);
156 compute_trt_inverse (canon, eqv);
157 XCHAR_TABLE (table)->extras[2] = eqv;
158 }
159
160 if (standard)
161 Vascii_downcase_table = table;
162 else
163 {
164 current_buffer->downcase_table = table;
165 current_buffer->upcase_table = up;
166 current_buffer->case_canon_table = canon;
167 current_buffer->case_eqv_table = eqv;
168 }
169
170 return table;
171}
172\f
173/* Using the scratch array at BYTES of which the first DEPTH elements
174 are already set, and using the multi-byte structure inherited from
175 TRT, make INVERSE be an identity mapping. That is, for each slot
176 that's indexed by a single byte, store that byte in INVERSE.
177 Where TRT has a subtable, make a corresponding subtable in INVERSE
178 and recursively initialize that subtable so that its elements are
179 the multi-byte characters that correspond to the index bytes.
180 This is the first step in generating an inverse mapping. */
181
182static void
183compute_trt_identity (bytes, depth, trt, inverse)
184 unsigned char *bytes;
185 int depth;
186 struct Lisp_Char_Table *trt, *inverse;
187{
188 register int i;
189 int lim = (depth == 0 ? CHAR_TABLE_ORDINARY_SLOTS : SUB_CHAR_TABLE_ORDINARY_SLOTS);
190
191 for (i = 0; i < lim; i++)
192 {
193 if (NATNUMP (trt->contents[i]))
194 {
195 bytes[depth] = i;
196 XSETFASTINT (inverse->contents[i],
197 (depth == 0 && i < CHAR_TABLE_SINGLE_BYTE_SLOTS ? i
198 : MAKE_NON_ASCII_CHAR (bytes[0], bytes[1], bytes[2])));
199 }
200 else if (SUB_CHAR_TABLE_P (trt->contents[i]))
201 {
202 bytes[depth] = i - 128;
203 inverse->contents[i] = make_sub_char_table (Qnil);
204 compute_trt_identity (bytes, depth + 1,
205 XCHAR_TABLE (trt->contents[i]),
206 XCHAR_TABLE (inverse->contents[i]));
207 }
208 else /* must be Qnil or Qidentity */
209 inverse->contents[i] = trt->contents[i];
210 }
211}
212
213/* Using the scratch array at BYTES of which the first DEPTH elements
214 are already set, permute the elements of INVERSE (which is initially
215 an identity mapping) so that it has one cycle for each equivalence
216 class induced by the translation table TRT. IBASE is the lispy
217 version of the outermost (depth 0) instance of INVERSE. */
218
219static void
220compute_trt_shuffle (bytes, depth, ibase, trt, inverse)
221 unsigned char *bytes;
222 int depth;
223 Lisp_Object ibase;
224 struct Lisp_Char_Table *trt, *inverse;
225{
226 register int i;
227 Lisp_Object j, tem, q;
228 int lim = (depth == 0 ? CHAR_TABLE_ORDINARY_SLOTS : SUB_CHAR_TABLE_ORDINARY_SLOTS);
229
230 for (i = 0; i < lim; i++)
231 {
232 bytes[depth] = i;
233 XSETFASTINT (j,
234 (depth == 0 && i < CHAR_TABLE_SINGLE_BYTE_SLOTS ? i
235 : MAKE_NON_ASCII_CHAR (bytes[0], bytes[1], bytes[2])));
236 q = trt->contents[i];
237 if (NATNUMP (q) && XFASTINT (q) != XFASTINT (j))
238 {
239 tem = Faref (ibase, q);
240 Faset (ibase, q, j);
241 Faset (ibase, j, tem);
242 }
243 else if (SUB_CHAR_TABLE_P (q))
244 {
245 bytes[depth] = i - 128;
246 compute_trt_shuffle (bytes, depth + 1, ibase,
247 XCHAR_TABLE (trt->contents[i]),
248 XCHAR_TABLE (inverse->contents[i]));
249 }
250 }
251}
252
253/* Given a translate table TRT, store the inverse mapping into INVERSE.
254 Since TRT is not one-to-one, INVERSE is not a simple mapping.
255 Instead, it divides the space of characters into equivalence classes.
256 All characters in a given class form one circular list, chained through
257 the elements of INVERSE. */
258
259static void
260compute_trt_inverse (trt, inv)
261 Lisp_Object trt, inv;
262{
263 unsigned char bytes[3];
264 compute_trt_identity (bytes, 0, XCHAR_TABLE (trt), XCHAR_TABLE (inv));
265 compute_trt_shuffle (bytes, 0, inv, XCHAR_TABLE (trt), XCHAR_TABLE (inv));
266}
267\f
268init_casetab_once ()
269{
270 register int i;
271 Lisp_Object down, up;
272 Qcase_table = intern ("case-table");
273 staticpro (&Qcase_table);
274
275 /* Intern this now in case it isn't already done.
276 Setting this variable twice is harmless.
277 But don't staticpro it here--that is done in alloc.c. */
278 Qchar_table_extra_slots = intern ("char-table-extra-slots");
279
280 /* Now we are ready to set up this property, so we can
281 create char tables. */
282 Fput (Qcase_table, Qchar_table_extra_slots, make_number (3));
283
284 down = Fmake_char_table (Qcase_table, Qnil);
285 Vascii_downcase_table = down;
286 XCHAR_TABLE (down)->purpose = Qcase_table;
287
288 for (i = 0; i < CHAR_TABLE_SINGLE_BYTE_SLOTS; i++)
289 XSETFASTINT (XCHAR_TABLE (down)->contents[i],
290 (i >= 'A' && i <= 'Z') ? i + ('a' - 'A') : i);
291
292 XCHAR_TABLE (down)->extras[1] = Fcopy_sequence (down);
293
294 up = Fmake_char_table (Qcase_table, Qnil);
295 XCHAR_TABLE (down)->extras[0] = up;
296
297 for (i = 0; i < CHAR_TABLE_SINGLE_BYTE_SLOTS; i++)
298 XSETFASTINT (XCHAR_TABLE (up)->contents[i],
299 ((i >= 'A' && i <= 'Z')
300 ? i + ('a' - 'A')
301 : ((i >= 'a' && i <= 'z')
302 ? i + ('A' - 'a')
303 : i)));
304
305 XCHAR_TABLE (down)->extras[2] = Fcopy_sequence (up);
306}
307
308syms_of_casetab ()
309{
310 Qcase_table_p = intern ("case-table-p");
311 staticpro (&Qcase_table_p);
312
313 staticpro (&Vascii_canon_table);
314 staticpro (&Vascii_downcase_table);
315 staticpro (&Vascii_eqv_table);
316 staticpro (&Vascii_upcase_table);
317
318 defsubr (&Scase_table_p);
319 defsubr (&Scurrent_case_table);
320 defsubr (&Sstandard_case_table);
321 defsubr (&Sset_case_table);
322 defsubr (&Sset_standard_case_table);
323}