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