(define-charset): New args :min-code and :max-code.
[bpt/emacs.git] / src / casefiddle.c
CommitLineData
dcfdbac7 1/* GNU Emacs case conversion functions.
4a2f9c6a 2 Copyright (C) 1985, 1994, 1997 Free Software Foundation, Inc.
dcfdbac7
JB
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
7c938215 8the Free Software Foundation; either version 2, or (at your option)
dcfdbac7
JB
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
3b7ad313
EN
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
dcfdbac7
JB
20
21
18160b98 22#include <config.h>
dcfdbac7
JB
23#include "lisp.h"
24#include "buffer.h"
83be827a 25#include "character.h"
dcfdbac7
JB
26#include "commands.h"
27#include "syntax.h"
66da2880 28#include "composite.h"
e35f6ff7 29#include "keymap.h"
dcfdbac7
JB
30
31enum case_action {CASE_UP, CASE_DOWN, CASE_CAPITALIZE, CASE_CAPITALIZE_UP};
a04538da
KH
32
33Lisp_Object Qidentity;
dcfdbac7
JB
34\f
35Lisp_Object
36casify_object (flag, obj)
37 enum case_action flag;
38 Lisp_Object obj;
39{
40 register int i, c, len;
41 register int inword = flag == CASE_DOWN;
42
bd47bd35
RS
43 /* If the case table is flagged as modified, rescan it. */
44 if (NILP (XCHAR_TABLE (current_buffer->downcase_table)->extras[1]))
45 Fset_case_table (current_buffer->downcase_table);
46
dcfdbac7
JB
47 while (1)
48 {
9d05e3d4 49 if (INTEGERP (obj))
dcfdbac7 50 {
e3a10b5e
KH
51 int flagbits = (CHAR_ALT | CHAR_SUPER | CHAR_HYPER
52 | CHAR_SHIFT | CHAR_CTL | CHAR_META);
53 int flags = XINT (obj) & flagbits;
54
55 c = DOWNCASE (XFASTINT (obj) & ~flagbits);
5dd4d40a 56 if (inword)
e3a10b5e
KH
57 XSETFASTINT (obj, c | flags);
58 else if (c == (XFASTINT (obj) & ~flagbits))
5dd4d40a 59 {
e3a10b5e
KH
60 c = UPCASE1 ((XFASTINT (obj) & ~flagbits));
61 XSETFASTINT (obj, c | flags);
5dd4d40a 62 }
dcfdbac7
JB
63 return obj;
64 }
5245463a 65
9d05e3d4 66 if (STRINGP (obj))
dcfdbac7 67 {
5245463a 68 int multibyte = STRING_MULTIBYTE (obj);
a0615d90 69
dcfdbac7 70 obj = Fcopy_sequence (obj);
fc932ac6 71 len = STRING_BYTES (XSTRING (obj));
5245463a
RS
72
73 /* Scan all single-byte characters from start of string. */
74 for (i = 0; i < len;)
dcfdbac7
JB
75 {
76 c = XSTRING (obj)->data[i];
5245463a 77
a0615d90
KH
78 if (multibyte && c >= 0x80)
79 /* A multibyte character can't be handled in this
80 simple loop. */
81 break;
96927ba4 82 if (inword && flag != CASE_CAPITALIZE_UP)
dcfdbac7 83 c = DOWNCASE (c);
96927ba4
RS
84 else if (!UPPERCASEP (c)
85 && (!inword || flag != CASE_CAPITALIZE_UP))
dcfdbac7 86 c = UPCASE1 (c);
5245463a
RS
87 /* If this char won't fit in a single-byte string.
88 fall out to the multibyte case. */
89 if (multibyte ? ! ASCII_BYTE_P (c)
90 : ! SINGLE_BYTE_CHAR_P (c))
91 break;
92
dcfdbac7 93 XSTRING (obj)->data[i] = c;
96927ba4 94 if ((int) flag >= (int) CASE_CAPITALIZE)
dcfdbac7 95 inword = SYNTAX (c) == Sword;
5245463a 96 i++;
dcfdbac7 97 }
5245463a
RS
98
99 /* If we didn't do the whole string as single-byte,
100 scan the rest in a more complex way. */
a0615d90
KH
101 if (i < len)
102 {
103 /* The work is not yet finished because of a multibyte
104 character just encountered. */
8d0941b6 105 int fromlen, j_byte = i;
a0615d90 106 char *buf
66da2880 107 = (char *) alloca ((len - i) * MAX_MULTIBYTE_LENGTH + i);
a0615d90
KH
108
109 /* Copy data already handled. */
110 bcopy (XSTRING (obj)->data, buf, i);
111
5245463a 112 /* From now on, I counts bytes. */
a0615d90
KH
113 while (i < len)
114 {
115 c = STRING_CHAR_AND_LENGTH (XSTRING (obj)->data + i,
116 len - i, fromlen);
117 if (inword && flag != CASE_CAPITALIZE_UP)
118 c = DOWNCASE (c);
119 else if (!UPPERCASEP (c)
120 && (!inword || flag != CASE_CAPITALIZE_UP))
121 c = UPCASE1 (c);
a0615d90 122 i += fromlen;
66da2880 123 j_byte += CHAR_STRING (c, buf + j_byte);
a0615d90
KH
124 if ((int) flag >= (int) CASE_CAPITALIZE)
125 inword = SYNTAX (c) == Sword;
126 }
f16d5106
KH
127 obj = make_multibyte_string (buf, XSTRING (obj)->size,
128 j_byte);
a0615d90 129 }
dcfdbac7
JB
130 return obj;
131 }
b37902c8 132 obj = wrong_type_argument (Qchar_or_string_p, obj);
dcfdbac7
JB
133 }
134}
135
136DEFUN ("upcase", Fupcase, Supcase, 1, 1, 0,
fdb82f93
PJ
137 doc: /* Convert argument to upper case and return that.
138The argument may be a character or string. The result has the same type.
139The argument object is not altered--the value is a copy.
140See also `capitalize', `downcase' and `upcase-initials'. */)
141 (obj)
dcfdbac7
JB
142 Lisp_Object obj;
143{
144 return casify_object (CASE_UP, obj);
145}
146
147DEFUN ("downcase", Fdowncase, Sdowncase, 1, 1, 0,
fdb82f93
PJ
148 doc: /* Convert argument to lower case and return that.
149The argument may be a character or string. The result has the same type.
150The argument object is not altered--the value is a copy. */)
151 (obj)
dcfdbac7
JB
152 Lisp_Object obj;
153{
154 return casify_object (CASE_DOWN, obj);
155}
156
157DEFUN ("capitalize", Fcapitalize, Scapitalize, 1, 1, 0,
fdb82f93
PJ
158 doc: /* Convert argument to capitalized form and return that.
159This means that each word's first character is upper case
160and the rest is lower case.
161The argument may be a character or string. The result has the same type.
162The argument object is not altered--the value is a copy. */)
163 (obj)
dcfdbac7
JB
164 Lisp_Object obj;
165{
166 return casify_object (CASE_CAPITALIZE, obj);
167}
96927ba4 168
2371fad4
KH
169/* Like Fcapitalize but change only the initials. */
170
8cef1f78 171DEFUN ("upcase-initials", Fupcase_initials, Supcase_initials, 1, 1, 0,
fdb82f93
PJ
172 doc: /* Convert the initial of each word in the argument to upper case.
173Do not change the other letters of each word.
174The argument may be a character or string. The result has the same type.
175The argument object is not altered--the value is a copy. */)
176 (obj)
8cef1f78
RS
177 Lisp_Object obj;
178{
179 return casify_object (CASE_CAPITALIZE_UP, obj);
180}
dcfdbac7
JB
181\f
182/* flag is CASE_UP, CASE_DOWN or CASE_CAPITALIZE or CASE_CAPITALIZE_UP.
183 b and e specify range of buffer to operate on. */
184
dfcf069d 185void
dcfdbac7
JB
186casify_region (flag, b, e)
187 enum case_action flag;
188 Lisp_Object b, e;
189{
190 register int i;
191 register int c;
192 register int inword = flag == CASE_DOWN;
a0615d90 193 register int multibyte = !NILP (current_buffer->enable_multibyte_characters);
2371fad4 194 int start, end;
4c7b7eab 195 int start_byte, end_byte;
66da2880 196 int changed = 0;
dcfdbac7
JB
197
198 if (EQ (b, e))
199 /* Not modifying because nothing marked */
200 return;
201
bd47bd35
RS
202 /* If the case table is flagged as modified, rescan it. */
203 if (NILP (XCHAR_TABLE (current_buffer->downcase_table)->extras[1]))
204 Fset_case_table (current_buffer->downcase_table);
205
dcfdbac7 206 validate_region (&b, &e);
2371fad4
KH
207 start = XFASTINT (b);
208 end = XFASTINT (e);
209 modify_region (current_buffer, start, end);
210 record_change (start, end - start);
4c7b7eab
RS
211 start_byte = CHAR_TO_BYTE (start);
212 end_byte = CHAR_TO_BYTE (end);
dcfdbac7 213
66da2880 214 for (i = start_byte; i < end_byte; i++, start++)
a04538da 215 {
66da2880
KH
216 int c2;
217 c = c2 = FETCH_BYTE (i);
a0615d90
KH
218 if (multibyte && c >= 0x80)
219 /* A multibyte character can't be handled in this simple loop. */
220 break;
221 if (inword && flag != CASE_CAPITALIZE_UP)
222 c = DOWNCASE (c);
223 else if (!UPPERCASEP (c)
224 && (!inword || flag != CASE_CAPITALIZE_UP))
225 c = UPCASE1 (c);
226 FETCH_BYTE (i) = c;
66da2880
KH
227 if (c != c2)
228 changed = 1;
a0615d90
KH
229 if ((int) flag >= (int) CASE_CAPITALIZE)
230 inword = SYNTAX (c) == Sword;
a04538da 231 }
4c7b7eab 232 if (i < end_byte)
dcfdbac7 233 {
a0615d90
KH
234 /* The work is not yet finished because of a multibyte character
235 just encountered. */
4c7b7eab
RS
236 int opoint = PT;
237 int opoint_byte = PT_BYTE;
238 int c2;
a04538da 239
4c7b7eab 240 while (i < end_byte)
a04538da 241 {
a0615d90
KH
242 if ((c = FETCH_BYTE (i)) >= 0x80)
243 c = FETCH_MULTIBYTE_CHAR (i);
244 c2 = c;
a04538da 245 if (inword && flag != CASE_CAPITALIZE_UP)
a0615d90
KH
246 c2 = DOWNCASE (c);
247 else if (!UPPERCASEP (c)
a04538da 248 && (!inword || flag != CASE_CAPITALIZE_UP))
a0615d90
KH
249 c2 = UPCASE1 (c);
250 if (c != c2)
a04538da
KH
251 {
252 int fromlen, tolen, j;
66da2880 253 unsigned char str[MAX_MULTIBYTE_LENGTH];
a04538da 254
66da2880 255 changed = 1;
a04538da 256 /* Handle the most likely case */
ed5d25fe
KH
257 if (multibyte ? (c < 0200 && c2 < 0200)
258 : (c < 0400 && c2 < 0400))
a0615d90 259 FETCH_BYTE (i) = c2;
66da2880
KH
260 else if (fromlen = CHAR_STRING (c, str),
261 tolen = CHAR_STRING (c2, str),
a04538da
KH
262 fromlen == tolen)
263 {
264 for (j = 0; j < tolen; ++j)
265 FETCH_BYTE (i + j) = str[j];
266 }
267 else
268 {
269 error ("Can't casify letters that change length");
270#if 0 /* This is approximately what we'd like to be able to do here */
271 if (tolen < fromlen)
7dae4502 272 del_range_1 (i + tolen, i + fromlen, 0, 0);
a04538da
KH
273 else if (tolen > fromlen)
274 {
275 TEMP_SET_PT (i + fromlen);
4c7b7eab 276 insert_1 (str + fromlen, tolen - fromlen, 1, 0, 0);
a04538da
KH
277 }
278#endif
279 }
280 }
281 if ((int) flag >= (int) CASE_CAPITALIZE)
a0615d90 282 inword = SYNTAX (c2) == Sword;
66da2880 283 INC_BOTH (start, i);
a04538da 284 }
4c7b7eab 285 TEMP_SET_PT_BOTH (opoint, opoint_byte);
dcfdbac7
JB
286 }
287
66da2880
KH
288 start = XFASTINT (b);
289 if (changed)
290 {
291 signal_after_change (start, end - start, end - start);
292 update_compositions (start, end, CHECK_ALL);
293 }
dcfdbac7
JB
294}
295
296DEFUN ("upcase-region", Fupcase_region, Supcase_region, 2, 2, "r",
fdb82f93
PJ
297 doc: /* Convert the region to upper case. In programs, wants two arguments.
298These arguments specify the starting and ending character numbers of
299the region to operate on. When used as a command, the text between
300point and the mark is operated on.
301See also `capitalize-region'. */)
302 (beg, end)
8c22d56c 303 Lisp_Object beg, end;
dcfdbac7 304{
8c22d56c 305 casify_region (CASE_UP, beg, end);
dcfdbac7
JB
306 return Qnil;
307}
308
309DEFUN ("downcase-region", Fdowncase_region, Sdowncase_region, 2, 2, "r",
fdb82f93
PJ
310 doc: /* Convert the region to lower case. In programs, wants two arguments.
311These arguments specify the starting and ending character numbers of
312the region to operate on. When used as a command, the text between
313point and the mark is operated on. */)
314 (beg, end)
8c22d56c 315 Lisp_Object beg, end;
dcfdbac7 316{
8c22d56c 317 casify_region (CASE_DOWN, beg, end);
dcfdbac7
JB
318 return Qnil;
319}
320
321DEFUN ("capitalize-region", Fcapitalize_region, Scapitalize_region, 2, 2, "r",
fdb82f93
PJ
322 doc: /* Convert the region to capitalized form.
323Capitalized form means each word's first character is upper case
324and the rest of it is lower case.
325In programs, give two arguments, the starting and ending
326character positions to operate on. */)
327 (beg, end)
8c22d56c 328 Lisp_Object beg, end;
dcfdbac7 329{
8c22d56c 330 casify_region (CASE_CAPITALIZE, beg, end);
dcfdbac7
JB
331 return Qnil;
332}
333
2371fad4
KH
334/* Like Fcapitalize_region but change only the initials. */
335
8cef1f78
RS
336DEFUN ("upcase-initials-region", Fupcase_initials_region,
337 Supcase_initials_region, 2, 2, "r",
fdb82f93
PJ
338 doc: /* Upcase the initial of each word in the region.
339Subsequent letters of each word are not changed.
340In programs, give two arguments, the starting and ending
341character positions to operate on. */)
342 (beg, end)
8c22d56c 343 Lisp_Object beg, end;
8cef1f78 344{
8c22d56c 345 casify_region (CASE_CAPITALIZE_UP, beg, end);
8cef1f78
RS
346 return Qnil;
347}
dcfdbac7
JB
348\f
349Lisp_Object
34628a90 350operate_on_word (arg, newpoint)
dcfdbac7 351 Lisp_Object arg;
34628a90 352 int *newpoint;
dcfdbac7 353{
39fb55ff 354 Lisp_Object val;
34628a90 355 int farend;
2371fad4 356 int iarg;
dcfdbac7 357
b7826503 358 CHECK_NUMBER (arg);
2371fad4 359 iarg = XINT (arg);
6ec8bbd2 360 farend = scan_words (PT, iarg);
dcfdbac7 361 if (!farend)
2371fad4 362 farend = iarg > 0 ? ZV : BEGV;
dcfdbac7 363
6ec8bbd2 364 *newpoint = PT > farend ? PT : farend;
18e23fd0 365 XSETFASTINT (val, farend);
dcfdbac7
JB
366
367 return val;
368}
369
370DEFUN ("upcase-word", Fupcase_word, Supcase_word, 1, 1, "p",
fdb82f93
PJ
371 doc: /* Convert following word (or ARG words) to upper case, moving over.
372With negative argument, convert previous words but do not move.
373See also `capitalize-word'. */)
374 (arg)
dcfdbac7
JB
375 Lisp_Object arg;
376{
34628a90
RS
377 Lisp_Object beg, end;
378 int newpoint;
6ec8bbd2 379 XSETFASTINT (beg, PT);
34628a90
RS
380 end = operate_on_word (arg, &newpoint);
381 casify_region (CASE_UP, beg, end);
382 SET_PT (newpoint);
dcfdbac7
JB
383 return Qnil;
384}
385
386DEFUN ("downcase-word", Fdowncase_word, Sdowncase_word, 1, 1, "p",
fdb82f93
PJ
387 doc: /* Convert following word (or ARG words) to lower case, moving over.
388With negative argument, convert previous words but do not move. */)
389 (arg)
dcfdbac7
JB
390 Lisp_Object arg;
391{
34628a90
RS
392 Lisp_Object beg, end;
393 int newpoint;
6ec8bbd2 394 XSETFASTINT (beg, PT);
34628a90
RS
395 end = operate_on_word (arg, &newpoint);
396 casify_region (CASE_DOWN, beg, end);
397 SET_PT (newpoint);
dcfdbac7
JB
398 return Qnil;
399}
400
401DEFUN ("capitalize-word", Fcapitalize_word, Scapitalize_word, 1, 1, "p",
fdb82f93
PJ
402 doc: /* Capitalize the following word (or ARG words), moving over.
403This gives the word(s) a first character in upper case
404and the rest lower case.
405With negative argument, capitalize previous words but do not move. */)
406 (arg)
dcfdbac7
JB
407 Lisp_Object arg;
408{
34628a90
RS
409 Lisp_Object beg, end;
410 int newpoint;
6ec8bbd2 411 XSETFASTINT (beg, PT);
34628a90
RS
412 end = operate_on_word (arg, &newpoint);
413 casify_region (CASE_CAPITALIZE, beg, end);
414 SET_PT (newpoint);
dcfdbac7
JB
415 return Qnil;
416}
417\f
dfcf069d 418void
dcfdbac7
JB
419syms_of_casefiddle ()
420{
a04538da
KH
421 Qidentity = intern ("identity");
422 staticpro (&Qidentity);
dcfdbac7
JB
423 defsubr (&Supcase);
424 defsubr (&Sdowncase);
425 defsubr (&Scapitalize);
8cef1f78 426 defsubr (&Supcase_initials);
dcfdbac7
JB
427 defsubr (&Supcase_region);
428 defsubr (&Sdowncase_region);
429 defsubr (&Scapitalize_region);
8cef1f78 430 defsubr (&Supcase_initials_region);
dcfdbac7
JB
431 defsubr (&Supcase_word);
432 defsubr (&Sdowncase_word);
433 defsubr (&Scapitalize_word);
434}
435
dfcf069d 436void
dcfdbac7
JB
437keys_of_casefiddle ()
438{
439 initial_define_key (control_x_map, Ctl('U'), "upcase-region");
d427b66a 440 Fput (intern ("upcase-region"), Qdisabled, Qt);
dcfdbac7 441 initial_define_key (control_x_map, Ctl('L'), "downcase-region");
d427b66a
JB
442 Fput (intern ("downcase-region"), Qdisabled, Qt);
443
dcfdbac7
JB
444 initial_define_key (meta_map, 'u', "upcase-word");
445 initial_define_key (meta_map, 'l', "downcase-word");
446 initial_define_key (meta_map, 'c', "capitalize-word");
447}