Use STRING_BYTES and SET_STRING_BYTES.
[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 }
5245463a 127 obj = make_multibyte_string (buf, j, 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
184casify_region (flag, b, e)
185 enum case_action flag;
186 Lisp_Object b, e;
187{
188 register int i;
189 register int c;
190 register int inword = flag == CASE_DOWN;
a0615d90 191 register int multibyte = !NILP (current_buffer->enable_multibyte_characters);
2371fad4 192 int start, end;
4c7b7eab 193 int start_byte, end_byte;
a04538da 194 Lisp_Object ch, downch, val;
dcfdbac7
JB
195
196 if (EQ (b, e))
197 /* Not modifying because nothing marked */
198 return;
199
bd47bd35
RS
200 /* If the case table is flagged as modified, rescan it. */
201 if (NILP (XCHAR_TABLE (current_buffer->downcase_table)->extras[1]))
202 Fset_case_table (current_buffer->downcase_table);
203
dcfdbac7 204 validate_region (&b, &e);
2371fad4
KH
205 start = XFASTINT (b);
206 end = XFASTINT (e);
207 modify_region (current_buffer, start, end);
208 record_change (start, end - start);
4c7b7eab
RS
209 start_byte = CHAR_TO_BYTE (start);
210 end_byte = CHAR_TO_BYTE (end);
dcfdbac7 211
4c7b7eab 212 for (i = start_byte; i < end_byte; i++)
a04538da 213 {
a0615d90
KH
214 c = FETCH_BYTE (i);
215 if (multibyte && c >= 0x80)
216 /* A multibyte character can't be handled in this simple loop. */
217 break;
218 if (inword && flag != CASE_CAPITALIZE_UP)
219 c = DOWNCASE (c);
220 else if (!UPPERCASEP (c)
221 && (!inword || flag != CASE_CAPITALIZE_UP))
222 c = UPCASE1 (c);
223 FETCH_BYTE (i) = c;
224 if ((int) flag >= (int) CASE_CAPITALIZE)
225 inword = SYNTAX (c) == Sword;
a04538da 226 }
4c7b7eab 227 if (i < end_byte)
dcfdbac7 228 {
a0615d90
KH
229 /* The work is not yet finished because of a multibyte character
230 just encountered. */
4c7b7eab
RS
231 int opoint = PT;
232 int opoint_byte = PT_BYTE;
233 int c2;
a04538da 234
4c7b7eab 235 while (i < end_byte)
a04538da 236 {
a0615d90
KH
237 if ((c = FETCH_BYTE (i)) >= 0x80)
238 c = FETCH_MULTIBYTE_CHAR (i);
239 c2 = c;
a04538da 240 if (inword && flag != CASE_CAPITALIZE_UP)
a0615d90
KH
241 c2 = DOWNCASE (c);
242 else if (!UPPERCASEP (c)
a04538da 243 && (!inword || flag != CASE_CAPITALIZE_UP))
a0615d90
KH
244 c2 = UPCASE1 (c);
245 if (c != c2)
a04538da
KH
246 {
247 int fromlen, tolen, j;
4c7b7eab 248 unsigned char workbuf[4], *str;
a04538da 249
a04538da 250 /* Handle the most likely case */
a0615d90
KH
251 if (c < 0400 && c2 < 0400)
252 FETCH_BYTE (i) = c2;
a04538da 253 else if (fromlen = CHAR_STRING (c, workbuf, str),
a0615d90 254 tolen = CHAR_STRING (c2, workbuf, str),
a04538da
KH
255 fromlen == tolen)
256 {
257 for (j = 0; j < tolen; ++j)
258 FETCH_BYTE (i + j) = str[j];
259 }
260 else
261 {
262 error ("Can't casify letters that change length");
263#if 0 /* This is approximately what we'd like to be able to do here */
264 if (tolen < fromlen)
265 del_range_1 (i + tolen, i + fromlen, 0);
266 else if (tolen > fromlen)
267 {
268 TEMP_SET_PT (i + fromlen);
4c7b7eab 269 insert_1 (str + fromlen, tolen - fromlen, 1, 0, 0);
a04538da
KH
270 }
271#endif
272 }
273 }
274 if ((int) flag >= (int) CASE_CAPITALIZE)
a0615d90 275 inword = SYNTAX (c2) == Sword;
a04538da
KH
276 INC_POS (i);
277 }
4c7b7eab 278 TEMP_SET_PT_BOTH (opoint, opoint_byte);
dcfdbac7
JB
279 }
280
2371fad4 281 signal_after_change (start, end - start, end - start);
dcfdbac7
JB
282}
283
284DEFUN ("upcase-region", Fupcase_region, Supcase_region, 2, 2, "r",
285 "Convert the region to upper case. In programs, wants two arguments.\n\
286These arguments specify the starting and ending character numbers of\n\
287the region to operate on. When used as a command, the text between\n\
288point and the mark is operated on.\n\
289See also `capitalize-region'.")
8c22d56c
EN
290 (beg, end)
291 Lisp_Object beg, end;
dcfdbac7 292{
8c22d56c 293 casify_region (CASE_UP, beg, end);
dcfdbac7
JB
294 return Qnil;
295}
296
297DEFUN ("downcase-region", Fdowncase_region, Sdowncase_region, 2, 2, "r",
298 "Convert the region to lower case. In programs, wants two arguments.\n\
299These arguments specify the starting and ending character numbers of\n\
300the region to operate on. When used as a command, the text between\n\
301point and the mark is operated on.")
8c22d56c
EN
302 (beg, end)
303 Lisp_Object beg, end;
dcfdbac7 304{
8c22d56c 305 casify_region (CASE_DOWN, beg, end);
dcfdbac7
JB
306 return Qnil;
307}
308
309DEFUN ("capitalize-region", Fcapitalize_region, Scapitalize_region, 2, 2, "r",
310 "Convert the region to capitalized form.\n\
311Capitalized form means each word's first character is upper case\n\
312and the rest of it is lower case.\n\
313In programs, give two arguments, the starting and ending\n\
314character positions to operate on.")
8c22d56c
EN
315 (beg, end)
316 Lisp_Object beg, end;
dcfdbac7 317{
8c22d56c 318 casify_region (CASE_CAPITALIZE, beg, end);
dcfdbac7
JB
319 return Qnil;
320}
321
2371fad4
KH
322/* Like Fcapitalize_region but change only the initials. */
323
8cef1f78
RS
324DEFUN ("upcase-initials-region", Fupcase_initials_region,
325 Supcase_initials_region, 2, 2, "r",
326 "Upcase the initial of each word in the region.\n\
327Subsequent letters of each word are not changed.\n\
328In programs, give two arguments, the starting and ending\n\
329character positions to operate on.")
8c22d56c
EN
330 (beg, end)
331 Lisp_Object beg, end;
8cef1f78 332{
8c22d56c 333 casify_region (CASE_CAPITALIZE_UP, beg, end);
8cef1f78
RS
334 return Qnil;
335}
dcfdbac7
JB
336\f
337Lisp_Object
34628a90 338operate_on_word (arg, newpoint)
dcfdbac7 339 Lisp_Object arg;
34628a90 340 int *newpoint;
dcfdbac7 341{
39fb55ff 342 Lisp_Object val;
34628a90 343 int farend;
2371fad4 344 int iarg;
dcfdbac7
JB
345
346 CHECK_NUMBER (arg, 0);
2371fad4 347 iarg = XINT (arg);
6ec8bbd2 348 farend = scan_words (PT, iarg);
dcfdbac7 349 if (!farend)
2371fad4 350 farend = iarg > 0 ? ZV : BEGV;
dcfdbac7 351
6ec8bbd2 352 *newpoint = PT > farend ? PT : farend;
18e23fd0 353 XSETFASTINT (val, farend);
dcfdbac7
JB
354
355 return val;
356}
357
358DEFUN ("upcase-word", Fupcase_word, Supcase_word, 1, 1, "p",
359 "Convert following word (or ARG words) to upper case, moving over.\n\
360With negative argument, convert previous words but do not move.\n\
361See also `capitalize-word'.")
362 (arg)
363 Lisp_Object arg;
364{
34628a90
RS
365 Lisp_Object beg, end;
366 int newpoint;
6ec8bbd2 367 XSETFASTINT (beg, PT);
34628a90
RS
368 end = operate_on_word (arg, &newpoint);
369 casify_region (CASE_UP, beg, end);
370 SET_PT (newpoint);
dcfdbac7
JB
371 return Qnil;
372}
373
374DEFUN ("downcase-word", Fdowncase_word, Sdowncase_word, 1, 1, "p",
375 "Convert following word (or ARG words) to lower case, moving over.\n\
376With negative argument, convert previous words but do not move.")
377 (arg)
378 Lisp_Object arg;
379{
34628a90
RS
380 Lisp_Object beg, end;
381 int newpoint;
6ec8bbd2 382 XSETFASTINT (beg, PT);
34628a90
RS
383 end = operate_on_word (arg, &newpoint);
384 casify_region (CASE_DOWN, beg, end);
385 SET_PT (newpoint);
dcfdbac7
JB
386 return Qnil;
387}
388
389DEFUN ("capitalize-word", Fcapitalize_word, Scapitalize_word, 1, 1, "p",
390 "Capitalize the following word (or ARG words), moving over.\n\
391This gives the word(s) a first character in upper case\n\
392and the rest lower case.\n\
393With negative argument, capitalize previous words but do not move.")
394 (arg)
395 Lisp_Object arg;
396{
34628a90
RS
397 Lisp_Object beg, end;
398 int newpoint;
6ec8bbd2 399 XSETFASTINT (beg, PT);
34628a90
RS
400 end = operate_on_word (arg, &newpoint);
401 casify_region (CASE_CAPITALIZE, beg, end);
402 SET_PT (newpoint);
dcfdbac7
JB
403 return Qnil;
404}
405\f
406syms_of_casefiddle ()
407{
a04538da
KH
408 Qidentity = intern ("identity");
409 staticpro (&Qidentity);
dcfdbac7
JB
410 defsubr (&Supcase);
411 defsubr (&Sdowncase);
412 defsubr (&Scapitalize);
8cef1f78 413 defsubr (&Supcase_initials);
dcfdbac7
JB
414 defsubr (&Supcase_region);
415 defsubr (&Sdowncase_region);
416 defsubr (&Scapitalize_region);
8cef1f78 417 defsubr (&Supcase_initials_region);
dcfdbac7
JB
418 defsubr (&Supcase_word);
419 defsubr (&Sdowncase_word);
420 defsubr (&Scapitalize_word);
421}
422
423keys_of_casefiddle ()
424{
425 initial_define_key (control_x_map, Ctl('U'), "upcase-region");
d427b66a 426 Fput (intern ("upcase-region"), Qdisabled, Qt);
dcfdbac7 427 initial_define_key (control_x_map, Ctl('L'), "downcase-region");
d427b66a
JB
428 Fput (intern ("downcase-region"), Qdisabled, Qt);
429
dcfdbac7
JB
430 initial_define_key (meta_map, 'u', "upcase-word");
431 initial_define_key (meta_map, 'l', "downcase-word");
432 initial_define_key (meta_map, 'c', "capitalize-word");
433}