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