Add arch taglines
[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"
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);
d5db4077 71 len = SBYTES (obj);
5245463a
RS
72
73 /* Scan all single-byte characters from start of string. */
74 for (i = 0; i < len;)
dcfdbac7 75 {
d5db4077 76 c = SREF (obj, 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
f5b81cd8 93 SSET (obj, 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. */
d5db4077 110 bcopy (SDATA (obj), buf, i);
a0615d90 111
5245463a 112 /* From now on, I counts bytes. */
a0615d90
KH
113 while (i < len)
114 {
d5db4077 115 c = STRING_CHAR_AND_LENGTH (SDATA (obj) + i,
a0615d90
KH
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 }
d5db4077 127 obj = make_multibyte_string (buf, SCHARS (obj),
f16d5106 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 229 if ((int) flag >= (int) CASE_CAPITALIZE)
cdeb3480 230 inword = SYNTAX (c) == Sword && (inword || !SYNTAX_PREFIX (c));
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 */
a0615d90
KH
257 if (c < 0400 && c2 < 0400)
258 FETCH_BYTE (i) = c2;
66da2880
KH
259 else if (fromlen = CHAR_STRING (c, str),
260 tolen = CHAR_STRING (c2, str),
a04538da
KH
261 fromlen == tolen)
262 {
263 for (j = 0; j < tolen; ++j)
264 FETCH_BYTE (i + j) = str[j];
265 }
266 else
267 {
268 error ("Can't casify letters that change length");
269#if 0 /* This is approximately what we'd like to be able to do here */
270 if (tolen < fromlen)
7dae4502 271 del_range_1 (i + tolen, i + fromlen, 0, 0);
a04538da
KH
272 else if (tolen > fromlen)
273 {
274 TEMP_SET_PT (i + fromlen);
4c7b7eab 275 insert_1 (str + fromlen, tolen - fromlen, 1, 0, 0);
a04538da
KH
276 }
277#endif
278 }
279 }
280 if ((int) flag >= (int) CASE_CAPITALIZE)
a0615d90 281 inword = SYNTAX (c2) == Sword;
66da2880 282 INC_BOTH (start, i);
a04538da 283 }
4c7b7eab 284 TEMP_SET_PT_BOTH (opoint, opoint_byte);
dcfdbac7
JB
285 }
286
66da2880
KH
287 start = XFASTINT (b);
288 if (changed)
289 {
290 signal_after_change (start, end - start, end - start);
291 update_compositions (start, end, CHECK_ALL);
292 }
dcfdbac7
JB
293}
294
295DEFUN ("upcase-region", Fupcase_region, Supcase_region, 2, 2, "r",
fdb82f93
PJ
296 doc: /* Convert the region to upper case. In programs, wants two arguments.
297These arguments specify the starting and ending character numbers of
298the region to operate on. When used as a command, the text between
299point and the mark is operated on.
300See also `capitalize-region'. */)
301 (beg, end)
8c22d56c 302 Lisp_Object beg, end;
dcfdbac7 303{
8c22d56c 304 casify_region (CASE_UP, beg, end);
dcfdbac7
JB
305 return Qnil;
306}
307
308DEFUN ("downcase-region", Fdowncase_region, Sdowncase_region, 2, 2, "r",
fdb82f93
PJ
309 doc: /* Convert the region to lower case. In programs, wants two arguments.
310These arguments specify the starting and ending character numbers of
311the region to operate on. When used as a command, the text between
312point and the mark is operated on. */)
313 (beg, end)
8c22d56c 314 Lisp_Object beg, end;
dcfdbac7 315{
8c22d56c 316 casify_region (CASE_DOWN, beg, end);
dcfdbac7
JB
317 return Qnil;
318}
319
320DEFUN ("capitalize-region", Fcapitalize_region, Scapitalize_region, 2, 2, "r",
fdb82f93
PJ
321 doc: /* Convert the region to capitalized form.
322Capitalized form means each word's first character is upper case
323and the rest of it is lower case.
324In programs, give two arguments, the starting and ending
325character positions to operate on. */)
326 (beg, end)
8c22d56c 327 Lisp_Object beg, end;
dcfdbac7 328{
8c22d56c 329 casify_region (CASE_CAPITALIZE, beg, end);
dcfdbac7
JB
330 return Qnil;
331}
332
2371fad4
KH
333/* Like Fcapitalize_region but change only the initials. */
334
8cef1f78
RS
335DEFUN ("upcase-initials-region", Fupcase_initials_region,
336 Supcase_initials_region, 2, 2, "r",
fdb82f93
PJ
337 doc: /* Upcase the initial of each word in the region.
338Subsequent letters of each word are not changed.
339In programs, give two arguments, the starting and ending
340character positions to operate on. */)
341 (beg, end)
8c22d56c 342 Lisp_Object beg, end;
8cef1f78 343{
8c22d56c 344 casify_region (CASE_CAPITALIZE_UP, beg, end);
8cef1f78
RS
345 return Qnil;
346}
dcfdbac7
JB
347\f
348Lisp_Object
34628a90 349operate_on_word (arg, newpoint)
dcfdbac7 350 Lisp_Object arg;
34628a90 351 int *newpoint;
dcfdbac7 352{
39fb55ff 353 Lisp_Object val;
34628a90 354 int farend;
2371fad4 355 int iarg;
dcfdbac7 356
b7826503 357 CHECK_NUMBER (arg);
2371fad4 358 iarg = XINT (arg);
6ec8bbd2 359 farend = scan_words (PT, iarg);
dcfdbac7 360 if (!farend)
2371fad4 361 farend = iarg > 0 ? ZV : BEGV;
dcfdbac7 362
6ec8bbd2 363 *newpoint = PT > farend ? PT : farend;
18e23fd0 364 XSETFASTINT (val, farend);
dcfdbac7
JB
365
366 return val;
367}
368
369DEFUN ("upcase-word", Fupcase_word, Supcase_word, 1, 1, "p",
fdb82f93
PJ
370 doc: /* Convert following word (or ARG words) to upper case, moving over.
371With negative argument, convert previous words but do not move.
372See also `capitalize-word'. */)
373 (arg)
dcfdbac7
JB
374 Lisp_Object arg;
375{
34628a90
RS
376 Lisp_Object beg, end;
377 int newpoint;
6ec8bbd2 378 XSETFASTINT (beg, PT);
34628a90
RS
379 end = operate_on_word (arg, &newpoint);
380 casify_region (CASE_UP, beg, end);
381 SET_PT (newpoint);
dcfdbac7
JB
382 return Qnil;
383}
384
385DEFUN ("downcase-word", Fdowncase_word, Sdowncase_word, 1, 1, "p",
fdb82f93
PJ
386 doc: /* Convert following word (or ARG words) to lower case, moving over.
387With negative argument, convert previous words but do not move. */)
388 (arg)
dcfdbac7
JB
389 Lisp_Object arg;
390{
34628a90
RS
391 Lisp_Object beg, end;
392 int newpoint;
6ec8bbd2 393 XSETFASTINT (beg, PT);
34628a90
RS
394 end = operate_on_word (arg, &newpoint);
395 casify_region (CASE_DOWN, beg, end);
396 SET_PT (newpoint);
dcfdbac7
JB
397 return Qnil;
398}
399
400DEFUN ("capitalize-word", Fcapitalize_word, Scapitalize_word, 1, 1, "p",
fdb82f93
PJ
401 doc: /* Capitalize the following word (or ARG words), moving over.
402This gives the word(s) a first character in upper case
403and the rest lower case.
404With negative argument, capitalize previous words but do not move. */)
405 (arg)
dcfdbac7
JB
406 Lisp_Object arg;
407{
34628a90
RS
408 Lisp_Object beg, end;
409 int newpoint;
6ec8bbd2 410 XSETFASTINT (beg, PT);
34628a90
RS
411 end = operate_on_word (arg, &newpoint);
412 casify_region (CASE_CAPITALIZE, beg, end);
413 SET_PT (newpoint);
dcfdbac7
JB
414 return Qnil;
415}
416\f
dfcf069d 417void
dcfdbac7
JB
418syms_of_casefiddle ()
419{
a04538da
KH
420 Qidentity = intern ("identity");
421 staticpro (&Qidentity);
dcfdbac7
JB
422 defsubr (&Supcase);
423 defsubr (&Sdowncase);
424 defsubr (&Scapitalize);
8cef1f78 425 defsubr (&Supcase_initials);
dcfdbac7
JB
426 defsubr (&Supcase_region);
427 defsubr (&Sdowncase_region);
428 defsubr (&Scapitalize_region);
8cef1f78 429 defsubr (&Supcase_initials_region);
dcfdbac7
JB
430 defsubr (&Supcase_word);
431 defsubr (&Sdowncase_word);
432 defsubr (&Scapitalize_word);
433}
434
dfcf069d 435void
dcfdbac7
JB
436keys_of_casefiddle ()
437{
438 initial_define_key (control_x_map, Ctl('U'), "upcase-region");
d427b66a 439 Fput (intern ("upcase-region"), Qdisabled, Qt);
dcfdbac7 440 initial_define_key (control_x_map, Ctl('L'), "downcase-region");
d427b66a
JB
441 Fput (intern ("downcase-region"), Qdisabled, Qt);
442
dcfdbac7
JB
443 initial_define_key (meta_map, 'u', "upcase-word");
444 initial_define_key (meta_map, 'l', "downcase-word");
445 initial_define_key (meta_map, 'c', "capitalize-word");
446}
ab5796a9
MB
447
448/* arch-tag: 60a73c66-5489-47e7-a81f-cead4057c526
449 (do not change this comment) */