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