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