Implement 'exact-integer?' and 'scm_is_exact_integer'.
[bpt/guile.git] / libguile / srfi-13.c
CommitLineData
63181a97
MV
1/* srfi-13.c --- SRFI-13 procedures for Guile
2 *
47153f29 3 * Copyright (C) 2001, 2004, 2005, 2006, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
63181a97
MV
4 *
5 * This library is free software; you can redistribute it and/or
53befeb7
NJ
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 3 of
8 * the License, or (at your option) any later version.
63181a97 9 *
53befeb7
NJ
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
63181a97
MV
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
53befeb7
NJ
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA
63181a97
MV
19 */
20
21
dbb605f5
LC
22#ifdef HAVE_CONFIG_H
23# include <config.h>
24#endif
25
63181a97 26#include <string.h>
f846bd1a
MG
27#include <unicase.h>
28#include <unictype.h>
63181a97
MV
29
30#include "libguile.h"
31
9fe717e2 32#include <libguile/deprecation.h>
63181a97
MV
33#include "libguile/srfi-13.h"
34#include "libguile/srfi-14.h"
35
63181a97
MV
36#define MY_VALIDATE_SUBSTRING_SPEC(pos_str, str, \
37 pos_start, start, c_start, \
38 pos_end, end, c_end) \
39 do { \
40 SCM_VALIDATE_STRING (pos_str, str); \
41 scm_i_get_substring_spec (scm_i_string_length (str), \
42 start, &c_start, end, &c_end); \
43 } while (0)
44
f846bd1a
MG
45#define MY_SUBF_VALIDATE_SUBSTRING_SPEC(fname, pos_str, str, \
46 pos_start, start, c_start, \
47 pos_end, end, c_end) \
48 do { \
49 SCM_ASSERT_TYPE (scm_is_string (str), str, pos_str, fname, "string"); \
50 scm_i_get_substring_spec (scm_i_string_length (str), \
51 start, &c_start, end, &c_end); \
52 } while (0)
53
54#define REF_IN_CHARSET(s, i, cs) \
55 (scm_is_true (scm_char_set_contains_p ((cs), SCM_MAKE_CHAR (scm_i_string_ref (s, i)))))
56
63181a97
MV
57SCM_DEFINE (scm_string_null_p, "string-null?", 1, 0, 0,
58 (SCM str),
59 "Return @code{#t} if @var{str}'s length is zero, and\n"
60 "@code{#f} otherwise.\n"
61 "@lisp\n"
62 "(string-null? \"\") @result{} #t\n"
63 "y @result{} \"foo\"\n"
64 "(string-null? y) @result{} #f\n"
65 "@end lisp")
66#define FUNC_NAME s_scm_string_null_p
67{
68 SCM_VALIDATE_STRING (1, str);
69 return scm_from_bool (scm_i_string_length (str) == 0);
70}
71#undef FUNC_NAME
72
3731fc67
MV
73#if 0
74static void
75race_error ()
76{
77 scm_misc_error (NULL, "race condition detected", SCM_EOL);
78}
79#endif
80
fdc6aebf 81SCM_DEFINE (scm_string_any, "string-any-c-code", 2, 2, 0,
63181a97 82 (SCM char_pred, SCM s, SCM start, SCM end),
69d2000d
MV
83"Check if @var{char_pred} is true for any character in string @var{s}.\n"
84"\n"
85"@var{char_pred} can be a character to check for any equal to that, or\n"
86"a character set (@pxref{Character Sets}) to check for any in that set,\n"
87"or a predicate procedure to call.\n"
88"\n"
89"For a procedure, calls @code{(@var{char_pred} c)} are made\n"
90"successively on the characters from @var{start} to @var{end}. If\n"
91"@var{char_pred} returns true (ie.@: non-@code{#f}), @code{string-any}\n"
92"stops and that return value is the return from @code{string-any}. The\n"
93"call on the last character (ie.@: at @math{@var{end}-1}), if that\n"
94"point is reached, is a tail call.\n"
95"\n"
96"If there are no characters in @var{s} (ie.@: @var{start} equals\n"
97"@var{end}) then the return is @code{#f}.\n")
63181a97
MV
98#define FUNC_NAME s_scm_string_any
99{
3731fc67
MV
100 size_t cstart, cend;
101 SCM res = SCM_BOOL_F;
63181a97 102
f846bd1a
MG
103 MY_VALIDATE_SUBSTRING_SPEC (2, s,
104 3, start, cstart,
105 4, end, cend);
63181a97
MV
106
107 if (SCM_CHARP (char_pred))
108 {
f846bd1a
MG
109 size_t i;
110 for (i = cstart; i < cend; i ++)
111 if (scm_i_string_ref (s, i) == SCM_CHAR (char_pred))
112 {
113 res = SCM_BOOL_T;
114 break;
115 }
63181a97
MV
116 }
117 else if (SCM_CHARSETP (char_pred))
118 {
1a82a460 119 size_t i;
63181a97 120 for (i = cstart; i < cend; i++)
f846bd1a 121 if (REF_IN_CHARSET (s, i, char_pred))
3731fc67
MV
122 {
123 res = SCM_BOOL_T;
124 break;
125 }
63181a97
MV
126 }
127 else
128 {
6c9e8a53
AW
129 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
130 char_pred, SCM_ARG1, FUNC_NAME);
63181a97 131
63181a97
MV
132 while (cstart < cend)
133 {
6c9e8a53 134 res = scm_call_1 (char_pred,
f846bd1a 135 SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97 136 if (scm_is_true (res))
3731fc67 137 break;
63181a97
MV
138 cstart++;
139 }
140 }
3731fc67
MV
141
142 scm_remember_upto_here_1 (s);
143 return res;
63181a97
MV
144}
145#undef FUNC_NAME
146
147
fdc6aebf 148SCM_DEFINE (scm_string_every, "string-every-c-code", 2, 2, 0,
63181a97 149 (SCM char_pred, SCM s, SCM start, SCM end),
69d2000d
MV
150"Check if @var{char_pred} is true for every character in string\n"
151"@var{s}.\n"
152"\n"
153"@var{char_pred} can be a character to check for every character equal\n"
154"to that, or a character set (@pxref{Character Sets}) to check for\n"
155"every character being in that set, or a predicate procedure to call.\n"
156"\n"
157"For a procedure, calls @code{(@var{char_pred} c)} are made\n"
158"successively on the characters from @var{start} to @var{end}. If\n"
159"@var{char_pred} returns @code{#f}, @code{string-every} stops and\n"
160"returns @code{#f}. The call on the last character (ie.@: at\n"
161"@math{@var{end}-1}), if that point is reached, is a tail call and the\n"
162"return from that call is the return from @code{string-every}.\n"
163"\n"
164"If there are no characters in @var{s} (ie.@: @var{start} equals\n"
165"@var{end}) then the return is @code{#t}.\n")
63181a97
MV
166#define FUNC_NAME s_scm_string_every
167{
3731fc67
MV
168 size_t cstart, cend;
169 SCM res = SCM_BOOL_T;
63181a97 170
f846bd1a
MG
171 MY_VALIDATE_SUBSTRING_SPEC (2, s,
172 3, start, cstart,
173 4, end, cend);
63181a97
MV
174 if (SCM_CHARP (char_pred))
175 {
1a82a460 176 size_t i;
63181a97 177 for (i = cstart; i < cend; i++)
f846bd1a 178 if (scm_i_string_ref (s, i) != SCM_CHAR (char_pred))
3731fc67
MV
179 {
180 res = SCM_BOOL_F;
181 break;
182 }
63181a97
MV
183 }
184 else if (SCM_CHARSETP (char_pred))
185 {
1a82a460 186 size_t i;
63181a97 187 for (i = cstart; i < cend; i++)
f846bd1a 188 if (!REF_IN_CHARSET (s, i, char_pred))
3731fc67
MV
189 {
190 res = SCM_BOOL_F;
191 break;
192 }
63181a97
MV
193 }
194 else
195 {
6c9e8a53
AW
196 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
197 char_pred, SCM_ARG1, FUNC_NAME);
63181a97 198
63181a97
MV
199 while (cstart < cend)
200 {
6c9e8a53 201 res = scm_call_1 (char_pred,
f846bd1a 202 SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97 203 if (scm_is_false (res))
3731fc67 204 break;
63181a97
MV
205 cstart++;
206 }
63181a97 207 }
3731fc67
MV
208
209 scm_remember_upto_here_1 (s);
210 return res;
63181a97
MV
211}
212#undef FUNC_NAME
213
214
215SCM_DEFINE (scm_string_tabulate, "string-tabulate", 2, 0, 0,
216 (SCM proc, SCM len),
217 "@var{proc} is an integer->char procedure. Construct a string\n"
218 "of size @var{len} by applying @var{proc} to each index to\n"
219 "produce the corresponding string element. The order in which\n"
220 "@var{proc} is applied to the indices is not specified.")
221#define FUNC_NAME s_scm_string_tabulate
222{
223 size_t clen, i;
224 SCM res;
225 SCM ch;
3540b915 226
6c9e8a53
AW
227 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)),
228 proc, SCM_ARG1, FUNC_NAME);
63181a97 229
53a468dd 230 SCM_ASSERT_RANGE (2, len, scm_to_int (len) >= 0);
63181a97 231 clen = scm_to_size_t (len);
63181a97 232
f846bd1a
MG
233 {
234 /* This function is more complicated than necessary for the sake
235 of speed. */
236 scm_t_wchar *buf = scm_malloc (clen * sizeof (scm_t_wchar));
237 int wide = 0;
238 i = 0;
239 while (i < clen)
240 {
6c9e8a53 241 ch = scm_call_1 (proc, scm_from_size_t (i));
f846bd1a
MG
242 if (!SCM_CHARP (ch))
243 {
244 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (proc));
245 }
246 if (SCM_CHAR (ch) > 255)
247 wide = 1;
248 buf[i] = SCM_CHAR (ch);
249 i++;
250 }
251 if (wide)
252 {
253 scm_t_wchar *wbuf = NULL;
190d4b0d 254 res = scm_i_make_wide_string (clen, &wbuf, 0);
f846bd1a
MG
255 memcpy (wbuf, buf, clen * sizeof (scm_t_wchar));
256 free (buf);
257 }
258 else
259 {
260 char *nbuf = NULL;
190d4b0d 261 res = scm_i_make_string (clen, &nbuf, 0);
f846bd1a
MG
262 for (i = 0; i < clen; i ++)
263 nbuf[i] = (unsigned char) buf[i];
264 free (buf);
265 }
266 }
267
63181a97
MV
268 return res;
269}
270#undef FUNC_NAME
271
272
273SCM_DEFINE (scm_substring_to_list, "string->list", 1, 2, 0,
274 (SCM str, SCM start, SCM end),
275 "Convert the string @var{str} into a list of characters.")
276#define FUNC_NAME s_scm_substring_to_list
277{
3731fc67 278 size_t cstart, cend;
f846bd1a 279 int narrow;
63181a97
MV
280 SCM result = SCM_EOL;
281
f846bd1a
MG
282 MY_VALIDATE_SUBSTRING_SPEC (1, str,
283 2, start, cstart,
284 3, end, cend);
285
286 /* This explicit narrow/wide logic (instead of just using
287 scm_i_string_ref) is for speed optimizaion. */
288 narrow = scm_i_is_narrow_string (str);
289 if (narrow)
63181a97 290 {
f846bd1a
MG
291 const char *buf = scm_i_string_chars (str);
292 while (cstart < cend)
293 {
294 cend--;
295 result = scm_cons (SCM_MAKE_CHAR (buf[cend]), result);
296 }
297 }
298 else
299 {
300 const scm_t_wchar *buf = scm_i_string_wide_chars (str);
301 while (cstart < cend)
302 {
303 cend--;
304 result = scm_cons (SCM_MAKE_CHAR (buf[cend]), result);
305 }
63181a97 306 }
3731fc67 307 scm_remember_upto_here_1 (str);
63181a97
MV
308 return result;
309}
310#undef FUNC_NAME
311
312/* We export scm_substring_to_list as "string->list" since it is
313 compatible and more general. This function remains for the benefit
314 of C code that used it.
315*/
316
317SCM
318scm_string_to_list (SCM str)
319{
320 return scm_substring_to_list (str, SCM_UNDEFINED, SCM_UNDEFINED);
321}
322
323SCM_DEFINE (scm_reverse_list_to_string, "reverse-list->string", 1, 0, 0,
324 (SCM chrs),
325 "An efficient implementation of @code{(compose string->list\n"
326 "reverse)}:\n"
327 "\n"
328 "@smalllisp\n"
329 "(reverse-list->string '(#\\a #\\B #\\c)) @result{} \"cBa\"\n"
330 "@end smalllisp")
331#define FUNC_NAME s_scm_reverse_list_to_string
332{
333 SCM result;
f846bd1a 334 long i = scm_ilength (chrs), j;
63181a97
MV
335 char *data;
336
337 if (i < 0)
338 SCM_WRONG_TYPE_ARG (1, chrs);
190d4b0d 339 result = scm_i_make_string (i, &data, 0);
63181a97
MV
340
341 {
f846bd1a
MG
342 SCM rest;
343 rest = chrs;
344 j = 0;
345 while (j < i && scm_is_pair (rest))
63181a97 346 {
f846bd1a
MG
347 SCM elt = SCM_CAR (rest);
348 SCM_VALIDATE_CHAR (SCM_ARGn, elt);
349 j++;
350 rest = SCM_CDR (rest);
351 }
352 rest = chrs;
353 j = i;
354 result = scm_i_string_start_writing (result);
355 while (j > 0 && scm_is_pair (rest))
356 {
357 SCM elt = SCM_CAR (rest);
358 scm_i_string_set_x (result, j-1, SCM_CHAR (elt));
359 rest = SCM_CDR (rest);
360 j--;
63181a97 361 }
f846bd1a 362 scm_i_string_stop_writing ();
63181a97 363 }
3731fc67 364
63181a97
MV
365 return result;
366}
367#undef FUNC_NAME
368
369
370SCM_SYMBOL (scm_sym_infix, "infix");
371SCM_SYMBOL (scm_sym_strict_infix, "strict-infix");
372SCM_SYMBOL (scm_sym_suffix, "suffix");
373SCM_SYMBOL (scm_sym_prefix, "prefix");
374
375SCM_DEFINE (scm_string_join, "string-join", 1, 2, 0,
376 (SCM ls, SCM delimiter, SCM grammar),
377 "Append the string in the string list @var{ls}, using the string\n"
b7e64f8b 378 "@var{delimiter} as a delimiter between the elements of @var{ls}.\n"
63181a97
MV
379 "@var{grammar} is a symbol which specifies how the delimiter is\n"
380 "placed between the strings, and defaults to the symbol\n"
381 "@code{infix}.\n"
382 "\n"
383 "@table @code\n"
384 "@item infix\n"
385 "Insert the separator between list elements. An empty string\n"
386 "will produce an empty list.\n"
3b80c358 387 "@item strict-infix\n"
63181a97
MV
388 "Like @code{infix}, but will raise an error if given the empty\n"
389 "list.\n"
390 "@item suffix\n"
391 "Insert the separator after every list element.\n"
392 "@item prefix\n"
393 "Insert the separator before each list element.\n"
394 "@end table")
395#define FUNC_NAME s_scm_string_join
396{
786ab425
MW
397 SCM append_list = SCM_EOL;
398 long list_len = scm_ilength (ls);
399 size_t delimiter_len = 0;
63181a97
MV
400
401 /* Validate the string list. */
786ab425 402 if (list_len < 0)
63181a97
MV
403 SCM_WRONG_TYPE_ARG (1, ls);
404
405 /* Validate the delimiter and record its length. */
406 if (SCM_UNBNDP (delimiter))
407 {
408 delimiter = scm_from_locale_string (" ");
786ab425 409 delimiter_len = 1;
63181a97
MV
410 }
411 else
f846bd1a
MG
412 {
413 SCM_VALIDATE_STRING (2, delimiter);
786ab425 414 delimiter_len = scm_i_string_length (delimiter);
f846bd1a 415 }
63181a97 416
786ab425 417 /* Validate the grammar symbol. */
63181a97 418 if (SCM_UNBNDP (grammar))
786ab425
MW
419 grammar = scm_sym_infix;
420 else if (!(scm_is_eq (grammar, scm_sym_infix)
421 || scm_is_eq (grammar, scm_sym_strict_infix)
422 || scm_is_eq (grammar, scm_sym_suffix)
423 || scm_is_eq (grammar, scm_sym_prefix)))
63181a97
MV
424 SCM_WRONG_TYPE_ARG (3, grammar);
425
786ab425
MW
426 if (list_len == 0)
427 {
428 if (scm_is_eq (grammar, scm_sym_strict_infix))
429 SCM_MISC_ERROR ("strict-infix grammar requires non-empty list",
430 SCM_EOL);
431 else
432 /* Handle empty lists specially */
433 append_list = SCM_EOL;
434 }
435 else if (delimiter_len == 0)
436 /* Handle empty delimiters specially */
437 append_list = ls;
438 else
439 {
440 SCM *last_cdr_p = &append_list;
441
de2bc673
MW
442#define ADD_TO_APPEND_LIST(x) \
443 ((*last_cdr_p = scm_list_1 (x)), \
444 (last_cdr_p = SCM_CDRLOC (*last_cdr_p)))
63181a97 445
786ab425
MW
446 /* Build a list of strings to pass to 'string-append'.
447 Here we assume that 'ls' has at least one element. */
63181a97 448
786ab425
MW
449 /* If using the 'prefix' grammar, start with the delimiter. */
450 if (scm_is_eq (grammar, scm_sym_prefix))
451 ADD_TO_APPEND_LIST (delimiter);
452
453 /* Handle the first element of 'ls' specially, so that in the loop
454 that follows we can unconditionally insert the delimiter before
455 every remaining element. */
456 ADD_TO_APPEND_LIST (SCM_CAR (ls));
457 ls = SCM_CDR (ls);
458
459 /* Insert the delimiter before every remaining element. */
460 while (scm_is_pair (ls))
461 {
462 ADD_TO_APPEND_LIST (delimiter);
463 ADD_TO_APPEND_LIST (SCM_CAR (ls));
464 ls = SCM_CDR (ls);
465 }
466
467 /* If using the 'suffix' grammar, add the delimiter to the end. */
468 if (scm_is_eq (grammar, scm_sym_suffix))
469 ADD_TO_APPEND_LIST (delimiter);
470
471#undef ADD_TO_APPEND_LIST
63181a97 472 }
3731fc67 473
786ab425
MW
474 /* Construct the final result. */
475 return scm_string_append (append_list);
63181a97
MV
476}
477#undef FUNC_NAME
478
479
480/* There are a number of functions to consider here for Scheme and C:
481
482 string-copy STR [start [end]] ;; SRFI-13 variant of R5RS string-copy
483 substring/copy STR start [end] ;; Guile variant of R5RS substring
484
485 scm_string_copy (str) ;; Old function from Guile
486 scm_substring_copy (str, [start, [end]])
487 ;; C version of SRFI-13 string-copy
488 ;; and C version of substring/copy
489
490 The C function underlying string-copy is not exported to C
491 programs. scm_substring_copy is defined in strings.c as the
492 underlying function of substring/copy and allows an optional START
493 argument.
494*/
495
496SCM scm_srfi13_substring_copy (SCM str, SCM start, SCM end);
497
498SCM_DEFINE (scm_srfi13_substring_copy, "string-copy", 1, 2, 0,
499 (SCM str, SCM start, SCM end),
500 "Return a freshly allocated copy of the string @var{str}. If\n"
501 "given, @var{start} and @var{end} delimit the portion of\n"
502 "@var{str} which is copied.")
503#define FUNC_NAME s_scm_srfi13_substring_copy
504{
63181a97
MV
505 size_t cstart, cend;
506
f846bd1a
MG
507 MY_VALIDATE_SUBSTRING_SPEC (1, str,
508 2, start, cstart,
509 3, end, cend);
510 return scm_i_substring_copy (str, cstart, cend);
63181a97
MV
511}
512#undef FUNC_NAME
513
514SCM
515scm_string_copy (SCM str)
516{
f846bd1a
MG
517 if (!scm_is_string (str))
518 scm_wrong_type_arg ("scm_string_copy", 0, str);
519
520 return scm_i_substring (str, 0, scm_i_string_length (str));
63181a97
MV
521}
522
523SCM_DEFINE (scm_string_copy_x, "string-copy!", 3, 2, 0,
524 (SCM target, SCM tstart, SCM s, SCM start, SCM end),
525 "Copy the sequence of characters from index range [@var{start},\n"
526 "@var{end}) in string @var{s} to string @var{target}, beginning\n"
527 "at index @var{tstart}. The characters are copied left-to-right\n"
528 "or right-to-left as needed -- the copy is guaranteed to work,\n"
529 "even if @var{target} and @var{s} are the same string. It is an\n"
530 "error if the copy operation runs off the end of the target\n"
531 "string.")
532#define FUNC_NAME s_scm_string_copy_x
533{
f846bd1a 534 size_t cstart, cend, ctstart, dummy, len, i;
63181a97
MV
535 SCM sdummy = SCM_UNDEFINED;
536
537 MY_VALIDATE_SUBSTRING_SPEC (1, target,
538 2, tstart, ctstart,
539 2, sdummy, dummy);
f846bd1a
MG
540 MY_VALIDATE_SUBSTRING_SPEC (3, s,
541 4, start, cstart,
542 5, end, cend);
b2fb40a9 543 if (cstart < cend)
f846bd1a 544 {
b2fb40a9
MW
545 len = cend - cstart;
546 SCM_ASSERT_RANGE (3, s, len <= scm_i_string_length (target) - ctstart);
547
548 target = scm_i_string_start_writing (target);
549 for (i = 0; i < cend - cstart; i++)
550 {
551 scm_i_string_set_x (target, ctstart + i,
552 scm_i_string_ref (s, cstart + i));
553 }
554 scm_i_string_stop_writing ();
555 scm_remember_upto_here_1 (target);
f846bd1a 556 }
63181a97
MV
557
558 return SCM_UNSPECIFIED;
559}
560#undef FUNC_NAME
561
562SCM_DEFINE (scm_substring_move_x, "substring-move!", 5, 0, 0,
563 (SCM str1, SCM start1, SCM end1, SCM str2, SCM start2),
564 "Copy the substring of @var{str1} bounded by @var{start1} and @var{end1}\n"
565 "into @var{str2} beginning at position @var{start2}.\n"
566 "@var{str1} and @var{str2} can be the same string.")
567#define FUNC_NAME s_scm_substring_move_x
568{
569 return scm_string_copy_x (str2, start2, str1, start1, end1);
570}
571#undef FUNC_NAME
572
573SCM_DEFINE (scm_string_take, "string-take", 2, 0, 0,
574 (SCM s, SCM n),
575 "Return the @var{n} first characters of @var{s}.")
576#define FUNC_NAME s_scm_string_take
577{
578 return scm_substring (s, SCM_INUM0, n);
579}
580#undef FUNC_NAME
581
582
583SCM_DEFINE (scm_string_drop, "string-drop", 2, 0, 0,
584 (SCM s, SCM n),
585 "Return all but the first @var{n} characters of @var{s}.")
586#define FUNC_NAME s_scm_string_drop
587{
588 return scm_substring (s, n, SCM_UNDEFINED);
589}
590#undef FUNC_NAME
591
592
593SCM_DEFINE (scm_string_take_right, "string-take-right", 2, 0, 0,
594 (SCM s, SCM n),
595 "Return the @var{n} last characters of @var{s}.")
596#define FUNC_NAME s_scm_string_take_right
597{
598 return scm_substring (s,
599 scm_difference (scm_string_length (s), n),
600 SCM_UNDEFINED);
601}
602#undef FUNC_NAME
603
604
605SCM_DEFINE (scm_string_drop_right, "string-drop-right", 2, 0, 0,
606 (SCM s, SCM n),
607 "Return all but the last @var{n} characters of @var{s}.")
608#define FUNC_NAME s_scm_string_drop_right
609{
610 return scm_substring (s,
611 SCM_INUM0,
612 scm_difference (scm_string_length (s), n));
613}
614#undef FUNC_NAME
615
616
617SCM_DEFINE (scm_string_pad, "string-pad", 2, 3, 0,
618 (SCM s, SCM len, SCM chr, SCM start, SCM end),
619 "Take that characters from @var{start} to @var{end} from the\n"
620 "string @var{s} and return a new string, right-padded by the\n"
621 "character @var{chr} to length @var{len}. If the resulting\n"
622 "string is longer than @var{len}, it is truncated on the right.")
623#define FUNC_NAME s_scm_string_pad
624{
63181a97
MV
625 size_t cstart, cend, clen;
626
3731fc67
MV
627 MY_VALIDATE_SUBSTRING_SPEC (1, s,
628 4, start, cstart,
629 5, end, cend);
63181a97
MV
630 clen = scm_to_size_t (len);
631
632 if (SCM_UNBNDP (chr))
f846bd1a 633 chr = SCM_MAKE_CHAR (' ');
63181a97
MV
634 else
635 {
636 SCM_VALIDATE_CHAR (3, chr);
63181a97
MV
637 }
638 if (clen < (cend - cstart))
f846bd1a 639 return scm_i_substring (s, cend - clen, cend);
63181a97
MV
640 else
641 {
642 SCM result;
f846bd1a
MG
643 result = (scm_string_append
644 (scm_list_2 (scm_c_make_string (clen - (cend - cstart), chr),
645 scm_i_substring (s, cstart, cend))));
63181a97
MV
646 return result;
647 }
648}
649#undef FUNC_NAME
650
651
652SCM_DEFINE (scm_string_pad_right, "string-pad-right", 2, 3, 0,
653 (SCM s, SCM len, SCM chr, SCM start, SCM end),
654 "Take that characters from @var{start} to @var{end} from the\n"
655 "string @var{s} and return a new string, left-padded by the\n"
656 "character @var{chr} to length @var{len}. If the resulting\n"
657 "string is longer than @var{len}, it is truncated on the left.")
658#define FUNC_NAME s_scm_string_pad_right
659{
63181a97
MV
660 size_t cstart, cend, clen;
661
3731fc67
MV
662 MY_VALIDATE_SUBSTRING_SPEC (1, s,
663 4, start, cstart,
664 5, end, cend);
63181a97
MV
665 clen = scm_to_size_t (len);
666
667 if (SCM_UNBNDP (chr))
f846bd1a 668 chr = SCM_MAKE_CHAR (' ');
63181a97
MV
669 else
670 {
671 SCM_VALIDATE_CHAR (3, chr);
63181a97
MV
672 }
673 if (clen < (cend - cstart))
f846bd1a 674 return scm_i_substring (s, cstart, cstart + clen);
63181a97
MV
675 else
676 {
677 SCM result;
63181a97 678
f846bd1a
MG
679 result = (scm_string_append
680 (scm_list_2 (scm_i_substring (s, cstart, cend),
681 scm_c_make_string (clen - (cend - cstart), chr))));
682
63181a97
MV
683 return result;
684 }
685}
686#undef FUNC_NAME
687
688
689SCM_DEFINE (scm_string_trim, "string-trim", 1, 3, 0,
690 (SCM s, SCM char_pred, SCM start, SCM end),
691 "Trim @var{s} by skipping over all characters on the left\n"
692 "that satisfy the parameter @var{char_pred}:\n"
693 "\n"
694 "@itemize @bullet\n"
695 "@item\n"
696 "if it is the character @var{ch}, characters equal to\n"
697 "@var{ch} are trimmed,\n"
698 "\n"
699 "@item\n"
700 "if it is a procedure @var{pred} characters that\n"
701 "satisfy @var{pred} are trimmed,\n"
702 "\n"
703 "@item\n"
704 "if it is a character set, characters in that set are trimmed.\n"
705 "@end itemize\n"
706 "\n"
707 "If called without a @var{char_pred} argument, all whitespace is\n"
708 "trimmed.")
709#define FUNC_NAME s_scm_string_trim
710{
63181a97
MV
711 size_t cstart, cend;
712
f846bd1a
MG
713 MY_VALIDATE_SUBSTRING_SPEC (1, s,
714 3, start, cstart,
715 4, end, cend);
47153f29
AW
716 if (SCM_UNBNDP (char_pred)
717 || scm_is_eq (char_pred, scm_char_set_whitespace))
63181a97
MV
718 {
719 while (cstart < cend)
720 {
f846bd1a 721 if (!uc_is_c_whitespace (scm_i_string_ref (s, cstart)))
63181a97
MV
722 break;
723 cstart++;
724 }
725 }
726 else if (SCM_CHARP (char_pred))
727 {
63181a97
MV
728 while (cstart < cend)
729 {
f846bd1a 730 if (scm_i_string_ref (s, cstart) != SCM_CHAR (char_pred))
63181a97
MV
731 break;
732 cstart++;
733 }
734 }
735 else if (SCM_CHARSETP (char_pred))
736 {
737 while (cstart < cend)
738 {
f846bd1a 739 if (!REF_IN_CHARSET (s, cstart, char_pred))
63181a97
MV
740 break;
741 cstart++;
742 }
743 }
744 else
745 {
6c9e8a53
AW
746 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
747 char_pred, SCM_ARG2, FUNC_NAME);
3540b915 748
63181a97
MV
749 while (cstart < cend)
750 {
751 SCM res;
752
6c9e8a53 753 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97
MV
754 if (scm_is_false (res))
755 break;
63181a97
MV
756 cstart++;
757 }
758 }
f846bd1a 759 return scm_i_substring (s, cstart, cend);
63181a97
MV
760}
761#undef FUNC_NAME
762
763
764SCM_DEFINE (scm_string_trim_right, "string-trim-right", 1, 3, 0,
765 (SCM s, SCM char_pred, SCM start, SCM end),
f846bd1a 766 "Trim @var{s} by skipping over all characters on the right\n"
63181a97
MV
767 "that satisfy the parameter @var{char_pred}:\n"
768 "\n"
769 "@itemize @bullet\n"
770 "@item\n"
771 "if it is the character @var{ch}, characters equal to @var{ch}\n"
772 "are trimmed,\n"
773 "\n"
774 "@item\n"
775 "if it is a procedure @var{pred} characters that satisfy\n"
776 "@var{pred} are trimmed,\n"
777 "\n"
778 "@item\n"
779 "if it is a character sets, all characters in that set are\n"
780 "trimmed.\n"
781 "@end itemize\n"
782 "\n"
783 "If called without a @var{char_pred} argument, all whitespace is\n"
784 "trimmed.")
785#define FUNC_NAME s_scm_string_trim_right
786{
616775ed 787 size_t cstart, cend;
63181a97 788
f846bd1a
MG
789 MY_VALIDATE_SUBSTRING_SPEC (1, s,
790 3, start, cstart,
791 4, end, cend);
47153f29
AW
792 if (SCM_UNBNDP (char_pred)
793 || scm_is_eq (char_pred, scm_char_set_whitespace))
63181a97
MV
794 {
795 while (cstart < cend)
796 {
f846bd1a 797 if (!uc_is_c_whitespace (scm_i_string_ref (s, cend - 1)))
63181a97
MV
798 break;
799 cend--;
800 }
801 }
802 else if (SCM_CHARP (char_pred))
803 {
63181a97
MV
804 while (cstart < cend)
805 {
f846bd1a 806 if (scm_i_string_ref (s, cend - 1) != SCM_CHAR (char_pred))
63181a97
MV
807 break;
808 cend--;
809 }
810 }
811 else if (SCM_CHARSETP (char_pred))
812 {
813 while (cstart < cend)
814 {
f846bd1a 815 if (!REF_IN_CHARSET (s, cend-1, char_pred))
63181a97
MV
816 break;
817 cend--;
818 }
819 }
820 else
821 {
6c9e8a53
AW
822 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
823 char_pred, SCM_ARG2, FUNC_NAME);
3540b915 824
63181a97
MV
825 while (cstart < cend)
826 {
827 SCM res;
828
6c9e8a53 829 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cend - 1)));
63181a97
MV
830 if (scm_is_false (res))
831 break;
63181a97
MV
832 cend--;
833 }
834 }
f846bd1a 835 return scm_i_substring (s, cstart, cend);
63181a97
MV
836}
837#undef FUNC_NAME
838
839
840SCM_DEFINE (scm_string_trim_both, "string-trim-both", 1, 3, 0,
841 (SCM s, SCM char_pred, SCM start, SCM end),
842 "Trim @var{s} by skipping over all characters on both sides of\n"
843 "the string that satisfy the parameter @var{char_pred}:\n"
844 "\n"
845 "@itemize @bullet\n"
846 "@item\n"
847 "if it is the character @var{ch}, characters equal to @var{ch}\n"
848 "are trimmed,\n"
849 "\n"
850 "@item\n"
851 "if it is a procedure @var{pred} characters that satisfy\n"
852 "@var{pred} are trimmed,\n"
853 "\n"
854 "@item\n"
855 "if it is a character set, the characters in the set are\n"
856 "trimmed.\n"
857 "@end itemize\n"
858 "\n"
859 "If called without a @var{char_pred} argument, all whitespace is\n"
860 "trimmed.")
861#define FUNC_NAME s_scm_string_trim_both
862{
63181a97
MV
863 size_t cstart, cend;
864
f846bd1a
MG
865 MY_VALIDATE_SUBSTRING_SPEC (1, s,
866 3, start, cstart,
867 4, end, cend);
47153f29
AW
868 if (SCM_UNBNDP (char_pred)
869 || scm_is_eq (char_pred, scm_char_set_whitespace))
63181a97
MV
870 {
871 while (cstart < cend)
872 {
f846bd1a 873 if (!uc_is_c_whitespace (scm_i_string_ref (s, cstart)))
63181a97
MV
874 break;
875 cstart++;
876 }
877 while (cstart < cend)
878 {
f846bd1a 879 if (!uc_is_c_whitespace (scm_i_string_ref (s, cend - 1)))
63181a97
MV
880 break;
881 cend--;
882 }
883 }
884 else if (SCM_CHARP (char_pred))
885 {
63181a97
MV
886 while (cstart < cend)
887 {
f846bd1a 888 if (scm_i_string_ref (s, cstart) != SCM_CHAR(char_pred))
63181a97
MV
889 break;
890 cstart++;
891 }
892 while (cstart < cend)
893 {
f846bd1a 894 if (scm_i_string_ref (s, cend - 1) != SCM_CHAR (char_pred))
63181a97
MV
895 break;
896 cend--;
897 }
898 }
899 else if (SCM_CHARSETP (char_pred))
900 {
901 while (cstart < cend)
902 {
f846bd1a 903 if (!REF_IN_CHARSET (s, cstart, char_pred))
63181a97
MV
904 break;
905 cstart++;
906 }
907 while (cstart < cend)
908 {
f846bd1a 909 if (!REF_IN_CHARSET (s, cend-1, char_pred))
63181a97
MV
910 break;
911 cend--;
912 }
913 }
914 else
915 {
6c9e8a53
AW
916 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
917 char_pred, SCM_ARG2, FUNC_NAME);
3540b915 918
63181a97
MV
919 while (cstart < cend)
920 {
921 SCM res;
922
6c9e8a53 923 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97
MV
924 if (scm_is_false (res))
925 break;
63181a97
MV
926 cstart++;
927 }
928 while (cstart < cend)
929 {
930 SCM res;
931
6c9e8a53 932 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cend - 1)));
63181a97
MV
933 if (scm_is_false (res))
934 break;
63181a97
MV
935 cend--;
936 }
937 }
f846bd1a 938 return scm_i_substring (s, cstart, cend);
63181a97
MV
939}
940#undef FUNC_NAME
941
942
943SCM_DEFINE (scm_substring_fill_x, "string-fill!", 2, 2, 0,
944 (SCM str, SCM chr, SCM start, SCM end),
945 "Stores @var{chr} in every element of the given @var{str} and\n"
946 "returns an unspecified value.")
947#define FUNC_NAME s_scm_substring_fill_x
948{
63181a97 949 size_t cstart, cend;
63181a97
MV
950 size_t k;
951
952 /* Older versions of Guile provided the function
953 scm_substring_fill_x with the following order of arguments:
954
955 str, start, end, chr
956
957 We accomodate this here by detecting such a usage and reordering
958 the arguments.
959 */
960 if (SCM_CHARP (end))
961 {
962 SCM tmp = end;
963 end = start;
964 start = chr;
965 chr = tmp;
966 }
967
968 MY_VALIDATE_SUBSTRING_SPEC (1, str,
969 3, start, cstart,
970 4, end, cend);
f846bd1a
MG
971 SCM_VALIDATE_CHAR (2, chr);
972
b2fb40a9
MW
973 if (cstart < cend)
974 {
975 str = scm_i_string_start_writing (str);
976 for (k = cstart; k < cend; k++)
977 scm_i_string_set_x (str, k, SCM_CHAR (chr));
978 scm_i_string_stop_writing ();
979 }
3731fc67 980
63181a97
MV
981 return SCM_UNSPECIFIED;
982}
983#undef FUNC_NAME
984
985SCM
986scm_string_fill_x (SCM str, SCM chr)
987{
988 return scm_substring_fill_x (str, chr, SCM_UNDEFINED, SCM_UNDEFINED);
989}
990
991SCM_DEFINE (scm_string_compare, "string-compare", 5, 4, 0,
992 (SCM s1, SCM s2, SCM proc_lt, SCM proc_eq, SCM proc_gt, SCM start1, SCM end1, SCM start2, SCM end2),
993 "Apply @var{proc_lt}, @var{proc_eq}, @var{proc_gt} to the\n"
994 "mismatch index, depending upon whether @var{s1} is less than,\n"
995 "equal to, or greater than @var{s2}. The mismatch index is the\n"
996 "largest index @var{i} such that for every 0 <= @var{j} <\n"
997 "@var{i}, @var{s1}[@var{j}] = @var{s2}[@var{j}] -- that is,\n"
998 "@var{i} is the first position that does not match.")
999#define FUNC_NAME s_scm_string_compare
1000{
63181a97 1001 size_t cstart1, cend1, cstart2, cend2;
3731fc67 1002 SCM proc;
63181a97 1003
f846bd1a
MG
1004 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1005 6, start1, cstart1,
1006 7, end1, cend1);
1007 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1008 8, start2, cstart2,
1009 9, end2, cend2);
63181a97
MV
1010 SCM_VALIDATE_PROC (3, proc_lt);
1011 SCM_VALIDATE_PROC (4, proc_eq);
1012 SCM_VALIDATE_PROC (5, proc_gt);
1013
1014 while (cstart1 < cend1 && cstart2 < cend2)
1015 {
f846bd1a
MG
1016 if (scm_i_string_ref (s1, cstart1)
1017 < scm_i_string_ref (s2, cstart2))
3731fc67
MV
1018 {
1019 proc = proc_lt;
1020 goto ret;
1021 }
f846bd1a
MG
1022 else if (scm_i_string_ref (s1, cstart1)
1023 > scm_i_string_ref (s2, cstart2))
3731fc67
MV
1024 {
1025 proc = proc_gt;
1026 goto ret;
1027 }
63181a97
MV
1028 cstart1++;
1029 cstart2++;
1030 }
1031 if (cstart1 < cend1)
3731fc67 1032 proc = proc_gt;
63181a97 1033 else if (cstart2 < cend2)
3731fc67 1034 proc = proc_lt;
63181a97 1035 else
3731fc67
MV
1036 proc = proc_eq;
1037
1038 ret:
1039 scm_remember_upto_here_2 (s1, s2);
1040 return scm_call_1 (proc, scm_from_size_t (cstart1));
63181a97
MV
1041}
1042#undef FUNC_NAME
1043
1044
1045SCM_DEFINE (scm_string_compare_ci, "string-compare-ci", 5, 4, 0,
1046 (SCM s1, SCM s2, SCM proc_lt, SCM proc_eq, SCM proc_gt, SCM start1, SCM end1, SCM start2, SCM end2),
1047 "Apply @var{proc_lt}, @var{proc_eq}, @var{proc_gt} to the\n"
1048 "mismatch index, depending upon whether @var{s1} is less than,\n"
1049 "equal to, or greater than @var{s2}. The mismatch index is the\n"
1050 "largest index @var{i} such that for every 0 <= @var{j} <\n"
1051 "@var{i}, @var{s1}[@var{j}] = @var{s2}[@var{j}] -- that is,\n"
f846bd1a
MG
1052 "@var{i} is the first position where the lowercased letters \n"
1053 "do not match.\n")
63181a97
MV
1054#define FUNC_NAME s_scm_string_compare_ci
1055{
63181a97 1056 size_t cstart1, cend1, cstart2, cend2;
3731fc67 1057 SCM proc;
63181a97 1058
f846bd1a
MG
1059 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1060 6, start1, cstart1,
1061 7, end1, cend1);
1062 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1063 8, start2, cstart2,
1064 9, end2, cend2);
63181a97
MV
1065 SCM_VALIDATE_PROC (3, proc_lt);
1066 SCM_VALIDATE_PROC (4, proc_eq);
1067 SCM_VALIDATE_PROC (5, proc_gt);
1068
1069 while (cstart1 < cend1 && cstart2 < cend2)
1070 {
f846bd1a
MG
1071 if (uc_tolower (uc_toupper (scm_i_string_ref (s1, cstart1)))
1072 < uc_tolower (uc_toupper (scm_i_string_ref (s2, cstart2))))
3731fc67
MV
1073 {
1074 proc = proc_lt;
1075 goto ret;
1076 }
f846bd1a
MG
1077 else if (uc_tolower (uc_toupper (scm_i_string_ref (s1, cstart1)))
1078 > uc_tolower (uc_toupper (scm_i_string_ref (s2, cstart2))))
3731fc67
MV
1079 {
1080 proc = proc_gt;
1081 goto ret;
1082 }
63181a97
MV
1083 cstart1++;
1084 cstart2++;
1085 }
3731fc67 1086
63181a97 1087 if (cstart1 < cend1)
3731fc67 1088 proc = proc_gt;
63181a97 1089 else if (cstart2 < cend2)
3731fc67 1090 proc = proc_lt;
63181a97 1091 else
3731fc67
MV
1092 proc = proc_eq;
1093
1094 ret:
1095 scm_remember_upto_here (s1, s2);
1096 return scm_call_1 (proc, scm_from_size_t (cstart1));
63181a97
MV
1097}
1098#undef FUNC_NAME
1099
f846bd1a
MG
1100/* This function compares two substrings, S1 from START1 to END1 and
1101 S2 from START2 to END2, possibly case insensitively, and returns
4b1c3f0e
AW
1102 one of the parameters LESSTHAN, GREATERTHAN, SHORTER, LONGER, or
1103 EQUAL depending if S1 is less than S2, greater than S2, shorter,
1104 longer, or equal. */
f846bd1a
MG
1105static SCM
1106compare_strings (const char *fname, int case_insensitive,
1107 SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2,
4b1c3f0e 1108 SCM lessthan, SCM greaterthan, SCM shorter, SCM longer, SCM equal)
63181a97 1109{
63181a97 1110 size_t cstart1, cend1, cstart2, cend2;
f846bd1a
MG
1111 SCM ret;
1112 scm_t_wchar a, b;
63181a97 1113
f846bd1a 1114 MY_SUBF_VALIDATE_SUBSTRING_SPEC (fname, 1, s1,
63181a97
MV
1115 3, start1, cstart1,
1116 4, end1, cend1);
f846bd1a 1117 MY_SUBF_VALIDATE_SUBSTRING_SPEC (fname, 2, s2,
63181a97
MV
1118 5, start2, cstart2,
1119 6, end2, cend2);
1120
f846bd1a 1121 while (cstart1 < cend1 && cstart2 < cend2)
63181a97 1122 {
f846bd1a
MG
1123 if (case_insensitive)
1124 {
1125 a = uc_tolower (uc_toupper (scm_i_string_ref (s1, cstart1)));
1126 b = uc_tolower (uc_toupper (scm_i_string_ref (s2, cstart2)));
1127 }
1128 else
1129 {
1130 a = scm_i_string_ref (s1, cstart1);
1131 b = scm_i_string_ref (s2, cstart2);
1132 }
1133 if (a < b)
1134 {
1135 ret = lessthan;
1136 goto done;
1137 }
1138 else if (a > b)
1139 {
1140 ret = greaterthan;
1141 goto done;
1142 }
63181a97
MV
1143 cstart1++;
1144 cstart2++;
1145 }
f846bd1a
MG
1146 if (cstart1 < cend1)
1147 {
1148 ret = longer;
1149 goto done;
1150 }
1151 else if (cstart2 < cend2)
1152 {
1153 ret = shorter;
1154 goto done;
1155 }
1156 else
1157 {
1158 ret = equal;
1159 goto done;
1160 }
63181a97 1161
f846bd1a 1162 done:
63181a97 1163 scm_remember_upto_here_2 (s1, s2);
f846bd1a
MG
1164 return ret;
1165}
1166
1167
1168SCM_DEFINE (scm_string_eq, "string=", 2, 4, 0,
1169 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1170 "Return @code{#f} if @var{s1} and @var{s2} are not equal, a true\n"
1171 "value otherwise.")
1172#define FUNC_NAME s_scm_string_eq
1173{
7614c983
LC
1174 if (SCM_LIKELY (scm_is_string (s1) && scm_is_string (s2) &&
1175 scm_i_is_narrow_string (s1) == scm_i_is_narrow_string (s2)
0cc92ac4
LC
1176 && SCM_UNBNDP (start1) && SCM_UNBNDP (end1)
1177 && SCM_UNBNDP (start2) && SCM_UNBNDP (end2)))
1178 {
1179 /* Fast path for this common case, which avoids the repeated calls to
1180 `scm_i_string_ref'. */
1181 size_t len1, len2;
1182
1183 len1 = scm_i_string_length (s1);
1184 len2 = scm_i_string_length (s2);
1185
06fc34c2
MW
1186 if (len1 != len2)
1187 return SCM_BOOL_F;
1188 else
0cc92ac4
LC
1189 {
1190 if (!scm_i_is_narrow_string (s1))
1191 len1 *= 4;
1192
1193 return scm_from_bool (memcmp (scm_i_string_data (s1),
1194 scm_i_string_data (s2),
1195 len1) == 0);
1196 }
1197 }
1198
f846bd1a
MG
1199 return compare_strings (FUNC_NAME, 0,
1200 s1, s2, start1, end1, start2, end2,
1201 SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_T);
63181a97
MV
1202}
1203#undef FUNC_NAME
1204
1205
1206SCM_DEFINE (scm_string_neq, "string<>", 2, 4, 0,
1207 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1208 "Return @code{#f} if @var{s1} and @var{s2} are equal, a true\n"
1209 "value otherwise.")
1210#define FUNC_NAME s_scm_string_neq
1211{
f846bd1a
MG
1212 return compare_strings (FUNC_NAME, 0,
1213 s1, s2, start1, end1, start2, end2,
1214 SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_F);
63181a97
MV
1215}
1216#undef FUNC_NAME
1217
1218
1219SCM_DEFINE (scm_string_lt, "string<", 2, 4, 0,
1220 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1221 "Return @code{#f} if @var{s1} is greater or equal to @var{s2}, a\n"
1222 "true value otherwise.")
1223#define FUNC_NAME s_scm_string_lt
1224{
f846bd1a
MG
1225 return compare_strings (FUNC_NAME, 0,
1226 s1, s2, start1, end1, start2, end2,
1227 SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_F);
63181a97
MV
1228}
1229#undef FUNC_NAME
1230
1231
1232SCM_DEFINE (scm_string_gt, "string>", 2, 4, 0,
1233 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1234 "Return @code{#f} if @var{s1} is less or equal to @var{s2}, a\n"
1235 "true value otherwise.")
1236#define FUNC_NAME s_scm_string_gt
1237{
f846bd1a
MG
1238 return compare_strings (FUNC_NAME, 0,
1239 s1, s2, start1, end1, start2, end2,
1240 SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F);
63181a97
MV
1241}
1242#undef FUNC_NAME
1243
1244
1245SCM_DEFINE (scm_string_le, "string<=", 2, 4, 0,
1246 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1247 "Return @code{#f} if @var{s1} is greater to @var{s2}, a true\n"
1248 "value otherwise.")
1249#define FUNC_NAME s_scm_string_le
1250{
f846bd1a
MG
1251 return compare_strings (FUNC_NAME, 0,
1252 s1, s2, start1, end1, start2, end2,
1253 SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T);
63181a97
MV
1254}
1255#undef FUNC_NAME
1256
1257
1258SCM_DEFINE (scm_string_ge, "string>=", 2, 4, 0,
1259 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1260 "Return @code{#f} if @var{s1} is less to @var{s2}, a true value\n"
1261 "otherwise.")
1262#define FUNC_NAME s_scm_string_ge
1263{
f846bd1a
MG
1264 return compare_strings (FUNC_NAME, 0,
1265 s1, s2, start1, end1, start2, end2,
1266 SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_T);
63181a97
MV
1267}
1268#undef FUNC_NAME
1269
1270
1271SCM_DEFINE (scm_string_ci_eq, "string-ci=", 2, 4, 0,
1272 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1273 "Return @code{#f} if @var{s1} and @var{s2} are not equal, a true\n"
1274 "value otherwise. The character comparison is done\n"
1275 "case-insensitively.")
1276#define FUNC_NAME s_scm_string_ci_eq
1277{
f846bd1a
MG
1278 return compare_strings (FUNC_NAME, 1,
1279 s1, s2, start1, end1, start2, end2,
1280 SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_T);
63181a97
MV
1281}
1282#undef FUNC_NAME
1283
1284
1285SCM_DEFINE (scm_string_ci_neq, "string-ci<>", 2, 4, 0,
1286 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
f846bd1a
MG
1287 "Return @code{#f} if @var{s1} and @var{s2} are equal, a true\n"
1288 "value otherwise. The character comparison is done\n"
1289 "case-insensitively.")
1290#define FUNC_NAME s_scm_string_ci_neq
1291{
1292 return compare_strings (FUNC_NAME, 1,
1293 s1, s2, start1, end1, start2, end2,
1294 SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_F);
63181a97
MV
1295}
1296#undef FUNC_NAME
1297
1298
1299SCM_DEFINE (scm_string_ci_lt, "string-ci<", 2, 4, 0,
1300 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1301 "Return @code{#f} if @var{s1} is greater or equal to @var{s2}, a\n"
1302 "true value otherwise. The character comparison is done\n"
1303 "case-insensitively.")
1304#define FUNC_NAME s_scm_string_ci_lt
1305{
f846bd1a
MG
1306 return compare_strings (FUNC_NAME, 1,
1307 s1, s2, start1, end1, start2, end2,
1308 SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_F);
63181a97
MV
1309}
1310#undef FUNC_NAME
1311
1312
1313SCM_DEFINE (scm_string_ci_gt, "string-ci>", 2, 4, 0,
1314 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1315 "Return @code{#f} if @var{s1} is less or equal to @var{s2}, a\n"
1316 "true value otherwise. The character comparison is done\n"
1317 "case-insensitively.")
1318#define FUNC_NAME s_scm_string_ci_gt
1319{
f846bd1a
MG
1320 return compare_strings (FUNC_NAME, 1,
1321 s1, s2, start1, end1, start2, end2,
1322 SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F);
63181a97
MV
1323}
1324#undef FUNC_NAME
1325
1326
1327SCM_DEFINE (scm_string_ci_le, "string-ci<=", 2, 4, 0,
1328 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1329 "Return @code{#f} if @var{s1} is greater to @var{s2}, a true\n"
1330 "value otherwise. The character comparison is done\n"
1331 "case-insensitively.")
1332#define FUNC_NAME s_scm_string_ci_le
1333{
f846bd1a
MG
1334 return compare_strings (FUNC_NAME, 1,
1335 s1, s2, start1, end1, start2, end2,
1336 SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T);
63181a97
MV
1337}
1338#undef FUNC_NAME
1339
1340
1341SCM_DEFINE (scm_string_ci_ge, "string-ci>=", 2, 4, 0,
1342 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1343 "Return @code{#f} if @var{s1} is less to @var{s2}, a true value\n"
1344 "otherwise. The character comparison is done\n"
1345 "case-insensitively.")
1346#define FUNC_NAME s_scm_string_ci_ge
1347{
f846bd1a
MG
1348 return compare_strings (FUNC_NAME, 1,
1349 s1, s2, start1, end1, start2, end2,
1350 SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_T);
63181a97
MV
1351}
1352#undef FUNC_NAME
1353
1354SCM_DEFINE (scm_substring_hash, "string-hash", 1, 3, 0,
1355 (SCM s, SCM bound, SCM start, SCM end),
b7e64f8b 1356 "Compute a hash value for @var{s}. the optional argument "
63181a97
MV
1357 "@var{bound} is a non-negative exact "
1358 "integer specifying the range of the hash function. "
1359 "A positive value restricts the return value to the "
1360 "range [0,bound).")
1361#define FUNC_NAME s_scm_substring_hash
1362{
1363 if (SCM_UNBNDP (bound))
1364 bound = scm_from_intmax (SCM_MOST_POSITIVE_FIXNUM);
1365 if (SCM_UNBNDP (start))
1366 start = SCM_INUM0;
1367 return scm_hash (scm_substring_shared (s, start, end), bound);
1368}
1369#undef FUNC_NAME
1370
1371SCM_DEFINE (scm_substring_hash_ci, "string-hash-ci", 1, 3, 0,
1372 (SCM s, SCM bound, SCM start, SCM end),
b7e64f8b 1373 "Compute a hash value for @var{s}. the optional argument "
63181a97
MV
1374 "@var{bound} is a non-negative exact "
1375 "integer specifying the range of the hash function. "
1376 "A positive value restricts the return value to the "
1377 "range [0,bound).")
1378#define FUNC_NAME s_scm_substring_hash_ci
1379{
1380 return scm_substring_hash (scm_substring_downcase (s, start, end),
1381 bound,
1382 SCM_UNDEFINED, SCM_UNDEFINED);
1383}
1384#undef FUNC_NAME
1385
1386SCM_DEFINE (scm_string_prefix_length, "string-prefix-length", 2, 4, 0,
1387 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1388 "Return the length of the longest common prefix of the two\n"
1389 "strings.")
1390#define FUNC_NAME s_scm_string_prefix_length
1391{
63181a97
MV
1392 size_t cstart1, cend1, cstart2, cend2;
1393 size_t len = 0;
1394
f846bd1a
MG
1395 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1396 3, start1, cstart1,
1397 4, end1, cend1);
1398 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1399 5, start2, cstart2,
1400 6, end2, cend2);
1401
63181a97
MV
1402 while (cstart1 < cend1 && cstart2 < cend2)
1403 {
f846bd1a
MG
1404 if (scm_i_string_ref (s1, cstart1)
1405 != scm_i_string_ref (s2, cstart2))
3731fc67 1406 goto ret;
63181a97
MV
1407 len++;
1408 cstart1++;
1409 cstart2++;
1410 }
3731fc67
MV
1411
1412 ret:
1413 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1414 return scm_from_size_t (len);
1415}
1416#undef FUNC_NAME
1417
1418
1419SCM_DEFINE (scm_string_prefix_length_ci, "string-prefix-length-ci", 2, 4, 0,
1420 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1421 "Return the length of the longest common prefix of the two\n"
1422 "strings, ignoring character case.")
1423#define FUNC_NAME s_scm_string_prefix_length_ci
1424{
63181a97
MV
1425 size_t cstart1, cend1, cstart2, cend2;
1426 size_t len = 0;
1427
f846bd1a
MG
1428 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1429 3, start1, cstart1,
1430 4, end1, cend1);
1431 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1432 5, start2, cstart2,
1433 6, end2, cend2);
63181a97
MV
1434 while (cstart1 < cend1 && cstart2 < cend2)
1435 {
f846bd1a
MG
1436 if (uc_tolower (uc_toupper (scm_i_string_ref (s1, cstart1)))
1437 != uc_tolower (uc_toupper (scm_i_string_ref (s2, cstart2))))
3731fc67 1438 goto ret;
63181a97
MV
1439 len++;
1440 cstart1++;
1441 cstart2++;
1442 }
3731fc67
MV
1443
1444 ret:
1445 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1446 return scm_from_size_t (len);
1447}
1448#undef FUNC_NAME
1449
1450
1451SCM_DEFINE (scm_string_suffix_length, "string-suffix-length", 2, 4, 0,
1452 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1453 "Return the length of the longest common suffix of the two\n"
1454 "strings.")
1455#define FUNC_NAME s_scm_string_suffix_length
1456{
63181a97
MV
1457 size_t cstart1, cend1, cstart2, cend2;
1458 size_t len = 0;
1459
f846bd1a
MG
1460 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1461 3, start1, cstart1,
1462 4, end1, cend1);
1463 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1464 5, start2, cstart2,
1465 6, end2, cend2);
63181a97
MV
1466 while (cstart1 < cend1 && cstart2 < cend2)
1467 {
1468 cend1--;
1469 cend2--;
f846bd1a
MG
1470 if (scm_i_string_ref (s1, cend1)
1471 != scm_i_string_ref (s2, cend2))
3731fc67 1472 goto ret;
63181a97
MV
1473 len++;
1474 }
3731fc67
MV
1475
1476 ret:
1477 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1478 return scm_from_size_t (len);
1479}
1480#undef FUNC_NAME
1481
1482
1483SCM_DEFINE (scm_string_suffix_length_ci, "string-suffix-length-ci", 2, 4, 0,
1484 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1485 "Return the length of the longest common suffix of the two\n"
1486 "strings, ignoring character case.")
1487#define FUNC_NAME s_scm_string_suffix_length_ci
1488{
63181a97
MV
1489 size_t cstart1, cend1, cstart2, cend2;
1490 size_t len = 0;
1491
f846bd1a
MG
1492 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1493 3, start1, cstart1,
1494 4, end1, cend1);
1495 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1496 5, start2, cstart2,
1497 6, end2, cend2);
63181a97
MV
1498 while (cstart1 < cend1 && cstart2 < cend2)
1499 {
1500 cend1--;
1501 cend2--;
f846bd1a
MG
1502 if (uc_tolower (uc_toupper (scm_i_string_ref (s1, cend1)))
1503 != uc_tolower (uc_toupper (scm_i_string_ref (s2, cend2))))
3731fc67 1504 goto ret;
63181a97
MV
1505 len++;
1506 }
3731fc67
MV
1507
1508 ret:
1509 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1510 return scm_from_size_t (len);
1511}
1512#undef FUNC_NAME
1513
1514
1515SCM_DEFINE (scm_string_prefix_p, "string-prefix?", 2, 4, 0,
1516 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1517 "Is @var{s1} a prefix of @var{s2}?")
1518#define FUNC_NAME s_scm_string_prefix_p
1519{
63181a97
MV
1520 size_t cstart1, cend1, cstart2, cend2;
1521 size_t len = 0, len1;
1522
f846bd1a
MG
1523 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1524 3, start1, cstart1,
1525 4, end1, cend1);
1526 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1527 5, start2, cstart2,
1528 6, end2, cend2);
63181a97
MV
1529 len1 = cend1 - cstart1;
1530 while (cstart1 < cend1 && cstart2 < cend2)
1531 {
f846bd1a
MG
1532 if (scm_i_string_ref (s1, cstart1)
1533 != scm_i_string_ref (s2, cstart2))
3731fc67 1534 goto ret;
63181a97
MV
1535 len++;
1536 cstart1++;
1537 cstart2++;
1538 }
3731fc67
MV
1539
1540 ret:
1541 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1542 return scm_from_bool (len == len1);
1543}
1544#undef FUNC_NAME
1545
1546
1547SCM_DEFINE (scm_string_prefix_ci_p, "string-prefix-ci?", 2, 4, 0,
1548 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1549 "Is @var{s1} a prefix of @var{s2}, ignoring character case?")
1550#define FUNC_NAME s_scm_string_prefix_ci_p
1551{
63181a97
MV
1552 size_t cstart1, cend1, cstart2, cend2;
1553 size_t len = 0, len1;
1554
f846bd1a
MG
1555 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1556 3, start1, cstart1,
1557 4, end1, cend1);
1558 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1559 5, start2, cstart2,
1560 6, end2, cend2);
63181a97
MV
1561 len1 = cend1 - cstart1;
1562 while (cstart1 < cend1 && cstart2 < cend2)
1563 {
f846bd1a
MG
1564 scm_t_wchar a = uc_tolower (uc_toupper (scm_i_string_ref (s1, cstart1)));
1565 scm_t_wchar b = uc_tolower (uc_toupper (scm_i_string_ref (s2, cstart2)));
1566 if (a != b)
3731fc67 1567 goto ret;
63181a97
MV
1568 len++;
1569 cstart1++;
1570 cstart2++;
1571 }
3731fc67
MV
1572
1573 ret:
1574 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1575 return scm_from_bool (len == len1);
1576}
1577#undef FUNC_NAME
1578
1579
1580SCM_DEFINE (scm_string_suffix_p, "string-suffix?", 2, 4, 0,
1581 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1582 "Is @var{s1} a suffix of @var{s2}?")
1583#define FUNC_NAME s_scm_string_suffix_p
1584{
63181a97
MV
1585 size_t cstart1, cend1, cstart2, cend2;
1586 size_t len = 0, len1;
1587
f846bd1a
MG
1588 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1589 3, start1, cstart1,
1590 4, end1, cend1);
1591 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1592 5, start2, cstart2,
1593 6, end2, cend2);
63181a97
MV
1594 len1 = cend1 - cstart1;
1595 while (cstart1 < cend1 && cstart2 < cend2)
1596 {
1597 cend1--;
1598 cend2--;
f846bd1a
MG
1599 if (scm_i_string_ref (s1, cend1)
1600 != scm_i_string_ref (s2, cend2))
3731fc67 1601 goto ret;
63181a97
MV
1602 len++;
1603 }
3731fc67
MV
1604
1605 ret:
1606 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1607 return scm_from_bool (len == len1);
1608}
1609#undef FUNC_NAME
1610
1611
1612SCM_DEFINE (scm_string_suffix_ci_p, "string-suffix-ci?", 2, 4, 0,
1613 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1614 "Is @var{s1} a suffix of @var{s2}, ignoring character case?")
1615#define FUNC_NAME s_scm_string_suffix_ci_p
1616{
63181a97
MV
1617 size_t cstart1, cend1, cstart2, cend2;
1618 size_t len = 0, len1;
1619
f846bd1a
MG
1620 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1621 3, start1, cstart1,
1622 4, end1, cend1);
1623 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1624 5, start2, cstart2,
1625 6, end2, cend2);
63181a97
MV
1626 len1 = cend1 - cstart1;
1627 while (cstart1 < cend1 && cstart2 < cend2)
1628 {
1629 cend1--;
1630 cend2--;
f846bd1a
MG
1631 if (uc_tolower (uc_toupper (scm_i_string_ref (s1, cend1)))
1632 != uc_tolower (uc_toupper (scm_i_string_ref (s2, cend2))))
3731fc67 1633 goto ret;
63181a97
MV
1634 len++;
1635 }
3731fc67
MV
1636
1637 ret:
1638 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1639 return scm_from_bool (len == len1);
1640}
1641#undef FUNC_NAME
1642
1643
1644SCM_DEFINE (scm_string_index, "string-index", 2, 2, 0,
1645 (SCM s, SCM char_pred, SCM start, SCM end),
1646 "Search through the string @var{s} from left to right, returning\n"
ffb62a43 1647 "the index of the first occurrence of a character which\n"
63181a97
MV
1648 "\n"
1649 "@itemize @bullet\n"
1650 "@item\n"
1651 "equals @var{char_pred}, if it is character,\n"
1652 "\n"
1653 "@item\n"
ffb62a43 1654 "satisfies the predicate @var{char_pred}, if it is a procedure,\n"
63181a97
MV
1655 "\n"
1656 "@item\n"
1657 "is in the set @var{char_pred}, if it is a character set.\n"
bf7c2e96
LC
1658 "@end itemize\n\n"
1659 "Return @code{#f} if no match is found.")
63181a97
MV
1660#define FUNC_NAME s_scm_string_index
1661{
63181a97
MV
1662 size_t cstart, cend;
1663
f846bd1a
MG
1664 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1665 3, start, cstart,
1666 4, end, cend);
63181a97
MV
1667 if (SCM_CHARP (char_pred))
1668 {
63181a97
MV
1669 while (cstart < cend)
1670 {
f846bd1a 1671 if (scm_i_string_ref (s, cstart) == SCM_CHAR (char_pred))
3731fc67 1672 goto found;
63181a97
MV
1673 cstart++;
1674 }
1675 }
1676 else if (SCM_CHARSETP (char_pred))
1677 {
1678 while (cstart < cend)
1679 {
f846bd1a 1680 if (REF_IN_CHARSET (s, cstart, char_pred))
3731fc67 1681 goto found;
63181a97
MV
1682 cstart++;
1683 }
1684 }
1685 else
1686 {
6c9e8a53
AW
1687 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
1688 char_pred, SCM_ARG2, FUNC_NAME);
3540b915 1689
63181a97
MV
1690 while (cstart < cend)
1691 {
1692 SCM res;
6c9e8a53 1693 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97 1694 if (scm_is_true (res))
3731fc67 1695 goto found;
63181a97
MV
1696 cstart++;
1697 }
1698 }
3731fc67
MV
1699
1700 scm_remember_upto_here_1 (s);
63181a97 1701 return SCM_BOOL_F;
3731fc67
MV
1702
1703 found:
1704 scm_remember_upto_here_1 (s);
1705 return scm_from_size_t (cstart);
63181a97
MV
1706}
1707#undef FUNC_NAME
1708
1709SCM_DEFINE (scm_string_index_right, "string-index-right", 2, 2, 0,
1710 (SCM s, SCM char_pred, SCM start, SCM end),
1711 "Search through the string @var{s} from right to left, returning\n"
ffb62a43 1712 "the index of the last occurrence of a character which\n"
63181a97
MV
1713 "\n"
1714 "@itemize @bullet\n"
1715 "@item\n"
1716 "equals @var{char_pred}, if it is character,\n"
1717 "\n"
1718 "@item\n"
ffb62a43 1719 "satisfies the predicate @var{char_pred}, if it is a procedure,\n"
63181a97
MV
1720 "\n"
1721 "@item\n"
1722 "is in the set if @var{char_pred} is a character set.\n"
bf7c2e96
LC
1723 "@end itemize\n\n"
1724 "Return @code{#f} if no match is found.")
63181a97
MV
1725#define FUNC_NAME s_scm_string_index_right
1726{
63181a97
MV
1727 size_t cstart, cend;
1728
f846bd1a
MG
1729 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1730 3, start, cstart,
1731 4, end, cend);
63181a97
MV
1732 if (SCM_CHARP (char_pred))
1733 {
63181a97
MV
1734 while (cstart < cend)
1735 {
1736 cend--;
f846bd1a 1737 if (scm_i_string_ref (s, cend) == SCM_CHAR (char_pred))
3731fc67 1738 goto found;
63181a97
MV
1739 }
1740 }
1741 else if (SCM_CHARSETP (char_pred))
1742 {
1743 while (cstart < cend)
1744 {
1745 cend--;
f846bd1a 1746 if (REF_IN_CHARSET (s, cend, char_pred))
3731fc67 1747 goto found;
63181a97
MV
1748 }
1749 }
1750 else
1751 {
6c9e8a53
AW
1752 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
1753 char_pred, SCM_ARG2, FUNC_NAME);
3540b915 1754
63181a97
MV
1755 while (cstart < cend)
1756 {
1757 SCM res;
1758 cend--;
6c9e8a53 1759 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cend)));
63181a97 1760 if (scm_is_true (res))
3731fc67 1761 goto found;
63181a97
MV
1762 }
1763 }
3731fc67
MV
1764
1765 scm_remember_upto_here_1 (s);
63181a97 1766 return SCM_BOOL_F;
3731fc67
MV
1767
1768 found:
1769 scm_remember_upto_here_1 (s);
1770 return scm_from_size_t (cend);
63181a97
MV
1771}
1772#undef FUNC_NAME
1773
2562032b
MV
1774SCM_DEFINE (scm_string_rindex, "string-rindex", 2, 2, 0,
1775 (SCM s, SCM char_pred, SCM start, SCM end),
1776 "Search through the string @var{s} from right to left, returning\n"
ffb62a43 1777 "the index of the last occurrence of a character which\n"
2562032b
MV
1778 "\n"
1779 "@itemize @bullet\n"
1780 "@item\n"
1781 "equals @var{char_pred}, if it is character,\n"
1782 "\n"
1783 "@item\n"
ffb62a43 1784 "satisfies the predicate @var{char_pred}, if it is a procedure,\n"
2562032b
MV
1785 "\n"
1786 "@item\n"
1787 "is in the set if @var{char_pred} is a character set.\n"
bf7c2e96
LC
1788 "@end itemize\n\n"
1789 "Return @code{#f} if no match is found.")
3731fc67 1790#define FUNC_NAME s_scm_string_rindex
63181a97 1791{
2562032b 1792 return scm_string_index_right (s, char_pred, start, end);
63181a97 1793}
3731fc67 1794#undef FUNC_NAME
63181a97
MV
1795
1796SCM_DEFINE (scm_string_skip, "string-skip", 2, 2, 0,
1797 (SCM s, SCM char_pred, SCM start, SCM end),
1798 "Search through the string @var{s} from left to right, returning\n"
ffb62a43 1799 "the index of the first occurrence of a character which\n"
63181a97
MV
1800 "\n"
1801 "@itemize @bullet\n"
1802 "@item\n"
1803 "does not equal @var{char_pred}, if it is character,\n"
1804 "\n"
1805 "@item\n"
ffb62a43 1806 "does not satisfy the predicate @var{char_pred}, if it is a\n"
63181a97
MV
1807 "procedure,\n"
1808 "\n"
1809 "@item\n"
1810 "is not in the set if @var{char_pred} is a character set.\n"
1811 "@end itemize")
1812#define FUNC_NAME s_scm_string_skip
1813{
63181a97
MV
1814 size_t cstart, cend;
1815
f846bd1a
MG
1816 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1817 3, start, cstart,
1818 4, end, cend);
63181a97
MV
1819 if (SCM_CHARP (char_pred))
1820 {
63181a97
MV
1821 while (cstart < cend)
1822 {
f846bd1a 1823 if (scm_i_string_ref (s, cstart) != SCM_CHAR (char_pred))
3731fc67 1824 goto found;
63181a97
MV
1825 cstart++;
1826 }
1827 }
1828 else if (SCM_CHARSETP (char_pred))
1829 {
1830 while (cstart < cend)
1831 {
f846bd1a 1832 if (!REF_IN_CHARSET (s, cstart, char_pred))
3731fc67 1833 goto found;
63181a97
MV
1834 cstart++;
1835 }
1836 }
1837 else
1838 {
6c9e8a53
AW
1839 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
1840 char_pred, SCM_ARG2, FUNC_NAME);
3540b915 1841
63181a97
MV
1842 while (cstart < cend)
1843 {
1844 SCM res;
6c9e8a53 1845 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97 1846 if (scm_is_false (res))
3731fc67 1847 goto found;
63181a97
MV
1848 cstart++;
1849 }
1850 }
3731fc67
MV
1851
1852 scm_remember_upto_here_1 (s);
63181a97 1853 return SCM_BOOL_F;
3731fc67
MV
1854
1855 found:
1856 scm_remember_upto_here_1 (s);
1857 return scm_from_size_t (cstart);
63181a97
MV
1858}
1859#undef FUNC_NAME
1860
1861
1862SCM_DEFINE (scm_string_skip_right, "string-skip-right", 2, 2, 0,
1863 (SCM s, SCM char_pred, SCM start, SCM end),
1864 "Search through the string @var{s} from right to left, returning\n"
ffb62a43 1865 "the index of the last occurrence of a character which\n"
63181a97
MV
1866 "\n"
1867 "@itemize @bullet\n"
1868 "@item\n"
1869 "does not equal @var{char_pred}, if it is character,\n"
1870 "\n"
1871 "@item\n"
1872 "does not satisfy the predicate @var{char_pred}, if it is a\n"
1873 "procedure,\n"
1874 "\n"
1875 "@item\n"
1876 "is not in the set if @var{char_pred} is a character set.\n"
1877 "@end itemize")
1878#define FUNC_NAME s_scm_string_skip_right
1879{
63181a97
MV
1880 size_t cstart, cend;
1881
f846bd1a
MG
1882 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1883 3, start, cstart,
1884 4, end, cend);
63181a97
MV
1885 if (SCM_CHARP (char_pred))
1886 {
63181a97
MV
1887 while (cstart < cend)
1888 {
1889 cend--;
f846bd1a 1890 if (scm_i_string_ref (s, cend) != SCM_CHAR (char_pred))
3731fc67 1891 goto found;
63181a97
MV
1892 }
1893 }
1894 else if (SCM_CHARSETP (char_pred))
1895 {
1896 while (cstart < cend)
1897 {
1898 cend--;
f846bd1a 1899 if (!REF_IN_CHARSET (s, cend, char_pred))
3731fc67 1900 goto found;
63181a97
MV
1901 }
1902 }
1903 else
1904 {
6c9e8a53
AW
1905 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
1906 char_pred, SCM_ARG2, FUNC_NAME);
3540b915 1907
63181a97
MV
1908 while (cstart < cend)
1909 {
1910 SCM res;
1911 cend--;
6c9e8a53 1912 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cend)));
63181a97 1913 if (scm_is_false (res))
3731fc67 1914 goto found;
63181a97
MV
1915 }
1916 }
3731fc67
MV
1917
1918 scm_remember_upto_here_1 (s);
63181a97 1919 return SCM_BOOL_F;
3731fc67
MV
1920
1921 found:
1922 scm_remember_upto_here_1 (s);
1923 return scm_from_size_t (cend);
1924
63181a97
MV
1925}
1926#undef FUNC_NAME
1927
1928
1929SCM_DEFINE (scm_string_count, "string-count", 2, 2, 0,
1930 (SCM s, SCM char_pred, SCM start, SCM end),
1931 "Return the count of the number of characters in the string\n"
1932 "@var{s} which\n"
1933 "\n"
1934 "@itemize @bullet\n"
1935 "@item\n"
1936 "equals @var{char_pred}, if it is character,\n"
1937 "\n"
1938 "@item\n"
ffb62a43 1939 "satisfies the predicate @var{char_pred}, if it is a procedure.\n"
63181a97
MV
1940 "\n"
1941 "@item\n"
1942 "is in the set @var{char_pred}, if it is a character set.\n"
1943 "@end itemize")
1944#define FUNC_NAME s_scm_string_count
1945{
63181a97
MV
1946 size_t cstart, cend;
1947 size_t count = 0;
1948
f846bd1a
MG
1949 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1950 3, start, cstart,
1951 4, end, cend);
63181a97
MV
1952 if (SCM_CHARP (char_pred))
1953 {
63181a97
MV
1954 while (cstart < cend)
1955 {
f846bd1a 1956 if (scm_i_string_ref (s, cstart) == SCM_CHAR(char_pred))
63181a97
MV
1957 count++;
1958 cstart++;
1959 }
1960 }
1961 else if (SCM_CHARSETP (char_pred))
1962 {
1963 while (cstart < cend)
1964 {
f846bd1a 1965 if (REF_IN_CHARSET (s, cstart, char_pred))
63181a97
MV
1966 count++;
1967 cstart++;
1968 }
1969 }
1970 else
1971 {
6c9e8a53
AW
1972 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
1973 char_pred, SCM_ARG2, FUNC_NAME);
3540b915 1974
63181a97
MV
1975 while (cstart < cend)
1976 {
1977 SCM res;
6c9e8a53 1978 res = scm_call_1 (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97
MV
1979 if (scm_is_true (res))
1980 count++;
63181a97
MV
1981 cstart++;
1982 }
1983 }
3731fc67
MV
1984
1985 scm_remember_upto_here_1 (s);
63181a97
MV
1986 return scm_from_size_t (count);
1987}
1988#undef FUNC_NAME
1989
1990
1991/* FIXME::martin: This should definitely get implemented more
1992 efficiently -- maybe with Knuth-Morris-Pratt, like in the reference
1993 implementation. */
1994SCM_DEFINE (scm_string_contains, "string-contains", 2, 4, 0,
1995 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1996 "Does string @var{s1} contain string @var{s2}? Return the index\n"
1997 "in @var{s1} where @var{s2} occurs as a substring, or false.\n"
1998 "The optional start/end indices restrict the operation to the\n"
1999 "indicated substrings.")
2000#define FUNC_NAME s_scm_string_contains
2001{
63181a97
MV
2002 size_t cstart1, cend1, cstart2, cend2;
2003 size_t len2, i, j;
2004
f846bd1a
MG
2005 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
2006 3, start1, cstart1,
2007 4, end1, cend1);
2008 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
2009 5, start2, cstart2,
2010 6, end2, cend2);
63181a97 2011 len2 = cend2 - cstart2;
4a310f1c
MV
2012 if (cend1 - cstart1 >= len2)
2013 while (cstart1 <= cend1 - len2)
2014 {
2015 i = cstart1;
2016 j = cstart2;
f846bd1a
MG
2017 while (i < cend1
2018 && j < cend2
2019 && (scm_i_string_ref (s1, i)
2020 == scm_i_string_ref (s2, j)))
4a310f1c
MV
2021 {
2022 i++;
2023 j++;
2024 }
2025 if (j == cend2)
2026 {
2027 scm_remember_upto_here_2 (s1, s2);
2028 return scm_from_size_t (cstart1);
2029 }
2030 cstart1++;
2031 }
3731fc67
MV
2032
2033 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
2034 return SCM_BOOL_F;
2035}
2036#undef FUNC_NAME
2037
2038
2039/* FIXME::martin: This should definitely get implemented more
2040 efficiently -- maybe with Knuth-Morris-Pratt, like in the reference
2041 implementation. */
2042SCM_DEFINE (scm_string_contains_ci, "string-contains-ci", 2, 4, 0,
2043 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
2044 "Does string @var{s1} contain string @var{s2}? Return the index\n"
2045 "in @var{s1} where @var{s2} occurs as a substring, or false.\n"
2046 "The optional start/end indices restrict the operation to the\n"
2047 "indicated substrings. Character comparison is done\n"
2048 "case-insensitively.")
2049#define FUNC_NAME s_scm_string_contains_ci
2050{
63181a97
MV
2051 size_t cstart1, cend1, cstart2, cend2;
2052 size_t len2, i, j;
2053
f846bd1a
MG
2054 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
2055 3, start1, cstart1,
2056 4, end1, cend1);
2057 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
2058 5, start2, cstart2,
2059 6, end2, cend2);
63181a97 2060 len2 = cend2 - cstart2;
4a310f1c
MV
2061 if (cend1 - cstart1 >= len2)
2062 while (cstart1 <= cend1 - len2)
2063 {
2064 i = cstart1;
2065 j = cstart2;
f846bd1a
MG
2066 while (i < cend1
2067 && j < cend2
2068 && (uc_tolower (uc_toupper (scm_i_string_ref (s1, i)))
2069 == uc_tolower (uc_toupper (scm_i_string_ref (s2, j)))))
4a310f1c
MV
2070 {
2071 i++;
2072 j++;
2073 }
2074 if (j == cend2)
2075 {
2076 scm_remember_upto_here_2 (s1, s2);
2077 return scm_from_size_t (cstart1);
2078 }
2079 cstart1++;
2080 }
3731fc67
MV
2081
2082 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
2083 return SCM_BOOL_F;
2084}
2085#undef FUNC_NAME
2086
2087
f846bd1a 2088/* Helper function for the string uppercase conversion functions. */
63181a97 2089static SCM
1a82a460 2090string_upcase_x (SCM v, size_t start, size_t end)
63181a97
MV
2091{
2092 size_t k;
63181a97 2093
b2fb40a9
MW
2094 if (start < end)
2095 {
2096 v = scm_i_string_start_writing (v);
2097 for (k = start; k < end; ++k)
2098 scm_i_string_set_x (v, k, uc_toupper (scm_i_string_ref (v, k)));
2099 scm_i_string_stop_writing ();
2100 scm_remember_upto_here_1 (v);
2101 }
63181a97
MV
2102
2103 return v;
2104}
2105
2106SCM_DEFINE (scm_substring_upcase_x, "string-upcase!", 1, 2, 0,
2107 (SCM str, SCM start, SCM end),
2108 "Destructively upcase every character in @code{str}.\n"
2109 "\n"
2110 "@lisp\n"
2111 "(string-upcase! y)\n"
2112 "@result{} \"ARRDEFG\"\n"
2113 "y\n"
2114 "@result{} \"ARRDEFG\"\n"
2115 "@end lisp")
2116#define FUNC_NAME s_scm_substring_upcase_x
2117{
63181a97
MV
2118 size_t cstart, cend;
2119
f846bd1a
MG
2120 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2121 2, start, cstart,
2122 3, end, cend);
63181a97
MV
2123 return string_upcase_x (str, cstart, cend);
2124}
2125#undef FUNC_NAME
2126
2127SCM
2128scm_string_upcase_x (SCM str)
2129{
2130 return scm_substring_upcase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
2131}
2132
2133SCM_DEFINE (scm_substring_upcase, "string-upcase", 1, 2, 0,
2134 (SCM str, SCM start, SCM end),
2135 "Upcase every character in @code{str}.")
2136#define FUNC_NAME s_scm_substring_upcase
2137{
63181a97
MV
2138 size_t cstart, cend;
2139
f846bd1a
MG
2140 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2141 2, start, cstart,
2142 3, end, cend);
63181a97
MV
2143 return string_upcase_x (scm_string_copy (str), cstart, cend);
2144}
2145#undef FUNC_NAME
2146
2147SCM
2148scm_string_upcase (SCM str)
2149{
2150 return scm_substring_upcase (str, SCM_UNDEFINED, SCM_UNDEFINED);
2151}
2152
2153/* Helper function for the string lowercase conversion functions.
2154 * No argument checking is performed. */
2155static SCM
1a82a460 2156string_downcase_x (SCM v, size_t start, size_t end)
63181a97
MV
2157{
2158 size_t k;
63181a97 2159
b2fb40a9
MW
2160 if (start < end)
2161 {
2162 v = scm_i_string_start_writing (v);
2163 for (k = start; k < end; ++k)
2164 scm_i_string_set_x (v, k, uc_tolower (scm_i_string_ref (v, k)));
2165 scm_i_string_stop_writing ();
2166 scm_remember_upto_here_1 (v);
2167 }
63181a97
MV
2168
2169 return v;
2170}
2171
2172SCM_DEFINE (scm_substring_downcase_x, "string-downcase!", 1, 2, 0,
2173 (SCM str, SCM start, SCM end),
2174 "Destructively downcase every character in @var{str}.\n"
2175 "\n"
2176 "@lisp\n"
2177 "y\n"
2178 "@result{} \"ARRDEFG\"\n"
2179 "(string-downcase! y)\n"
2180 "@result{} \"arrdefg\"\n"
2181 "y\n"
2182 "@result{} \"arrdefg\"\n"
2183 "@end lisp")
2184#define FUNC_NAME s_scm_substring_downcase_x
2185{
63181a97
MV
2186 size_t cstart, cend;
2187
f846bd1a
MG
2188 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2189 2, start, cstart,
2190 3, end, cend);
63181a97
MV
2191 return string_downcase_x (str, cstart, cend);
2192}
2193#undef FUNC_NAME
2194
2195SCM
2196scm_string_downcase_x (SCM str)
2197{
2198 return scm_substring_downcase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
2199}
2200
2201SCM_DEFINE (scm_substring_downcase, "string-downcase", 1, 2, 0,
2202 (SCM str, SCM start, SCM end),
2203 "Downcase every character in @var{str}.")
2204#define FUNC_NAME s_scm_substring_downcase
2205{
63181a97
MV
2206 size_t cstart, cend;
2207
f846bd1a
MG
2208 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2209 2, start, cstart,
2210 3, end, cend);
63181a97
MV
2211 return string_downcase_x (scm_string_copy (str), cstart, cend);
2212}
2213#undef FUNC_NAME
2214
2215SCM
2216scm_string_downcase (SCM str)
2217{
2218 return scm_substring_downcase (str, SCM_UNDEFINED, SCM_UNDEFINED);
2219}
2220
2221/* Helper function for the string capitalization functions.
2222 * No argument checking is performed. */
2223static SCM
1a82a460 2224string_titlecase_x (SCM str, size_t start, size_t end)
63181a97 2225{
f846bd1a 2226 SCM ch;
63181a97
MV
2227 size_t i;
2228 int in_word = 0;
2229
b2fb40a9 2230 if (start < end)
63181a97 2231 {
b2fb40a9
MW
2232 str = scm_i_string_start_writing (str);
2233 for(i = start; i < end; i++)
2234 {
2235 ch = SCM_MAKE_CHAR (scm_i_string_ref (str, i));
2236 if (scm_is_true (scm_char_alphabetic_p (ch)))
2237 {
2238 if (!in_word)
2239 {
2240 scm_i_string_set_x (str, i, uc_totitle (SCM_CHAR (ch)));
2241 in_word = 1;
2242 }
2243 else
2244 {
2245 scm_i_string_set_x (str, i, uc_tolower (SCM_CHAR (ch)));
2246 }
2247 }
2248 else
2249 in_word = 0;
2250 }
2251 scm_i_string_stop_writing ();
2252 scm_remember_upto_here_1 (str);
63181a97 2253 }
63181a97
MV
2254
2255 return str;
2256}
2257
2258
2259SCM_DEFINE (scm_string_titlecase_x, "string-titlecase!", 1, 2, 0,
2260 (SCM str, SCM start, SCM end),
2261 "Destructively titlecase every first character in a word in\n"
2262 "@var{str}.")
2263#define FUNC_NAME s_scm_string_titlecase_x
2264{
63181a97
MV
2265 size_t cstart, cend;
2266
f846bd1a
MG
2267 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2268 2, start, cstart,
2269 3, end, cend);
63181a97
MV
2270 return string_titlecase_x (str, cstart, cend);
2271}
2272#undef FUNC_NAME
2273
2274
2275SCM_DEFINE (scm_string_titlecase, "string-titlecase", 1, 2, 0,
2276 (SCM str, SCM start, SCM end),
2277 "Titlecase every first character in a word in @var{str}.")
2278#define FUNC_NAME s_scm_string_titlecase
2279{
63181a97
MV
2280 size_t cstart, cend;
2281
f846bd1a
MG
2282 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2283 2, start, cstart,
2284 3, end, cend);
63181a97
MV
2285 return string_titlecase_x (scm_string_copy (str), cstart, cend);
2286}
2287#undef FUNC_NAME
2288
63181a97
MV
2289SCM_DEFINE (scm_string_capitalize_x, "string-capitalize!", 1, 0, 0,
2290 (SCM str),
2291 "Upcase the first character of every word in @var{str}\n"
2292 "destructively and return @var{str}.\n"
2293 "\n"
2294 "@lisp\n"
2295 "y @result{} \"hello world\"\n"
2296 "(string-capitalize! y) @result{} \"Hello World\"\n"
2297 "y @result{} \"Hello World\"\n"
2298 "@end lisp")
2299#define FUNC_NAME s_scm_string_capitalize_x
2300{
2301 return scm_string_titlecase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
2302}
2303#undef FUNC_NAME
2304
2305
2306SCM_DEFINE (scm_string_capitalize, "string-capitalize", 1, 0, 0,
2307 (SCM str),
2308 "Return a freshly allocated string with the characters in\n"
2309 "@var{str}, where the first character of every word is\n"
2310 "capitalized.")
2311#define FUNC_NAME s_scm_string_capitalize
2312{
2313 return scm_string_capitalize_x (scm_string_copy (str));
2314}
2315#undef FUNC_NAME
2316
2317
2318/* Reverse the portion of @var{str} between str[cstart] (including)
2319 and str[cend] excluding. */
2320static void
f846bd1a 2321string_reverse_x (SCM str, size_t cstart, size_t cend)
63181a97 2322{
b2fb40a9 2323 if (cstart < cend)
63181a97 2324 {
b2fb40a9
MW
2325 str = scm_i_string_start_writing (str);
2326 if (cend > 0)
2327 {
2328 SCM tmp;
2329
2330 cend--;
2331 while (cstart < cend)
2332 {
2333 tmp = SCM_MAKE_CHAR (scm_i_string_ref (str, cstart));
2334 scm_i_string_set_x (str, cstart, scm_i_string_ref (str, cend));
2335 scm_i_string_set_x (str, cend, SCM_CHAR (tmp));
2336 cstart++;
2337 cend--;
2338 }
2339 }
2340 scm_i_string_stop_writing ();
63181a97
MV
2341 }
2342}
2343
2344
2345SCM_DEFINE (scm_string_reverse, "string-reverse", 1, 2, 0,
2346 (SCM str, SCM start, SCM end),
2347 "Reverse the string @var{str}. The optional arguments\n"
2348 "@var{start} and @var{end} delimit the region of @var{str} to\n"
2349 "operate on.")
2350#define FUNC_NAME s_scm_string_reverse
2351{
63181a97
MV
2352 size_t cstart, cend;
2353 SCM result;
2354
f846bd1a
MG
2355 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2356 2, start, cstart,
2357 3, end, cend);
63181a97 2358 result = scm_string_copy (str);
f846bd1a 2359 string_reverse_x (result, cstart, cend);
3731fc67 2360 scm_remember_upto_here_1 (str);
63181a97
MV
2361 return result;
2362}
2363#undef FUNC_NAME
2364
2365
2366SCM_DEFINE (scm_string_reverse_x, "string-reverse!", 1, 2, 0,
2367 (SCM str, SCM start, SCM end),
2368 "Reverse the string @var{str} in-place. The optional arguments\n"
2369 "@var{start} and @var{end} delimit the region of @var{str} to\n"
2370 "operate on. The return value is unspecified.")
2371#define FUNC_NAME s_scm_string_reverse_x
2372{
63181a97
MV
2373 size_t cstart, cend;
2374
2375 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2376 2, start, cstart,
2377 3, end, cend);
2378
f846bd1a 2379 string_reverse_x (str, cstart, cend);
63181a97
MV
2380 scm_remember_upto_here_1 (str);
2381 return SCM_UNSPECIFIED;
2382}
2383#undef FUNC_NAME
2384
2385
2386SCM_DEFINE (scm_string_append_shared, "string-append/shared", 0, 0, 1,
a003f3eb 2387 (SCM rest),
63181a97
MV
2388 "Like @code{string-append}, but the result may share memory\n"
2389 "with the argument strings.")
2390#define FUNC_NAME s_scm_string_append_shared
2391{
a003f3eb
KR
2392 /* If "rest" contains just one non-empty string, return that.
2393 If it's entirely empty strings, then return scm_nullstr.
2394 Otherwise use scm_string_concatenate. */
63181a97 2395
a003f3eb
KR
2396 SCM ret = scm_nullstr;
2397 int seen_nonempty = 0;
2398 SCM l, s;
63181a97 2399
a003f3eb
KR
2400 SCM_VALIDATE_REST_ARGUMENT (rest);
2401
2402 for (l = rest; scm_is_pair (l); l = SCM_CDR (l))
2403 {
2404 s = SCM_CAR (l);
f846bd1a
MG
2405 if (!scm_is_string (s))
2406 scm_wrong_type_arg (FUNC_NAME, 0, s);
2407 if (scm_i_string_length (s) != 0)
a003f3eb
KR
2408 {
2409 if (seen_nonempty)
2410 /* two or more non-empty strings, need full concat */
2411 return scm_string_append (rest);
2412
2413 seen_nonempty = 1;
2414 ret = s;
2415 }
2416 }
2417 return ret;
63181a97
MV
2418}
2419#undef FUNC_NAME
2420
2421
2422SCM_DEFINE (scm_string_concatenate, "string-concatenate", 1, 0, 0,
2423 (SCM ls),
2424 "Append the elements of @var{ls} (which must be strings)\n"
2425 "together into a single string. Guaranteed to return a freshly\n"
2426 "allocated string.")
2427#define FUNC_NAME s_scm_string_concatenate
2428{
57d98034 2429 SCM_VALIDATE_LIST (SCM_ARG1, ls);
63181a97
MV
2430 return scm_string_append (ls);
2431}
2432#undef FUNC_NAME
2433
2434
2435SCM_DEFINE (scm_string_concatenate_reverse, "string-concatenate-reverse", 1, 2, 0,
2436 (SCM ls, SCM final_string, SCM end),
2437 "Without optional arguments, this procedure is equivalent to\n"
2438 "\n"
2439 "@smalllisp\n"
2440 "(string-concatenate (reverse ls))\n"
2441 "@end smalllisp\n"
2442 "\n"
2443 "If the optional argument @var{final_string} is specified, it is\n"
2444 "consed onto the beginning to @var{ls} before performing the\n"
2445 "list-reverse and string-concatenate operations. If @var{end}\n"
2446 "is given, only the characters of @var{final_string} up to index\n"
2447 "@var{end} are used.\n"
2448 "\n"
2449 "Guaranteed to return a freshly allocated string.")
2450#define FUNC_NAME s_scm_string_concatenate_reverse
2451{
3731fc67
MV
2452 if (!SCM_UNBNDP (end))
2453 final_string = scm_substring (final_string, SCM_INUM0, end);
63181a97 2454
63181a97 2455 if (!SCM_UNBNDP (final_string))
3731fc67 2456 ls = scm_cons (final_string, ls);
63181a97 2457
3731fc67 2458 return scm_string_concatenate (scm_reverse (ls));
63181a97
MV
2459}
2460#undef FUNC_NAME
2461
2462
2463SCM_DEFINE (scm_string_concatenate_shared, "string-concatenate/shared", 1, 0, 0,
2464 (SCM ls),
2465 "Like @code{string-concatenate}, but the result may share memory\n"
2466 "with the strings in the list @var{ls}.")
2467#define FUNC_NAME s_scm_string_concatenate_shared
2468{
57d98034 2469 SCM_VALIDATE_LIST (SCM_ARG1, ls);
63181a97
MV
2470 return scm_string_append_shared (ls);
2471}
2472#undef FUNC_NAME
2473
2474
2475SCM_DEFINE (scm_string_concatenate_reverse_shared, "string-concatenate-reverse/shared", 1, 2, 0,
2476 (SCM ls, SCM final_string, SCM end),
2477 "Like @code{string-concatenate-reverse}, but the result may\n"
ffb62a43 2478 "share memory with the strings in the @var{ls} arguments.")
63181a97
MV
2479#define FUNC_NAME s_scm_string_concatenate_reverse_shared
2480{
2481 /* Just call the non-sharing version. */
2482 return scm_string_concatenate_reverse (ls, final_string, end);
2483}
2484#undef FUNC_NAME
2485
2486
2487SCM_DEFINE (scm_string_map, "string-map", 2, 2, 0,
2488 (SCM proc, SCM s, SCM start, SCM end),
2489 "@var{proc} is a char->char procedure, it is mapped over\n"
2490 "@var{s}. The order in which the procedure is applied to the\n"
2491 "string elements is not specified.")
2492#define FUNC_NAME s_scm_string_map
2493{
f846bd1a 2494 size_t p;
63181a97
MV
2495 size_t cstart, cend;
2496 SCM result;
2497
6c9e8a53
AW
2498 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)),
2499 proc, SCM_ARG1, FUNC_NAME);
3731fc67
MV
2500 MY_VALIDATE_SUBSTRING_SPEC (2, s,
2501 3, start, cstart,
2502 4, end, cend);
190d4b0d 2503 result = scm_i_make_string (cend - cstart, NULL, 0);
f846bd1a 2504 p = 0;
63181a97
MV
2505 while (cstart < cend)
2506 {
6c9e8a53 2507 SCM ch = scm_call_1 (proc, scm_c_string_ref (s, cstart));
63181a97
MV
2508 if (!SCM_CHARP (ch))
2509 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (proc));
63181a97 2510 cstart++;
f846bd1a
MG
2511 result = scm_i_string_start_writing (result);
2512 scm_i_string_set_x (result, p, SCM_CHAR (ch));
2513 scm_i_string_stop_writing ();
2514 p++;
63181a97 2515 }
f846bd1a 2516
63181a97
MV
2517 return result;
2518}
2519#undef FUNC_NAME
2520
2521
2522SCM_DEFINE (scm_string_map_x, "string-map!", 2, 2, 0,
2523 (SCM proc, SCM s, SCM start, SCM end),
2524 "@var{proc} is a char->char procedure, it is mapped over\n"
2525 "@var{s}. The order in which the procedure is applied to the\n"
2526 "string elements is not specified. The string @var{s} is\n"
2527 "modified in-place, the return value is not specified.")
2528#define FUNC_NAME s_scm_string_map_x
2529{
2530 size_t cstart, cend;
2531
6c9e8a53
AW
2532 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)),
2533 proc, SCM_ARG1, FUNC_NAME);
63181a97
MV
2534 MY_VALIDATE_SUBSTRING_SPEC (2, s,
2535 3, start, cstart,
2536 4, end, cend);
2537 while (cstart < cend)
2538 {
6c9e8a53 2539 SCM ch = scm_call_1 (proc, scm_c_string_ref (s, cstart));
63181a97
MV
2540 if (!SCM_CHARP (ch))
2541 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (proc));
f846bd1a
MG
2542 s = scm_i_string_start_writing (s);
2543 scm_i_string_set_x (s, cstart, SCM_CHAR (ch));
2544 scm_i_string_stop_writing ();
63181a97
MV
2545 cstart++;
2546 }
2547 return SCM_UNSPECIFIED;
2548}
2549#undef FUNC_NAME
2550
2551
2552SCM_DEFINE (scm_string_fold, "string-fold", 3, 2, 0,
2553 (SCM kons, SCM knil, SCM s, SCM start, SCM end),
2554 "Fold @var{kons} over the characters of @var{s}, with @var{knil}\n"
2555 "as the terminating element, from left to right. @var{kons}\n"
2556 "must expect two arguments: The actual character and the last\n"
2557 "result of @var{kons}' application.")
2558#define FUNC_NAME s_scm_string_fold
2559{
63181a97
MV
2560 size_t cstart, cend;
2561 SCM result;
2562
2563 SCM_VALIDATE_PROC (1, kons);
f846bd1a
MG
2564 MY_VALIDATE_SUBSTRING_SPEC (3, s,
2565 4, start, cstart,
2566 5, end, cend);
63181a97
MV
2567 result = knil;
2568 while (cstart < cend)
2569 {
f846bd1a 2570 result = scm_call_2 (kons, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)), result);
63181a97
MV
2571 cstart++;
2572 }
3731fc67
MV
2573
2574 scm_remember_upto_here_1 (s);
63181a97
MV
2575 return result;
2576}
2577#undef FUNC_NAME
2578
2579
2580SCM_DEFINE (scm_string_fold_right, "string-fold-right", 3, 2, 0,
2581 (SCM kons, SCM knil, SCM s, SCM start, SCM end),
2582 "Fold @var{kons} over the characters of @var{s}, with @var{knil}\n"
2583 "as the terminating element, from right to left. @var{kons}\n"
2584 "must expect two arguments: The actual character and the last\n"
2585 "result of @var{kons}' application.")
2586#define FUNC_NAME s_scm_string_fold_right
2587{
63181a97
MV
2588 size_t cstart, cend;
2589 SCM result;
2590
2591 SCM_VALIDATE_PROC (1, kons);
f846bd1a
MG
2592 MY_VALIDATE_SUBSTRING_SPEC (3, s,
2593 4, start, cstart,
2594 5, end, cend);
63181a97
MV
2595 result = knil;
2596 while (cstart < cend)
2597 {
f846bd1a 2598 result = scm_call_2 (kons, SCM_MAKE_CHAR (scm_i_string_ref (s, cend-1)), result);
63181a97
MV
2599 cend--;
2600 }
3731fc67
MV
2601
2602 scm_remember_upto_here_1 (s);
63181a97
MV
2603 return result;
2604}
2605#undef FUNC_NAME
2606
2607
2608SCM_DEFINE (scm_string_unfold, "string-unfold", 4, 2, 0,
2609 (SCM p, SCM f, SCM g, SCM seed, SCM base, SCM make_final),
2610 "@itemize @bullet\n"
2611 "@item @var{g} is used to generate a series of @emph{seed}\n"
2612 "values from the initial @var{seed}: @var{seed}, (@var{g}\n"
2613 "@var{seed}), (@var{g}^2 @var{seed}), (@var{g}^3 @var{seed}),\n"
2614 "@dots{}\n"
2615 "@item @var{p} tells us when to stop -- when it returns true\n"
2616 "when applied to one of these seed values.\n"
2617 "@item @var{f} maps each seed value to the corresponding\n"
2618 "character in the result string. These chars are assembled\n"
2619 "into the string in a left-to-right order.\n"
2620 "@item @var{base} is the optional initial/leftmost portion\n"
2621 "of the constructed string; it default to the empty\n"
2622 "string.\n"
2623 "@item @var{make_final} is applied to the terminal seed\n"
2624 "value (on which @var{p} returns true) to produce\n"
2625 "the final/rightmost portion of the constructed string.\n"
2626 "It defaults to @code{(lambda (x) "")}.\n"
2627 "@end itemize")
2628#define FUNC_NAME s_scm_string_unfold
2629{
2630 SCM res, ans;
2631
2632 SCM_VALIDATE_PROC (1, p);
2633 SCM_VALIDATE_PROC (2, f);
2634 SCM_VALIDATE_PROC (3, g);
2635 if (!SCM_UNBNDP (base))
2636 {
2637 SCM_VALIDATE_STRING (5, base);
2638 ans = base;
2639 }
2640 else
190d4b0d 2641 ans = scm_i_make_string (0, NULL, 0);
63181a97
MV
2642 if (!SCM_UNBNDP (make_final))
2643 SCM_VALIDATE_PROC (6, make_final);
2644
2645 res = scm_call_1 (p, seed);
2646 while (scm_is_false (res))
2647 {
2648 SCM str;
f846bd1a 2649 size_t i = 0;
63181a97
MV
2650 SCM ch = scm_call_1 (f, seed);
2651 if (!SCM_CHARP (ch))
2652 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (f));
190d4b0d 2653 str = scm_i_make_string (1, NULL, 0);
f846bd1a
MG
2654 str = scm_i_string_start_writing (str);
2655 scm_i_string_set_x (str, i, SCM_CHAR (ch));
2656 scm_i_string_stop_writing ();
2657 i++;
63181a97
MV
2658
2659 ans = scm_string_append (scm_list_2 (ans, str));
2660 seed = scm_call_1 (g, seed);
2661 res = scm_call_1 (p, seed);
2662 }
2663 if (!SCM_UNBNDP (make_final))
2664 {
2665 res = scm_call_1 (make_final, seed);
2666 return scm_string_append (scm_list_2 (ans, res));
2667 }
2668 else
2669 return ans;
2670}
2671#undef FUNC_NAME
2672
2673
2674SCM_DEFINE (scm_string_unfold_right, "string-unfold-right", 4, 2, 0,
2675 (SCM p, SCM f, SCM g, SCM seed, SCM base, SCM make_final),
2676 "@itemize @bullet\n"
2677 "@item @var{g} is used to generate a series of @emph{seed}\n"
2678 "values from the initial @var{seed}: @var{seed}, (@var{g}\n"
2679 "@var{seed}), (@var{g}^2 @var{seed}), (@var{g}^3 @var{seed}),\n"
2680 "@dots{}\n"
2681 "@item @var{p} tells us when to stop -- when it returns true\n"
2682 "when applied to one of these seed values.\n"
2683 "@item @var{f} maps each seed value to the corresponding\n"
2684 "character in the result string. These chars are assembled\n"
2685 "into the string in a right-to-left order.\n"
2686 "@item @var{base} is the optional initial/rightmost portion\n"
2687 "of the constructed string; it default to the empty\n"
2688 "string.\n"
2689 "@item @var{make_final} is applied to the terminal seed\n"
2690 "value (on which @var{p} returns true) to produce\n"
2691 "the final/leftmost portion of the constructed string.\n"
2692 "It defaults to @code{(lambda (x) "")}.\n"
2693 "@end itemize")
2694#define FUNC_NAME s_scm_string_unfold_right
2695{
2696 SCM res, ans;
2697
2698 SCM_VALIDATE_PROC (1, p);
2699 SCM_VALIDATE_PROC (2, f);
2700 SCM_VALIDATE_PROC (3, g);
2701 if (!SCM_UNBNDP (base))
2702 {
2703 SCM_VALIDATE_STRING (5, base);
2704 ans = base;
2705 }
2706 else
190d4b0d 2707 ans = scm_i_make_string (0, NULL, 0);
63181a97
MV
2708 if (!SCM_UNBNDP (make_final))
2709 SCM_VALIDATE_PROC (6, make_final);
2710
2711 res = scm_call_1 (p, seed);
2712 while (scm_is_false (res))
2713 {
2714 SCM str;
f846bd1a 2715 size_t i = 0;
63181a97
MV
2716 SCM ch = scm_call_1 (f, seed);
2717 if (!SCM_CHARP (ch))
2718 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (f));
190d4b0d 2719 str = scm_i_make_string (1, NULL, 0);
f846bd1a
MG
2720 str = scm_i_string_start_writing (str);
2721 scm_i_string_set_x (str, i, SCM_CHAR (ch));
2722 scm_i_string_stop_writing ();
2723 i++;
63181a97
MV
2724
2725 ans = scm_string_append (scm_list_2 (str, ans));
2726 seed = scm_call_1 (g, seed);
2727 res = scm_call_1 (p, seed);
2728 }
2729 if (!SCM_UNBNDP (make_final))
2730 {
2731 res = scm_call_1 (make_final, seed);
2732 return scm_string_append (scm_list_2 (res, ans));
2733 }
2734 else
2735 return ans;
2736}
2737#undef FUNC_NAME
2738
2739
2740SCM_DEFINE (scm_string_for_each, "string-for-each", 2, 2, 0,
2741 (SCM proc, SCM s, SCM start, SCM end),
2742 "@var{proc} is mapped over @var{s} in left-to-right order. The\n"
2743 "return value is not specified.")
2744#define FUNC_NAME s_scm_string_for_each
2745{
63181a97
MV
2746 size_t cstart, cend;
2747
6c9e8a53
AW
2748 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)),
2749 proc, SCM_ARG1, FUNC_NAME);
9c44cd45
MG
2750 MY_VALIDATE_SUBSTRING_SPEC (2, s,
2751 3, start, cstart,
2752 4, end, cend);
63181a97
MV
2753 while (cstart < cend)
2754 {
6c9e8a53 2755 scm_call_1 (proc, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97
MV
2756 cstart++;
2757 }
3731fc67
MV
2758
2759 scm_remember_upto_here_1 (s);
63181a97
MV
2760 return SCM_UNSPECIFIED;
2761}
2762#undef FUNC_NAME
2763
2764SCM_DEFINE (scm_string_for_each_index, "string-for-each-index", 2, 2, 0,
2765 (SCM proc, SCM s, SCM start, SCM end),
1729055b
KR
2766 "Call @code{(@var{proc} i)} for each index i in @var{s}, from\n"
2767 "left to right.\n"
2768 "\n"
2769 "For example, to change characters to alternately upper and\n"
2770 "lower case,\n"
2771 "\n"
2772 "@example\n"
2773 "(define str (string-copy \"studly\"))\n"
2774 "(string-for-each-index\n"
2775 " (lambda (i)\n"
2776 " (string-set! str i\n"
2777 " ((if (even? i) char-upcase char-downcase)\n"
2778 " (string-ref str i))))\n"
2779 " str)\n"
2780 "str @result{} \"StUdLy\"\n"
2781 "@end example")
63181a97
MV
2782#define FUNC_NAME s_scm_string_for_each_index
2783{
63181a97
MV
2784 size_t cstart, cend;
2785
6c9e8a53
AW
2786 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)),
2787 proc, SCM_ARG1, FUNC_NAME);
3731fc67
MV
2788 MY_VALIDATE_SUBSTRING_SPEC (2, s,
2789 3, start, cstart,
2790 4, end, cend);
2791
63181a97
MV
2792 while (cstart < cend)
2793 {
6c9e8a53 2794 scm_call_1 (proc, scm_from_size_t (cstart));
63181a97
MV
2795 cstart++;
2796 }
3731fc67
MV
2797
2798 scm_remember_upto_here_1 (s);
63181a97
MV
2799 return SCM_UNSPECIFIED;
2800}
2801#undef FUNC_NAME
2802
2803SCM_DEFINE (scm_xsubstring, "xsubstring", 2, 3, 0,
2804 (SCM s, SCM from, SCM to, SCM start, SCM end),
2805 "This is the @emph{extended substring} procedure that implements\n"
2806 "replicated copying of a substring of some string.\n"
2807 "\n"
2808 "@var{s} is a string, @var{start} and @var{end} are optional\n"
2809 "arguments that demarcate a substring of @var{s}, defaulting to\n"
2810 "0 and the length of @var{s}. Replicate this substring up and\n"
2811 "down index space, in both the positive and negative directions.\n"
2812 "@code{xsubstring} returns the substring of this string\n"
2813 "beginning at index @var{from}, and ending at @var{to}, which\n"
2814 "defaults to @var{from} + (@var{end} - @var{start}).")
2815#define FUNC_NAME s_scm_xsubstring
2816{
f846bd1a 2817 size_t p;
1a82a460
MV
2818 size_t cstart, cend;
2819 int cfrom, cto;
63181a97
MV
2820 SCM result;
2821
3731fc67
MV
2822 MY_VALIDATE_SUBSTRING_SPEC (1, s,
2823 4, start, cstart,
2824 5, end, cend);
2825
1a82a460 2826 cfrom = scm_to_int (from);
63181a97
MV
2827 if (SCM_UNBNDP (to))
2828 cto = cfrom + (cend - cstart);
2829 else
1a82a460 2830 cto = scm_to_int (to);
63181a97
MV
2831 if (cstart == cend && cfrom != cto)
2832 SCM_MISC_ERROR ("start and end indices must not be equal", SCM_EOL);
2833
190d4b0d 2834 result = scm_i_make_string (cto - cfrom, NULL, 0);
f846bd1a 2835 result = scm_i_string_start_writing (result);
63181a97 2836
f846bd1a 2837 p = 0;
63181a97
MV
2838 while (cfrom < cto)
2839 {
1a82a460 2840 size_t t = ((cfrom < 0) ? -cfrom : cfrom) % (cend - cstart);
63181a97 2841 if (cfrom < 0)
f846bd1a
MG
2842 scm_i_string_set_x (result, p,
2843 scm_i_string_ref (s, (cend - cstart) - t));
63181a97 2844 else
f846bd1a 2845 scm_i_string_set_x (result, p, scm_i_string_ref (s, t));
63181a97
MV
2846 cfrom++;
2847 p++;
2848 }
f846bd1a 2849 scm_i_string_stop_writing ();
3731fc67 2850
63181a97
MV
2851 scm_remember_upto_here_1 (s);
2852 return result;
2853}
2854#undef FUNC_NAME
2855
2856
2857SCM_DEFINE (scm_string_xcopy_x, "string-xcopy!", 4, 3, 0,
2858 (SCM target, SCM tstart, SCM s, SCM sfrom, SCM sto, SCM start, SCM end),
2859 "Exactly the same as @code{xsubstring}, but the extracted text\n"
2860 "is written into the string @var{target} starting at index\n"
2861 "@var{tstart}. The operation is not defined if @code{(eq?\n"
2862 "@var{target} @var{s})} or these arguments share storage -- you\n"
2863 "cannot copy a string on top of itself.")
2864#define FUNC_NAME s_scm_string_xcopy_x
2865{
f846bd1a 2866 size_t p;
1a82a460
MV
2867 size_t ctstart, cstart, cend;
2868 int csfrom, csto;
63181a97 2869 SCM dummy = SCM_UNDEFINED;
616775ed 2870 size_t cdummy;
63181a97
MV
2871
2872 MY_VALIDATE_SUBSTRING_SPEC (1, target,
2873 2, tstart, ctstart,
2874 2, dummy, cdummy);
3731fc67
MV
2875 MY_VALIDATE_SUBSTRING_SPEC (3, s,
2876 6, start, cstart,
2877 7, end, cend);
1a82a460 2878 csfrom = scm_to_int (sfrom);
63181a97
MV
2879 if (SCM_UNBNDP (sto))
2880 csto = csfrom + (cend - cstart);
2881 else
1a82a460 2882 csto = scm_to_int (sto);
b2fb40a9 2883 if (csfrom < csto)
63181a97 2884 {
b2fb40a9
MW
2885 if (cstart == cend)
2886 SCM_MISC_ERROR ("start and end indices must not be equal", SCM_EOL);
2887 SCM_ASSERT_RANGE (1, tstart,
2888 ctstart + (csto - csfrom) <= scm_i_string_length (target));
63181a97 2889
b2fb40a9
MW
2890 p = 0;
2891 target = scm_i_string_start_writing (target);
2892 while (csfrom < csto)
2893 {
2894 size_t t = ((csfrom < 0) ? -csfrom : csfrom) % (cend - cstart);
2895 if (csfrom < 0)
2896 scm_i_string_set_x (target, p + cstart, scm_i_string_ref (s, (cend - cstart) - t));
2897 else
2898 scm_i_string_set_x (target, p + cstart, scm_i_string_ref (s, t));
2899 csfrom++;
2900 p++;
2901 }
2902 scm_i_string_stop_writing ();
2903
2904 scm_remember_upto_here_2 (target, s);
2905 }
63181a97
MV
2906 return SCM_UNSPECIFIED;
2907}
2908#undef FUNC_NAME
2909
2910
2911SCM_DEFINE (scm_string_replace, "string-replace", 2, 4, 0,
2912 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
2913 "Return the string @var{s1}, but with the characters\n"
2914 "@var{start1} @dots{} @var{end1} replaced by the characters\n"
2915 "@var{start2} @dots{} @var{end2} from @var{s2}.")
2916#define FUNC_NAME s_scm_string_replace
2917{
63181a97
MV
2918 size_t cstart1, cend1, cstart2, cend2;
2919 SCM result;
2920
3731fc67
MV
2921 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
2922 3, start1, cstart1,
2923 4, end1, cend1);
2924 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
2925 5, start2, cstart2,
2926 6, end2, cend2);
f846bd1a
MG
2927 return (scm_string_append
2928 (scm_list_3 (scm_i_substring (s1, 0, cstart1),
2929 scm_i_substring (s2, cstart2, cend2),
2930 scm_i_substring (s1, cend1, scm_i_string_length (s1)))));
63181a97
MV
2931 return result;
2932}
2933#undef FUNC_NAME
2934
2935
2936SCM_DEFINE (scm_string_tokenize, "string-tokenize", 1, 3, 0,
2937 (SCM s, SCM token_set, SCM start, SCM end),
2938 "Split the string @var{s} into a list of substrings, where each\n"
2939 "substring is a maximal non-empty contiguous sequence of\n"
2940 "characters from the character set @var{token_set}, which\n"
2941 "defaults to @code{char-set:graphic}.\n"
2942 "If @var{start} or @var{end} indices are provided, they restrict\n"
2943 "@code{string-tokenize} to operating on the indicated substring\n"
2944 "of @var{s}.")
2945#define FUNC_NAME s_scm_string_tokenize
2946{
63181a97
MV
2947 size_t cstart, cend;
2948 SCM result = SCM_EOL;
2949
f846bd1a
MG
2950 MY_VALIDATE_SUBSTRING_SPEC (1, s,
2951 3, start, cstart,
2952 4, end, cend);
63181a97
MV
2953
2954 if (SCM_UNBNDP (token_set))
2955 token_set = scm_char_set_graphic;
2956
2957 if (SCM_CHARSETP (token_set))
2958 {
1a82a460 2959 size_t idx;
63181a97
MV
2960
2961 while (cstart < cend)
2962 {
2963 while (cstart < cend)
2964 {
f846bd1a 2965 if (REF_IN_CHARSET (s, cend-1, token_set))
63181a97
MV
2966 break;
2967 cend--;
2968 }
2969 if (cstart >= cend)
2970 break;
2971 idx = cend;
2972 while (cstart < cend)
2973 {
f846bd1a 2974 if (!REF_IN_CHARSET (s, cend-1, token_set))
63181a97
MV
2975 break;
2976 cend--;
2977 }
f846bd1a 2978 result = scm_cons (scm_i_substring (s, cend, idx), result);
63181a97
MV
2979 }
2980 }
3731fc67
MV
2981 else
2982 SCM_WRONG_TYPE_ARG (2, token_set);
2983
63181a97
MV
2984 scm_remember_upto_here_1 (s);
2985 return result;
2986}
2987#undef FUNC_NAME
2988
2989SCM_DEFINE (scm_string_split, "string-split", 2, 0, 0,
5f085775 2990 (SCM str, SCM char_pred),
ffb62a43 2991 "Split the string @var{str} into a list of the substrings delimited\n"
5f085775
DH
2992 "by appearances of characters that\n"
2993 "\n"
2994 "@itemize @bullet\n"
2995 "@item\n"
2996 "equal @var{char_pred}, if it is a character,\n"
2997 "\n"
2998 "@item\n"
2999 "satisfy the predicate @var{char_pred}, if it is a procedure,\n"
3000 "\n"
3001 "@item\n"
3002 "are in the set @var{char_pred}, if it is a character set.\n"
3003 "@end itemize\n\n"
3004 "Note that an empty substring between separator characters\n"
3005 "will result in an empty string in the result list.\n"
63181a97
MV
3006 "\n"
3007 "@lisp\n"
3008 "(string-split \"root:x:0:0:root:/root:/bin/bash\" #\\:)\n"
3009 "@result{}\n"
3010 "(\"root\" \"x\" \"0\" \"0\" \"root\" \"/root\" \"/bin/bash\")\n"
3011 "\n"
3012 "(string-split \"::\" #\\:)\n"
3013 "@result{}\n"
3014 "(\"\" \"\" \"\")\n"
3015 "\n"
3016 "(string-split \"\" #\\:)\n"
3017 "@result{}\n"
3018 "(\"\")\n"
3019 "@end lisp")
3020#define FUNC_NAME s_scm_string_split
3021{
63181a97
MV
3022 SCM res = SCM_EOL;
3023
3024 SCM_VALIDATE_STRING (1, str);
f846bd1a 3025
5f085775 3026 if (SCM_CHARP (char_pred))
f846bd1a 3027 {
5f085775
DH
3028 long idx, last_idx;
3029 int narrow;
3030
3031 /* This is explicit wide/narrow logic (instead of using
3032 scm_i_string_ref) is a speed optimization. */
3033 idx = scm_i_string_length (str);
3034 narrow = scm_i_is_narrow_string (str);
3035 if (narrow)
3036 {
3037 const char *buf = scm_i_string_chars (str);
3038 while (idx >= 0)
3039 {
3040 last_idx = idx;
3041 while (idx > 0 && buf[idx-1] != (char) SCM_CHAR(char_pred))
3042 idx--;
3043 if (idx >= 0)
3044 {
3045 res = scm_cons (scm_i_substring (str, idx, last_idx), res);
3046 idx--;
3047 }
3048 }
3049 }
3050 else
f846bd1a 3051 {
5f085775
DH
3052 const scm_t_wchar *buf = scm_i_string_wide_chars (str);
3053 while (idx >= 0)
f846bd1a 3054 {
5f085775
DH
3055 last_idx = idx;
3056 while (idx > 0 && buf[idx-1] != SCM_CHAR(char_pred))
3057 idx--;
3058 if (idx >= 0)
3059 {
3060 res = scm_cons (scm_i_substring (str, idx, last_idx), res);
3061 idx--;
3062 }
f846bd1a
MG
3063 }
3064 }
3065 }
3066 else
3067 {
5f085775
DH
3068 SCM sidx, slast_idx;
3069
3070 if (!SCM_CHARSETP (char_pred))
3071 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
3072 char_pred, SCM_ARG2, FUNC_NAME);
3073
3074 /* Supporting predicates and character sets involves handling SCM
3075 values so there is less chance to optimize. */
3076 slast_idx = scm_string_length (str);
3077 for (;;)
f846bd1a 3078 {
5f085775
DH
3079 sidx = scm_string_index_right (str, char_pred, SCM_INUM0, slast_idx);
3080 if (scm_is_false (sidx))
3081 break;
3082 res = scm_cons (scm_substring (str, scm_oneplus (sidx), slast_idx), res);
3083 slast_idx = sidx;
f846bd1a 3084 }
5f085775
DH
3085
3086 res = scm_cons (scm_substring (str, SCM_INUM0, slast_idx), res);
63181a97 3087 }
5f085775 3088
63181a97
MV
3089 scm_remember_upto_here_1 (str);
3090 return res;
3091}
3092#undef FUNC_NAME
3093
3094
3095SCM_DEFINE (scm_string_filter, "string-filter", 2, 2, 0,
9fe717e2 3096 (SCM char_pred, SCM s, SCM start, SCM end),
8753a993 3097 "Filter the string @var{s}, retaining only those characters\n"
85912343 3098 "which satisfy @var{char_pred}.\n"
8753a993
KR
3099 "\n"
3100 "If @var{char_pred} is a procedure, it is applied to each\n"
3101 "character as a predicate, if it is a character, it is tested\n"
3102 "for equality and if it is a character set, it is tested for\n"
3103 "membership.")
63181a97
MV
3104#define FUNC_NAME s_scm_string_filter
3105{
63181a97
MV
3106 size_t cstart, cend;
3107 SCM result;
3108 size_t idx;
3109
215e0312 3110#if SCM_ENABLE_DEPRECATED == 1
9fe717e2
AW
3111 if (scm_is_string (char_pred))
3112 {
3113 SCM tmp;
215e0312 3114
9fe717e2
AW
3115 scm_c_issue_deprecation_warning
3116 ("Guile used to use the wrong argument order for string-filter.\n"
3117 "This call to string-filter had the arguments in the wrong order.\n"
3118 "See SRFI-13 for more details. At some point we will remove this hack.");
3119
3120 tmp = char_pred;
3121 char_pred = s;
3122 s = tmp;
3123 }
215e0312 3124#endif
9fe717e2
AW
3125
3126 MY_VALIDATE_SUBSTRING_SPEC (2, s,
f846bd1a
MG
3127 3, start, cstart,
3128 4, end, cend);
85912343
KR
3129
3130 /* The explicit loops below stripping leading and trailing non-matches
3131 mean we can return a substring if those are the only deletions, making
3132 string-filter as efficient as string-trim-both in that case. */
3133
63181a97
MV
3134 if (SCM_CHARP (char_pred))
3135 {
8753a993 3136 size_t count;
85912343
KR
3137
3138 /* strip leading non-matches by incrementing cstart */
f846bd1a 3139 while (cstart < cend && scm_i_string_ref (s, cstart) != SCM_CHAR (char_pred))
85912343
KR
3140 cstart++;
3141
3142 /* strip trailing non-matches by decrementing cend */
f846bd1a 3143 while (cend > cstart && scm_i_string_ref (s, cend-1) != SCM_CHAR (char_pred))
85912343
KR
3144 cend--;
3145
3146 /* count chars to keep */
8753a993
KR
3147 count = 0;
3148 for (idx = cstart; idx < cend; idx++)
f846bd1a 3149 if (scm_i_string_ref (s, idx) == SCM_CHAR (char_pred))
8753a993
KR
3150 count++;
3151
8753a993 3152 if (count == cend - cstart)
8230ee41 3153 {
85912343
KR
3154 /* whole of cstart to cend is to be kept, return a copy-on-write
3155 substring */
8230ee41
KR
3156 result_substring:
3157 result = scm_i_substring (s, cstart, cend);
3158 }
8753a993
KR
3159 else
3160 result = scm_c_make_string (count, char_pred);
63181a97
MV
3161 }
3162 else if (SCM_CHARSETP (char_pred))
3163 {
8753a993 3164 size_t count;
63181a97 3165
85912343 3166 /* strip leading non-matches by incrementing cstart */
f846bd1a 3167 while (cstart < cend && ! REF_IN_CHARSET (s, cstart, char_pred))
85912343
KR
3168 cstart++;
3169
3170 /* strip trailing non-matches by decrementing cend */
f846bd1a 3171 while (cend > cstart && ! REF_IN_CHARSET (s, cend-1, char_pred))
85912343
KR
3172 cend--;
3173
8753a993
KR
3174 /* count chars to be kept */
3175 count = 0;
3176 for (idx = cstart; idx < cend; idx++)
f846bd1a 3177 if (REF_IN_CHARSET (s, idx, char_pred))
8753a993
KR
3178 count++;
3179
8230ee41 3180 /* if whole of start to end kept then return substring */
8753a993 3181 if (count == cend - cstart)
8230ee41 3182 goto result_substring;
8753a993
KR
3183 else
3184 {
f846bd1a 3185 size_t dst = 0;
190d4b0d 3186 result = scm_i_make_string (count, NULL, 0);
f846bd1a 3187 result = scm_i_string_start_writing (result);
8753a993
KR
3188
3189 /* decrement "count" in this loop as well as using idx, so that if
3190 another thread is simultaneously changing "s" there's no chance
3191 it'll make us copy more than count characters */
3192 for (idx = cstart; idx < cend && count != 0; idx++)
3193 {
f846bd1a 3194 if (REF_IN_CHARSET (s, idx, char_pred))
8753a993 3195 {
f846bd1a
MG
3196 scm_i_string_set_x (result, dst, scm_i_string_ref (s, idx));
3197 dst ++;
8753a993
KR
3198 count--;
3199 }
3200 }
f846bd1a 3201 scm_i_string_stop_writing ();
8753a993 3202 }
63181a97
MV
3203 }
3204 else
3205 {
3206 SCM ls = SCM_EOL;
3207
6c9e8a53 3208 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
9fe717e2 3209 char_pred, SCM_ARG1, FUNC_NAME);
63181a97
MV
3210 idx = cstart;
3211 while (idx < cend)
3212 {
3731fc67 3213 SCM res, ch;
f846bd1a 3214 ch = SCM_MAKE_CHAR (scm_i_string_ref (s, idx));
6c9e8a53 3215 res = scm_call_1 (char_pred, ch);
63181a97 3216 if (scm_is_true (res))
3731fc67 3217 ls = scm_cons (ch, ls);
63181a97
MV
3218 idx++;
3219 }
3220 result = scm_reverse_list_to_string (ls);
3221 }
3731fc67 3222
63181a97
MV
3223 scm_remember_upto_here_1 (s);
3224 return result;
3225}
3226#undef FUNC_NAME
3227
3228
3229SCM_DEFINE (scm_string_delete, "string-delete", 2, 2, 0,
9fe717e2 3230 (SCM char_pred, SCM s, SCM start, SCM end),
85912343 3231 "Delete characters satisfying @var{char_pred} from @var{s}.\n"
8753a993
KR
3232 "\n"
3233 "If @var{char_pred} is a procedure, it is applied to each\n"
3234 "character as a predicate, if it is a character, it is tested\n"
3235 "for equality and if it is a character set, it is tested for\n"
3236 "membership.")
63181a97
MV
3237#define FUNC_NAME s_scm_string_delete
3238{
63181a97
MV
3239 size_t cstart, cend;
3240 SCM result;
3241 size_t idx;
3242
215e0312 3243#if SCM_ENABLE_DEPRECATED == 1
9fe717e2
AW
3244 if (scm_is_string (char_pred))
3245 {
3246 SCM tmp;
215e0312 3247
9fe717e2
AW
3248 scm_c_issue_deprecation_warning
3249 ("Guile used to use the wrong argument order for string-delete.\n"
3250 "This call to string-filter had the arguments in the wrong order.\n"
3251 "See SRFI-13 for more details. At some point we will remove this hack.");
3252
3253 tmp = char_pred;
3254 char_pred = s;
3255 s = tmp;
3256 }
215e0312 3257#endif
9fe717e2
AW
3258
3259 MY_VALIDATE_SUBSTRING_SPEC (2, s,
f846bd1a
MG
3260 3, start, cstart,
3261 4, end, cend);
85912343
KR
3262
3263 /* The explicit loops below stripping leading and trailing matches mean we
3264 can return a substring if those are the only deletions, making
3265 string-delete as efficient as string-trim-both in that case. */
3266
63181a97
MV
3267 if (SCM_CHARP (char_pred))
3268 {
8753a993 3269 size_t count;
8753a993 3270
85912343 3271 /* strip leading matches by incrementing cstart */
f846bd1a 3272 while (cstart < cend && scm_i_string_ref (s, cstart) == SCM_CHAR(char_pred))
85912343
KR
3273 cstart++;
3274
3275 /* strip trailing matches by decrementing cend */
f846bd1a 3276 while (cend > cstart && scm_i_string_ref (s, cend-1) == SCM_CHAR (char_pred))
85912343
KR
3277 cend--;
3278
8753a993
KR
3279 /* count chars to be kept */
3280 count = 0;
3281 for (idx = cstart; idx < cend; idx++)
f846bd1a 3282 if (scm_i_string_ref (s, idx) != SCM_CHAR (char_pred))
8753a993
KR
3283 count++;
3284
8753a993
KR
3285 if (count == cend - cstart)
3286 {
85912343
KR
3287 /* whole of cstart to cend is to be kept, return a copy-on-write
3288 substring */
8753a993 3289 result_substring:
8230ee41 3290 result = scm_i_substring (s, cstart, cend);
8753a993
KR
3291 }
3292 else
3293 {
f846bd1a 3294 int i = 0;
8753a993 3295 /* new string for retained portion */
190d4b0d 3296 result = scm_i_make_string (count, NULL, 0);
f846bd1a 3297 result = scm_i_string_start_writing (result);
8753a993
KR
3298 /* decrement "count" in this loop as well as using idx, so that if
3299 another thread is simultaneously changing "s" there's no chance
3300 it'll make us copy more than count characters */
3301 for (idx = cstart; idx < cend && count != 0; idx++)
3302 {
f846bd1a
MG
3303 scm_t_wchar c = scm_i_string_ref (s, idx);
3304 if (c != SCM_CHAR (char_pred))
8753a993 3305 {
f846bd1a
MG
3306 scm_i_string_set_x (result, i, c);
3307 i++;
8753a993
KR
3308 count--;
3309 }
3310 }
f846bd1a 3311 scm_i_string_stop_writing ();
8753a993 3312 }
63181a97
MV
3313 }
3314 else if (SCM_CHARSETP (char_pred))
3315 {
8753a993 3316 size_t count;
63181a97 3317
85912343 3318 /* strip leading matches by incrementing cstart */
f846bd1a 3319 while (cstart < cend && REF_IN_CHARSET (s, cstart, char_pred))
85912343
KR
3320 cstart++;
3321
3322 /* strip trailing matches by decrementing cend */
f846bd1a 3323 while (cend > cstart && REF_IN_CHARSET (s, cend-1, char_pred))
85912343
KR
3324 cend--;
3325
8753a993
KR
3326 /* count chars to be kept */
3327 count = 0;
3328 for (idx = cstart; idx < cend; idx++)
f846bd1a 3329 if (!REF_IN_CHARSET (s, idx, char_pred))
8753a993
KR
3330 count++;
3331
8753a993
KR
3332 if (count == cend - cstart)
3333 goto result_substring;
3334 else
3335 {
f846bd1a 3336 size_t i = 0;
8753a993 3337 /* new string for retained portion */
190d4b0d 3338 result = scm_i_make_string (count, NULL, 0);
f846bd1a 3339 result = scm_i_string_start_writing (result);
8753a993
KR
3340
3341 /* decrement "count" in this loop as well as using idx, so that if
3342 another thread is simultaneously changing "s" there's no chance
3343 it'll make us copy more than count characters */
3344 for (idx = cstart; idx < cend && count != 0; idx++)
3345 {
f846bd1a 3346 if (!REF_IN_CHARSET (s, idx, char_pred))
8753a993 3347 {
f846bd1a
MG
3348 scm_i_string_set_x (result, i, scm_i_string_ref (s, idx));
3349 i++;
8753a993
KR
3350 count--;
3351 }
3352 }
f846bd1a 3353 scm_i_string_stop_writing ();
8753a993 3354 }
63181a97
MV
3355 }
3356 else
3357 {
3358 SCM ls = SCM_EOL;
6c9e8a53 3359 SCM_ASSERT (scm_is_true (scm_procedure_p (char_pred)),
9fe717e2 3360 char_pred, SCM_ARG1, FUNC_NAME);
63181a97 3361
63181a97
MV
3362 idx = cstart;
3363 while (idx < cend)
3364 {
f846bd1a 3365 SCM res, ch = SCM_MAKE_CHAR (scm_i_string_ref (s, idx));
6c9e8a53 3366 res = scm_call_1 (char_pred, ch);
63181a97 3367 if (scm_is_false (res))
3731fc67 3368 ls = scm_cons (ch, ls);
63181a97
MV
3369 idx++;
3370 }
3371 result = scm_reverse_list_to_string (ls);
3372 }
3731fc67
MV
3373
3374 scm_remember_upto_here_1 (s);
63181a97
MV
3375 return result;
3376}
3377#undef FUNC_NAME
3378
63181a97
MV
3379void
3380scm_init_srfi_13 (void)
3381{
3382#include "libguile/srfi-13.x"
3383}
3384
3385/* End of srfi-13.c. */