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