gut the backtrace implementation for memoized code
[bpt/guile.git] / libguile / srfi-13.c
CommitLineData
63181a97
MV
1/* srfi-13.c --- SRFI-13 procedures for Guile
2 *
f846bd1a 3 * Copyright (C) 2001, 2004, 2005, 2006, 2008, 2009 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
32#include "libguile/srfi-13.h"
33#include "libguile/srfi-14.h"
34
63181a97
MV
35#define MY_VALIDATE_SUBSTRING_SPEC(pos_str, str, \
36 pos_start, start, c_start, \
37 pos_end, end, c_end) \
38 do { \
39 SCM_VALIDATE_STRING (pos_str, str); \
40 scm_i_get_substring_spec (scm_i_string_length (str), \
41 start, &c_start, end, &c_end); \
42 } while (0)
43
f846bd1a
MG
44#define MY_SUBF_VALIDATE_SUBSTRING_SPEC(fname, pos_str, str, \
45 pos_start, start, c_start, \
46 pos_end, end, c_end) \
47 do { \
48 SCM_ASSERT_TYPE (scm_is_string (str), str, pos_str, fname, "string"); \
49 scm_i_get_substring_spec (scm_i_string_length (str), \
50 start, &c_start, end, &c_end); \
51 } while (0)
52
53#define REF_IN_CHARSET(s, i, cs) \
54 (scm_is_true (scm_char_set_contains_p ((cs), SCM_MAKE_CHAR (scm_i_string_ref (s, i)))))
55
63181a97
MV
56SCM_DEFINE (scm_string_null_p, "string-null?", 1, 0, 0,
57 (SCM str),
58 "Return @code{#t} if @var{str}'s length is zero, and\n"
59 "@code{#f} otherwise.\n"
60 "@lisp\n"
61 "(string-null? \"\") @result{} #t\n"
62 "y @result{} \"foo\"\n"
63 "(string-null? y) @result{} #f\n"
64 "@end lisp")
65#define FUNC_NAME s_scm_string_null_p
66{
67 SCM_VALIDATE_STRING (1, str);
68 return scm_from_bool (scm_i_string_length (str) == 0);
69}
70#undef FUNC_NAME
71
3731fc67
MV
72#if 0
73static void
74race_error ()
75{
76 scm_misc_error (NULL, "race condition detected", SCM_EOL);
77}
78#endif
79
fdc6aebf 80SCM_DEFINE (scm_string_any, "string-any-c-code", 2, 2, 0,
63181a97 81 (SCM char_pred, SCM s, SCM start, SCM end),
69d2000d
MV
82"Check if @var{char_pred} is true for any character in string @var{s}.\n"
83"\n"
84"@var{char_pred} can be a character to check for any equal to that, or\n"
85"a character set (@pxref{Character Sets}) to check for any in that set,\n"
86"or a predicate procedure to call.\n"
87"\n"
88"For a procedure, calls @code{(@var{char_pred} c)} are made\n"
89"successively on the characters from @var{start} to @var{end}. If\n"
90"@var{char_pred} returns true (ie.@: non-@code{#f}), @code{string-any}\n"
91"stops and that return value is the return from @code{string-any}. The\n"
92"call on the last character (ie.@: at @math{@var{end}-1}), if that\n"
93"point is reached, is a tail call.\n"
94"\n"
95"If there are no characters in @var{s} (ie.@: @var{start} equals\n"
96"@var{end}) then the return is @code{#f}.\n")
63181a97
MV
97#define FUNC_NAME s_scm_string_any
98{
3731fc67
MV
99 size_t cstart, cend;
100 SCM res = SCM_BOOL_F;
63181a97 101
f846bd1a
MG
102 MY_VALIDATE_SUBSTRING_SPEC (2, s,
103 3, start, cstart,
104 4, end, cend);
63181a97
MV
105
106 if (SCM_CHARP (char_pred))
107 {
f846bd1a
MG
108 size_t i;
109 for (i = cstart; i < cend; i ++)
110 if (scm_i_string_ref (s, i) == SCM_CHAR (char_pred))
111 {
112 res = SCM_BOOL_T;
113 break;
114 }
63181a97
MV
115 }
116 else if (SCM_CHARSETP (char_pred))
117 {
1a82a460 118 size_t i;
63181a97 119 for (i = cstart; i < cend; i++)
f846bd1a 120 if (REF_IN_CHARSET (s, i, char_pred))
3731fc67
MV
121 {
122 res = SCM_BOOL_T;
123 break;
124 }
63181a97
MV
125 }
126 else
127 {
3540b915
KR
128 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
129 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG1, FUNC_NAME);
63181a97 130
63181a97
MV
131 while (cstart < cend)
132 {
f846bd1a
MG
133 res = pred_tramp (char_pred,
134 SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97 135 if (scm_is_true (res))
3731fc67 136 break;
63181a97
MV
137 cstart++;
138 }
139 }
3731fc67
MV
140
141 scm_remember_upto_here_1 (s);
142 return res;
63181a97
MV
143}
144#undef FUNC_NAME
145
146
fdc6aebf 147SCM_DEFINE (scm_string_every, "string-every-c-code", 2, 2, 0,
63181a97 148 (SCM char_pred, SCM s, SCM start, SCM end),
69d2000d
MV
149"Check if @var{char_pred} is true for every character in string\n"
150"@var{s}.\n"
151"\n"
152"@var{char_pred} can be a character to check for every character equal\n"
153"to that, or a character set (@pxref{Character Sets}) to check for\n"
154"every character being in that set, or a predicate procedure to call.\n"
155"\n"
156"For a procedure, calls @code{(@var{char_pred} c)} are made\n"
157"successively on the characters from @var{start} to @var{end}. If\n"
158"@var{char_pred} returns @code{#f}, @code{string-every} stops and\n"
159"returns @code{#f}. The call on the last character (ie.@: at\n"
160"@math{@var{end}-1}), if that point is reached, is a tail call and the\n"
161"return from that call is the return from @code{string-every}.\n"
162"\n"
163"If there are no characters in @var{s} (ie.@: @var{start} equals\n"
164"@var{end}) then the return is @code{#t}.\n")
63181a97
MV
165#define FUNC_NAME s_scm_string_every
166{
3731fc67
MV
167 size_t cstart, cend;
168 SCM res = SCM_BOOL_T;
63181a97 169
f846bd1a
MG
170 MY_VALIDATE_SUBSTRING_SPEC (2, s,
171 3, start, cstart,
172 4, end, cend);
63181a97
MV
173 if (SCM_CHARP (char_pred))
174 {
1a82a460 175 size_t i;
63181a97 176 for (i = cstart; i < cend; i++)
f846bd1a 177 if (scm_i_string_ref (s, i) != SCM_CHAR (char_pred))
3731fc67
MV
178 {
179 res = SCM_BOOL_F;
180 break;
181 }
63181a97
MV
182 }
183 else if (SCM_CHARSETP (char_pred))
184 {
1a82a460 185 size_t i;
63181a97 186 for (i = cstart; i < cend; i++)
f846bd1a 187 if (!REF_IN_CHARSET (s, i, char_pred))
3731fc67
MV
188 {
189 res = SCM_BOOL_F;
190 break;
191 }
63181a97
MV
192 }
193 else
194 {
3540b915
KR
195 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
196 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG1, FUNC_NAME);
63181a97 197
63181a97
MV
198 while (cstart < cend)
199 {
f846bd1a
MG
200 res = pred_tramp (char_pred,
201 SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97 202 if (scm_is_false (res))
3731fc67 203 break;
63181a97
MV
204 cstart++;
205 }
63181a97 206 }
3731fc67
MV
207
208 scm_remember_upto_here_1 (s);
209 return res;
63181a97
MV
210}
211#undef FUNC_NAME
212
213
214SCM_DEFINE (scm_string_tabulate, "string-tabulate", 2, 0, 0,
215 (SCM proc, SCM len),
216 "@var{proc} is an integer->char procedure. Construct a string\n"
217 "of size @var{len} by applying @var{proc} to each index to\n"
218 "produce the corresponding string element. The order in which\n"
219 "@var{proc} is applied to the indices is not specified.")
220#define FUNC_NAME s_scm_string_tabulate
221{
222 size_t clen, i;
223 SCM res;
224 SCM ch;
3540b915
KR
225 scm_t_trampoline_1 proc_tramp;
226
227 proc_tramp = scm_trampoline_1 (proc);
228 SCM_ASSERT (proc_tramp, 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 {
241 ch = proc_tramp (proc, scm_from_size_t (i));
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 {
3540b915
KR
748 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
749 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
750
63181a97
MV
751 while (cstart < cend)
752 {
753 SCM res;
754
f846bd1a 755 res = pred_tramp (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 {
3540b915
KR
823 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
824 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
825
63181a97
MV
826 while (cstart < cend)
827 {
828 SCM res;
829
f846bd1a 830 res = pred_tramp (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 {
3540b915
KR
916 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
917 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
918
63181a97
MV
919 while (cstart < cend)
920 {
921 SCM res;
922
f846bd1a 923 res = pred_tramp (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
f846bd1a 932 res = pred_tramp (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{
1172 return compare_strings (FUNC_NAME, 0,
1173 s1, s2, start1, end1, start2, end2,
1174 SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_T);
63181a97
MV
1175}
1176#undef FUNC_NAME
1177
1178
1179SCM_DEFINE (scm_string_neq, "string<>", 2, 4, 0,
1180 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1181 "Return @code{#f} if @var{s1} and @var{s2} are equal, a true\n"
1182 "value otherwise.")
1183#define FUNC_NAME s_scm_string_neq
1184{
f846bd1a
MG
1185 return compare_strings (FUNC_NAME, 0,
1186 s1, s2, start1, end1, start2, end2,
1187 SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_F);
63181a97
MV
1188}
1189#undef FUNC_NAME
1190
1191
1192SCM_DEFINE (scm_string_lt, "string<", 2, 4, 0,
1193 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1194 "Return @code{#f} if @var{s1} is greater or equal to @var{s2}, a\n"
1195 "true value otherwise.")
1196#define FUNC_NAME s_scm_string_lt
1197{
f846bd1a
MG
1198 return compare_strings (FUNC_NAME, 0,
1199 s1, s2, start1, end1, start2, end2,
1200 SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_F);
63181a97
MV
1201}
1202#undef FUNC_NAME
1203
1204
1205SCM_DEFINE (scm_string_gt, "string>", 2, 4, 0,
1206 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1207 "Return @code{#f} if @var{s1} is less or equal to @var{s2}, a\n"
1208 "true value otherwise.")
1209#define FUNC_NAME s_scm_string_gt
1210{
f846bd1a
MG
1211 return compare_strings (FUNC_NAME, 0,
1212 s1, s2, start1, end1, start2, end2,
1213 SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F);
63181a97
MV
1214}
1215#undef FUNC_NAME
1216
1217
1218SCM_DEFINE (scm_string_le, "string<=", 2, 4, 0,
1219 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1220 "Return @code{#f} if @var{s1} is greater to @var{s2}, a true\n"
1221 "value otherwise.")
1222#define FUNC_NAME s_scm_string_le
1223{
f846bd1a
MG
1224 return compare_strings (FUNC_NAME, 0,
1225 s1, s2, start1, end1, start2, end2,
1226 SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T);
63181a97
MV
1227}
1228#undef FUNC_NAME
1229
1230
1231SCM_DEFINE (scm_string_ge, "string>=", 2, 4, 0,
1232 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1233 "Return @code{#f} if @var{s1} is less to @var{s2}, a true value\n"
1234 "otherwise.")
1235#define FUNC_NAME s_scm_string_ge
1236{
f846bd1a
MG
1237 return compare_strings (FUNC_NAME, 0,
1238 s1, s2, start1, end1, start2, end2,
1239 SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_T);
63181a97
MV
1240}
1241#undef FUNC_NAME
1242
1243
1244SCM_DEFINE (scm_string_ci_eq, "string-ci=", 2, 4, 0,
1245 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1246 "Return @code{#f} if @var{s1} and @var{s2} are not equal, a true\n"
1247 "value otherwise. The character comparison is done\n"
1248 "case-insensitively.")
1249#define FUNC_NAME s_scm_string_ci_eq
1250{
f846bd1a
MG
1251 return compare_strings (FUNC_NAME, 1,
1252 s1, s2, start1, end1, start2, end2,
1253 SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_F, SCM_BOOL_T);
63181a97
MV
1254}
1255#undef FUNC_NAME
1256
1257
1258SCM_DEFINE (scm_string_ci_neq, "string-ci<>", 2, 4, 0,
1259 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
f846bd1a
MG
1260 "Return @code{#f} if @var{s1} and @var{s2} are equal, a true\n"
1261 "value otherwise. The character comparison is done\n"
1262 "case-insensitively.")
1263#define FUNC_NAME s_scm_string_ci_neq
1264{
1265 return compare_strings (FUNC_NAME, 1,
1266 s1, s2, start1, end1, start2, end2,
1267 SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_T, SCM_BOOL_F);
63181a97
MV
1268}
1269#undef FUNC_NAME
1270
1271
1272SCM_DEFINE (scm_string_ci_lt, "string-ci<", 2, 4, 0,
1273 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1274 "Return @code{#f} if @var{s1} is greater or equal to @var{s2}, a\n"
1275 "true value otherwise. The character comparison is done\n"
1276 "case-insensitively.")
1277#define FUNC_NAME s_scm_string_ci_lt
1278{
f846bd1a
MG
1279 return compare_strings (FUNC_NAME, 1,
1280 s1, s2, start1, end1, start2, end2,
1281 SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_F);
63181a97
MV
1282}
1283#undef FUNC_NAME
1284
1285
1286SCM_DEFINE (scm_string_ci_gt, "string-ci>", 2, 4, 0,
1287 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1288 "Return @code{#f} if @var{s1} is less or equal to @var{s2}, a\n"
1289 "true value otherwise. The character comparison is done\n"
1290 "case-insensitively.")
1291#define FUNC_NAME s_scm_string_ci_gt
1292{
f846bd1a
MG
1293 return compare_strings (FUNC_NAME, 1,
1294 s1, s2, start1, end1, start2, end2,
1295 SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F);
63181a97
MV
1296}
1297#undef FUNC_NAME
1298
1299
1300SCM_DEFINE (scm_string_ci_le, "string-ci<=", 2, 4, 0,
1301 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1302 "Return @code{#f} if @var{s1} is greater to @var{s2}, a true\n"
1303 "value otherwise. The character comparison is done\n"
1304 "case-insensitively.")
1305#define FUNC_NAME s_scm_string_ci_le
1306{
f846bd1a
MG
1307 return compare_strings (FUNC_NAME, 1,
1308 s1, s2, start1, end1, start2, end2,
1309 SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T);
63181a97
MV
1310}
1311#undef FUNC_NAME
1312
1313
1314SCM_DEFINE (scm_string_ci_ge, "string-ci>=", 2, 4, 0,
1315 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1316 "Return @code{#f} if @var{s1} is less to @var{s2}, a true value\n"
1317 "otherwise. The character comparison is done\n"
1318 "case-insensitively.")
1319#define FUNC_NAME s_scm_string_ci_ge
1320{
f846bd1a
MG
1321 return compare_strings (FUNC_NAME, 1,
1322 s1, s2, start1, end1, start2, end2,
1323 SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_F, SCM_BOOL_T, SCM_BOOL_T);
63181a97
MV
1324}
1325#undef FUNC_NAME
1326
1327SCM_DEFINE (scm_substring_hash, "string-hash", 1, 3, 0,
1328 (SCM s, SCM bound, SCM start, SCM end),
1329 "Compute a hash value for @var{S}. the optional argument "
1330 "@var{bound} is a non-negative exact "
1331 "integer specifying the range of the hash function. "
1332 "A positive value restricts the return value to the "
1333 "range [0,bound).")
1334#define FUNC_NAME s_scm_substring_hash
1335{
1336 if (SCM_UNBNDP (bound))
1337 bound = scm_from_intmax (SCM_MOST_POSITIVE_FIXNUM);
1338 if (SCM_UNBNDP (start))
1339 start = SCM_INUM0;
1340 return scm_hash (scm_substring_shared (s, start, end), bound);
1341}
1342#undef FUNC_NAME
1343
1344SCM_DEFINE (scm_substring_hash_ci, "string-hash-ci", 1, 3, 0,
1345 (SCM s, SCM bound, SCM start, SCM end),
1346 "Compute a hash value for @var{S}. the optional argument "
1347 "@var{bound} is a non-negative exact "
1348 "integer specifying the range of the hash function. "
1349 "A positive value restricts the return value to the "
1350 "range [0,bound).")
1351#define FUNC_NAME s_scm_substring_hash_ci
1352{
1353 return scm_substring_hash (scm_substring_downcase (s, start, end),
1354 bound,
1355 SCM_UNDEFINED, SCM_UNDEFINED);
1356}
1357#undef FUNC_NAME
1358
1359SCM_DEFINE (scm_string_prefix_length, "string-prefix-length", 2, 4, 0,
1360 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1361 "Return the length of the longest common prefix of the two\n"
1362 "strings.")
1363#define FUNC_NAME s_scm_string_prefix_length
1364{
63181a97
MV
1365 size_t cstart1, cend1, cstart2, cend2;
1366 size_t len = 0;
1367
f846bd1a
MG
1368 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1369 3, start1, cstart1,
1370 4, end1, cend1);
1371 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1372 5, start2, cstart2,
1373 6, end2, cend2);
1374
63181a97
MV
1375 while (cstart1 < cend1 && cstart2 < cend2)
1376 {
f846bd1a
MG
1377 if (scm_i_string_ref (s1, cstart1)
1378 != scm_i_string_ref (s2, cstart2))
3731fc67 1379 goto ret;
63181a97
MV
1380 len++;
1381 cstart1++;
1382 cstart2++;
1383 }
3731fc67
MV
1384
1385 ret:
1386 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1387 return scm_from_size_t (len);
1388}
1389#undef FUNC_NAME
1390
1391
1392SCM_DEFINE (scm_string_prefix_length_ci, "string-prefix-length-ci", 2, 4, 0,
1393 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1394 "Return the length of the longest common prefix of the two\n"
1395 "strings, ignoring character case.")
1396#define FUNC_NAME s_scm_string_prefix_length_ci
1397{
63181a97
MV
1398 size_t cstart1, cend1, cstart2, cend2;
1399 size_t len = 0;
1400
f846bd1a
MG
1401 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1402 3, start1, cstart1,
1403 4, end1, cend1);
1404 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1405 5, start2, cstart2,
1406 6, end2, cend2);
63181a97
MV
1407 while (cstart1 < cend1 && cstart2 < cend2)
1408 {
f846bd1a
MG
1409 if (uc_tolower (uc_toupper (scm_i_string_ref (s1, cstart1)))
1410 != uc_tolower (uc_toupper (scm_i_string_ref (s2, cstart2))))
3731fc67 1411 goto ret;
63181a97
MV
1412 len++;
1413 cstart1++;
1414 cstart2++;
1415 }
3731fc67
MV
1416
1417 ret:
1418 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1419 return scm_from_size_t (len);
1420}
1421#undef FUNC_NAME
1422
1423
1424SCM_DEFINE (scm_string_suffix_length, "string-suffix-length", 2, 4, 0,
1425 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1426 "Return the length of the longest common suffix of the two\n"
1427 "strings.")
1428#define FUNC_NAME s_scm_string_suffix_length
1429{
63181a97
MV
1430 size_t cstart1, cend1, cstart2, cend2;
1431 size_t len = 0;
1432
f846bd1a
MG
1433 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1434 3, start1, cstart1,
1435 4, end1, cend1);
1436 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1437 5, start2, cstart2,
1438 6, end2, cend2);
63181a97
MV
1439 while (cstart1 < cend1 && cstart2 < cend2)
1440 {
1441 cend1--;
1442 cend2--;
f846bd1a
MG
1443 if (scm_i_string_ref (s1, cend1)
1444 != scm_i_string_ref (s2, cend2))
3731fc67 1445 goto ret;
63181a97
MV
1446 len++;
1447 }
3731fc67
MV
1448
1449 ret:
1450 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1451 return scm_from_size_t (len);
1452}
1453#undef FUNC_NAME
1454
1455
1456SCM_DEFINE (scm_string_suffix_length_ci, "string-suffix-length-ci", 2, 4, 0,
1457 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1458 "Return the length of the longest common suffix of the two\n"
1459 "strings, ignoring character case.")
1460#define FUNC_NAME s_scm_string_suffix_length_ci
1461{
63181a97
MV
1462 size_t cstart1, cend1, cstart2, cend2;
1463 size_t len = 0;
1464
f846bd1a
MG
1465 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1466 3, start1, cstart1,
1467 4, end1, cend1);
1468 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1469 5, start2, cstart2,
1470 6, end2, cend2);
63181a97
MV
1471 while (cstart1 < cend1 && cstart2 < cend2)
1472 {
1473 cend1--;
1474 cend2--;
f846bd1a
MG
1475 if (uc_tolower (uc_toupper (scm_i_string_ref (s1, cend1)))
1476 != uc_tolower (uc_toupper (scm_i_string_ref (s2, cend2))))
3731fc67 1477 goto ret;
63181a97
MV
1478 len++;
1479 }
3731fc67
MV
1480
1481 ret:
1482 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1483 return scm_from_size_t (len);
1484}
1485#undef FUNC_NAME
1486
1487
1488SCM_DEFINE (scm_string_prefix_p, "string-prefix?", 2, 4, 0,
1489 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1490 "Is @var{s1} a prefix of @var{s2}?")
1491#define FUNC_NAME s_scm_string_prefix_p
1492{
63181a97
MV
1493 size_t cstart1, cend1, cstart2, cend2;
1494 size_t len = 0, len1;
1495
f846bd1a
MG
1496 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1497 3, start1, cstart1,
1498 4, end1, cend1);
1499 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1500 5, start2, cstart2,
1501 6, end2, cend2);
63181a97
MV
1502 len1 = cend1 - cstart1;
1503 while (cstart1 < cend1 && cstart2 < cend2)
1504 {
f846bd1a
MG
1505 if (scm_i_string_ref (s1, cstart1)
1506 != scm_i_string_ref (s2, cstart2))
3731fc67 1507 goto ret;
63181a97
MV
1508 len++;
1509 cstart1++;
1510 cstart2++;
1511 }
3731fc67
MV
1512
1513 ret:
1514 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1515 return scm_from_bool (len == len1);
1516}
1517#undef FUNC_NAME
1518
1519
1520SCM_DEFINE (scm_string_prefix_ci_p, "string-prefix-ci?", 2, 4, 0,
1521 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1522 "Is @var{s1} a prefix of @var{s2}, ignoring character case?")
1523#define FUNC_NAME s_scm_string_prefix_ci_p
1524{
63181a97
MV
1525 size_t cstart1, cend1, cstart2, cend2;
1526 size_t len = 0, len1;
1527
f846bd1a
MG
1528 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1529 3, start1, cstart1,
1530 4, end1, cend1);
1531 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1532 5, start2, cstart2,
1533 6, end2, cend2);
63181a97
MV
1534 len1 = cend1 - cstart1;
1535 while (cstart1 < cend1 && cstart2 < cend2)
1536 {
f846bd1a
MG
1537 scm_t_wchar a = uc_tolower (uc_toupper (scm_i_string_ref (s1, cstart1)));
1538 scm_t_wchar b = uc_tolower (uc_toupper (scm_i_string_ref (s2, cstart2)));
1539 if (a != b)
3731fc67 1540 goto ret;
63181a97
MV
1541 len++;
1542 cstart1++;
1543 cstart2++;
1544 }
3731fc67
MV
1545
1546 ret:
1547 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1548 return scm_from_bool (len == len1);
1549}
1550#undef FUNC_NAME
1551
1552
1553SCM_DEFINE (scm_string_suffix_p, "string-suffix?", 2, 4, 0,
1554 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1555 "Is @var{s1} a suffix of @var{s2}?")
1556#define FUNC_NAME s_scm_string_suffix_p
1557{
63181a97
MV
1558 size_t cstart1, cend1, cstart2, cend2;
1559 size_t len = 0, len1;
1560
f846bd1a
MG
1561 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1562 3, start1, cstart1,
1563 4, end1, cend1);
1564 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1565 5, start2, cstart2,
1566 6, end2, cend2);
63181a97
MV
1567 len1 = cend1 - cstart1;
1568 while (cstart1 < cend1 && cstart2 < cend2)
1569 {
1570 cend1--;
1571 cend2--;
f846bd1a
MG
1572 if (scm_i_string_ref (s1, cend1)
1573 != scm_i_string_ref (s2, cend2))
3731fc67 1574 goto ret;
63181a97
MV
1575 len++;
1576 }
3731fc67
MV
1577
1578 ret:
1579 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1580 return scm_from_bool (len == len1);
1581}
1582#undef FUNC_NAME
1583
1584
1585SCM_DEFINE (scm_string_suffix_ci_p, "string-suffix-ci?", 2, 4, 0,
1586 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1587 "Is @var{s1} a suffix of @var{s2}, ignoring character case?")
1588#define FUNC_NAME s_scm_string_suffix_ci_p
1589{
63181a97
MV
1590 size_t cstart1, cend1, cstart2, cend2;
1591 size_t len = 0, len1;
1592
f846bd1a
MG
1593 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1594 3, start1, cstart1,
1595 4, end1, cend1);
1596 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1597 5, start2, cstart2,
1598 6, end2, cend2);
63181a97
MV
1599 len1 = cend1 - cstart1;
1600 while (cstart1 < cend1 && cstart2 < cend2)
1601 {
1602 cend1--;
1603 cend2--;
f846bd1a
MG
1604 if (uc_tolower (uc_toupper (scm_i_string_ref (s1, cend1)))
1605 != uc_tolower (uc_toupper (scm_i_string_ref (s2, cend2))))
3731fc67 1606 goto ret;
63181a97
MV
1607 len++;
1608 }
3731fc67
MV
1609
1610 ret:
1611 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
1612 return scm_from_bool (len == len1);
1613}
1614#undef FUNC_NAME
1615
1616
1617SCM_DEFINE (scm_string_index, "string-index", 2, 2, 0,
1618 (SCM s, SCM char_pred, SCM start, SCM end),
1619 "Search through the string @var{s} from left to right, returning\n"
1620 "the index of the first occurence of a character which\n"
1621 "\n"
1622 "@itemize @bullet\n"
1623 "@item\n"
1624 "equals @var{char_pred}, if it is character,\n"
1625 "\n"
1626 "@item\n"
1627 "satisifies the predicate @var{char_pred}, if it is a procedure,\n"
1628 "\n"
1629 "@item\n"
1630 "is in the set @var{char_pred}, if it is a character set.\n"
1631 "@end itemize")
1632#define FUNC_NAME s_scm_string_index
1633{
63181a97
MV
1634 size_t cstart, cend;
1635
f846bd1a
MG
1636 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1637 3, start, cstart,
1638 4, end, cend);
63181a97
MV
1639 if (SCM_CHARP (char_pred))
1640 {
63181a97
MV
1641 while (cstart < cend)
1642 {
f846bd1a 1643 if (scm_i_string_ref (s, cstart) == SCM_CHAR (char_pred))
3731fc67 1644 goto found;
63181a97
MV
1645 cstart++;
1646 }
1647 }
1648 else if (SCM_CHARSETP (char_pred))
1649 {
1650 while (cstart < cend)
1651 {
f846bd1a 1652 if (REF_IN_CHARSET (s, cstart, char_pred))
3731fc67 1653 goto found;
63181a97
MV
1654 cstart++;
1655 }
1656 }
1657 else
1658 {
3540b915
KR
1659 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
1660 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
1661
63181a97
MV
1662 while (cstart < cend)
1663 {
1664 SCM res;
f846bd1a 1665 res = pred_tramp (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97 1666 if (scm_is_true (res))
3731fc67 1667 goto found;
63181a97
MV
1668 cstart++;
1669 }
1670 }
3731fc67
MV
1671
1672 scm_remember_upto_here_1 (s);
63181a97 1673 return SCM_BOOL_F;
3731fc67
MV
1674
1675 found:
1676 scm_remember_upto_here_1 (s);
1677 return scm_from_size_t (cstart);
63181a97
MV
1678}
1679#undef FUNC_NAME
1680
1681SCM_DEFINE (scm_string_index_right, "string-index-right", 2, 2, 0,
1682 (SCM s, SCM char_pred, SCM start, SCM end),
1683 "Search through the string @var{s} from right to left, returning\n"
1684 "the index of the last occurence of a character which\n"
1685 "\n"
1686 "@itemize @bullet\n"
1687 "@item\n"
1688 "equals @var{char_pred}, if it is character,\n"
1689 "\n"
1690 "@item\n"
1691 "satisifies the predicate @var{char_pred}, if it is a procedure,\n"
1692 "\n"
1693 "@item\n"
1694 "is in the set if @var{char_pred} is a character set.\n"
1695 "@end itemize")
1696#define FUNC_NAME s_scm_string_index_right
1697{
63181a97
MV
1698 size_t cstart, cend;
1699
f846bd1a
MG
1700 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1701 3, start, cstart,
1702 4, end, cend);
63181a97
MV
1703 if (SCM_CHARP (char_pred))
1704 {
63181a97
MV
1705 while (cstart < cend)
1706 {
1707 cend--;
f846bd1a 1708 if (scm_i_string_ref (s, cend) == SCM_CHAR (char_pred))
3731fc67 1709 goto found;
63181a97
MV
1710 }
1711 }
1712 else if (SCM_CHARSETP (char_pred))
1713 {
1714 while (cstart < cend)
1715 {
1716 cend--;
f846bd1a 1717 if (REF_IN_CHARSET (s, cend, char_pred))
3731fc67 1718 goto found;
63181a97
MV
1719 }
1720 }
1721 else
1722 {
3540b915
KR
1723 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
1724 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
1725
63181a97
MV
1726 while (cstart < cend)
1727 {
1728 SCM res;
1729 cend--;
f846bd1a 1730 res = pred_tramp (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cend)));
63181a97 1731 if (scm_is_true (res))
3731fc67 1732 goto found;
63181a97
MV
1733 }
1734 }
3731fc67
MV
1735
1736 scm_remember_upto_here_1 (s);
63181a97 1737 return SCM_BOOL_F;
3731fc67
MV
1738
1739 found:
1740 scm_remember_upto_here_1 (s);
1741 return scm_from_size_t (cend);
63181a97
MV
1742}
1743#undef FUNC_NAME
1744
2562032b
MV
1745SCM_DEFINE (scm_string_rindex, "string-rindex", 2, 2, 0,
1746 (SCM s, SCM char_pred, SCM start, SCM end),
1747 "Search through the string @var{s} from right to left, returning\n"
1748 "the index of the last occurence of a character which\n"
1749 "\n"
1750 "@itemize @bullet\n"
1751 "@item\n"
1752 "equals @var{char_pred}, if it is character,\n"
1753 "\n"
1754 "@item\n"
1755 "satisifies the predicate @var{char_pred}, if it is a procedure,\n"
1756 "\n"
1757 "@item\n"
1758 "is in the set if @var{char_pred} is a character set.\n"
1759 "@end itemize")
3731fc67 1760#define FUNC_NAME s_scm_string_rindex
63181a97 1761{
2562032b 1762 return scm_string_index_right (s, char_pred, start, end);
63181a97 1763}
3731fc67 1764#undef FUNC_NAME
63181a97
MV
1765
1766SCM_DEFINE (scm_string_skip, "string-skip", 2, 2, 0,
1767 (SCM s, SCM char_pred, SCM start, SCM end),
1768 "Search through the string @var{s} from left to right, returning\n"
1769 "the index of the first occurence of a character which\n"
1770 "\n"
1771 "@itemize @bullet\n"
1772 "@item\n"
1773 "does not equal @var{char_pred}, if it is character,\n"
1774 "\n"
1775 "@item\n"
1776 "does not satisify the predicate @var{char_pred}, if it is a\n"
1777 "procedure,\n"
1778 "\n"
1779 "@item\n"
1780 "is not in the set if @var{char_pred} is a character set.\n"
1781 "@end itemize")
1782#define FUNC_NAME s_scm_string_skip
1783{
63181a97
MV
1784 size_t cstart, cend;
1785
f846bd1a
MG
1786 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1787 3, start, cstart,
1788 4, end, cend);
63181a97
MV
1789 if (SCM_CHARP (char_pred))
1790 {
63181a97
MV
1791 while (cstart < cend)
1792 {
f846bd1a 1793 if (scm_i_string_ref (s, cstart) != SCM_CHAR (char_pred))
3731fc67 1794 goto found;
63181a97
MV
1795 cstart++;
1796 }
1797 }
1798 else if (SCM_CHARSETP (char_pred))
1799 {
1800 while (cstart < cend)
1801 {
f846bd1a 1802 if (!REF_IN_CHARSET (s, cstart, char_pred))
3731fc67 1803 goto found;
63181a97
MV
1804 cstart++;
1805 }
1806 }
1807 else
1808 {
3540b915
KR
1809 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
1810 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
1811
63181a97
MV
1812 while (cstart < cend)
1813 {
1814 SCM res;
f846bd1a 1815 res = pred_tramp (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97 1816 if (scm_is_false (res))
3731fc67 1817 goto found;
63181a97
MV
1818 cstart++;
1819 }
1820 }
3731fc67
MV
1821
1822 scm_remember_upto_here_1 (s);
63181a97 1823 return SCM_BOOL_F;
3731fc67
MV
1824
1825 found:
1826 scm_remember_upto_here_1 (s);
1827 return scm_from_size_t (cstart);
63181a97
MV
1828}
1829#undef FUNC_NAME
1830
1831
1832SCM_DEFINE (scm_string_skip_right, "string-skip-right", 2, 2, 0,
1833 (SCM s, SCM char_pred, SCM start, SCM end),
1834 "Search through the string @var{s} from right to left, returning\n"
1835 "the index of the last occurence of a character which\n"
1836 "\n"
1837 "@itemize @bullet\n"
1838 "@item\n"
1839 "does not equal @var{char_pred}, if it is character,\n"
1840 "\n"
1841 "@item\n"
1842 "does not satisfy the predicate @var{char_pred}, if it is a\n"
1843 "procedure,\n"
1844 "\n"
1845 "@item\n"
1846 "is not in the set if @var{char_pred} is a character set.\n"
1847 "@end itemize")
1848#define FUNC_NAME s_scm_string_skip_right
1849{
63181a97
MV
1850 size_t cstart, cend;
1851
f846bd1a
MG
1852 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1853 3, start, cstart,
1854 4, end, cend);
63181a97
MV
1855 if (SCM_CHARP (char_pred))
1856 {
63181a97
MV
1857 while (cstart < cend)
1858 {
1859 cend--;
f846bd1a 1860 if (scm_i_string_ref (s, cend) != SCM_CHAR (char_pred))
3731fc67 1861 goto found;
63181a97
MV
1862 }
1863 }
1864 else if (SCM_CHARSETP (char_pred))
1865 {
1866 while (cstart < cend)
1867 {
1868 cend--;
f846bd1a 1869 if (!REF_IN_CHARSET (s, cend, char_pred))
3731fc67 1870 goto found;
63181a97
MV
1871 }
1872 }
1873 else
1874 {
3540b915
KR
1875 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
1876 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
1877
63181a97
MV
1878 while (cstart < cend)
1879 {
1880 SCM res;
1881 cend--;
f846bd1a 1882 res = pred_tramp (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cend)));
63181a97 1883 if (scm_is_false (res))
3731fc67 1884 goto found;
63181a97
MV
1885 }
1886 }
3731fc67
MV
1887
1888 scm_remember_upto_here_1 (s);
63181a97 1889 return SCM_BOOL_F;
3731fc67
MV
1890
1891 found:
1892 scm_remember_upto_here_1 (s);
1893 return scm_from_size_t (cend);
1894
63181a97
MV
1895}
1896#undef FUNC_NAME
1897
1898
1899SCM_DEFINE (scm_string_count, "string-count", 2, 2, 0,
1900 (SCM s, SCM char_pred, SCM start, SCM end),
1901 "Return the count of the number of characters in the string\n"
1902 "@var{s} which\n"
1903 "\n"
1904 "@itemize @bullet\n"
1905 "@item\n"
1906 "equals @var{char_pred}, if it is character,\n"
1907 "\n"
1908 "@item\n"
1909 "satisifies the predicate @var{char_pred}, if it is a procedure.\n"
1910 "\n"
1911 "@item\n"
1912 "is in the set @var{char_pred}, if it is a character set.\n"
1913 "@end itemize")
1914#define FUNC_NAME s_scm_string_count
1915{
63181a97
MV
1916 size_t cstart, cend;
1917 size_t count = 0;
1918
f846bd1a
MG
1919 MY_VALIDATE_SUBSTRING_SPEC (1, s,
1920 3, start, cstart,
1921 4, end, cend);
63181a97
MV
1922 if (SCM_CHARP (char_pred))
1923 {
63181a97
MV
1924 while (cstart < cend)
1925 {
f846bd1a 1926 if (scm_i_string_ref (s, cstart) == SCM_CHAR(char_pred))
63181a97
MV
1927 count++;
1928 cstart++;
1929 }
1930 }
1931 else if (SCM_CHARSETP (char_pred))
1932 {
1933 while (cstart < cend)
1934 {
f846bd1a 1935 if (REF_IN_CHARSET (s, cstart, char_pred))
63181a97
MV
1936 count++;
1937 cstart++;
1938 }
1939 }
1940 else
1941 {
3540b915
KR
1942 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
1943 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
1944
63181a97
MV
1945 while (cstart < cend)
1946 {
1947 SCM res;
f846bd1a 1948 res = pred_tramp (char_pred, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97
MV
1949 if (scm_is_true (res))
1950 count++;
63181a97
MV
1951 cstart++;
1952 }
1953 }
3731fc67
MV
1954
1955 scm_remember_upto_here_1 (s);
63181a97
MV
1956 return scm_from_size_t (count);
1957}
1958#undef FUNC_NAME
1959
1960
1961/* FIXME::martin: This should definitely get implemented more
1962 efficiently -- maybe with Knuth-Morris-Pratt, like in the reference
1963 implementation. */
1964SCM_DEFINE (scm_string_contains, "string-contains", 2, 4, 0,
1965 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
1966 "Does string @var{s1} contain string @var{s2}? Return the index\n"
1967 "in @var{s1} where @var{s2} occurs as a substring, or false.\n"
1968 "The optional start/end indices restrict the operation to the\n"
1969 "indicated substrings.")
1970#define FUNC_NAME s_scm_string_contains
1971{
63181a97
MV
1972 size_t cstart1, cend1, cstart2, cend2;
1973 size_t len2, i, j;
1974
f846bd1a
MG
1975 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
1976 3, start1, cstart1,
1977 4, end1, cend1);
1978 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
1979 5, start2, cstart2,
1980 6, end2, cend2);
63181a97 1981 len2 = cend2 - cstart2;
4a310f1c
MV
1982 if (cend1 - cstart1 >= len2)
1983 while (cstart1 <= cend1 - len2)
1984 {
1985 i = cstart1;
1986 j = cstart2;
f846bd1a
MG
1987 while (i < cend1
1988 && j < cend2
1989 && (scm_i_string_ref (s1, i)
1990 == scm_i_string_ref (s2, j)))
4a310f1c
MV
1991 {
1992 i++;
1993 j++;
1994 }
1995 if (j == cend2)
1996 {
1997 scm_remember_upto_here_2 (s1, s2);
1998 return scm_from_size_t (cstart1);
1999 }
2000 cstart1++;
2001 }
3731fc67
MV
2002
2003 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
2004 return SCM_BOOL_F;
2005}
2006#undef FUNC_NAME
2007
2008
2009/* FIXME::martin: This should definitely get implemented more
2010 efficiently -- maybe with Knuth-Morris-Pratt, like in the reference
2011 implementation. */
2012SCM_DEFINE (scm_string_contains_ci, "string-contains-ci", 2, 4, 0,
2013 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
2014 "Does string @var{s1} contain string @var{s2}? Return the index\n"
2015 "in @var{s1} where @var{s2} occurs as a substring, or false.\n"
2016 "The optional start/end indices restrict the operation to the\n"
2017 "indicated substrings. Character comparison is done\n"
2018 "case-insensitively.")
2019#define FUNC_NAME s_scm_string_contains_ci
2020{
63181a97
MV
2021 size_t cstart1, cend1, cstart2, cend2;
2022 size_t len2, i, j;
2023
f846bd1a
MG
2024 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
2025 3, start1, cstart1,
2026 4, end1, cend1);
2027 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
2028 5, start2, cstart2,
2029 6, end2, cend2);
63181a97 2030 len2 = cend2 - cstart2;
4a310f1c
MV
2031 if (cend1 - cstart1 >= len2)
2032 while (cstart1 <= cend1 - len2)
2033 {
2034 i = cstart1;
2035 j = cstart2;
f846bd1a
MG
2036 while (i < cend1
2037 && j < cend2
2038 && (uc_tolower (uc_toupper (scm_i_string_ref (s1, i)))
2039 == uc_tolower (uc_toupper (scm_i_string_ref (s2, j)))))
4a310f1c
MV
2040 {
2041 i++;
2042 j++;
2043 }
2044 if (j == cend2)
2045 {
2046 scm_remember_upto_here_2 (s1, s2);
2047 return scm_from_size_t (cstart1);
2048 }
2049 cstart1++;
2050 }
3731fc67
MV
2051
2052 scm_remember_upto_here_2 (s1, s2);
63181a97
MV
2053 return SCM_BOOL_F;
2054}
2055#undef FUNC_NAME
2056
2057
f846bd1a 2058/* Helper function for the string uppercase conversion functions. */
63181a97 2059static SCM
1a82a460 2060string_upcase_x (SCM v, size_t start, size_t end)
63181a97
MV
2061{
2062 size_t k;
63181a97 2063
9c44cd45 2064 v = scm_i_string_start_writing (v);
63181a97 2065 for (k = start; k < end; ++k)
f846bd1a 2066 scm_i_string_set_x (v, k, uc_toupper (scm_i_string_ref (v, k)));
63181a97 2067 scm_i_string_stop_writing ();
3731fc67 2068 scm_remember_upto_here_1 (v);
63181a97
MV
2069
2070 return v;
2071}
2072
2073SCM_DEFINE (scm_substring_upcase_x, "string-upcase!", 1, 2, 0,
2074 (SCM str, SCM start, SCM end),
2075 "Destructively upcase every character in @code{str}.\n"
2076 "\n"
2077 "@lisp\n"
2078 "(string-upcase! y)\n"
2079 "@result{} \"ARRDEFG\"\n"
2080 "y\n"
2081 "@result{} \"ARRDEFG\"\n"
2082 "@end lisp")
2083#define FUNC_NAME s_scm_substring_upcase_x
2084{
63181a97
MV
2085 size_t cstart, cend;
2086
f846bd1a
MG
2087 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2088 2, start, cstart,
2089 3, end, cend);
63181a97
MV
2090 return string_upcase_x (str, cstart, cend);
2091}
2092#undef FUNC_NAME
2093
2094SCM
2095scm_string_upcase_x (SCM str)
2096{
2097 return scm_substring_upcase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
2098}
2099
2100SCM_DEFINE (scm_substring_upcase, "string-upcase", 1, 2, 0,
2101 (SCM str, SCM start, SCM end),
2102 "Upcase every character in @code{str}.")
2103#define FUNC_NAME s_scm_substring_upcase
2104{
63181a97
MV
2105 size_t cstart, cend;
2106
f846bd1a
MG
2107 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2108 2, start, cstart,
2109 3, end, cend);
63181a97
MV
2110 return string_upcase_x (scm_string_copy (str), cstart, cend);
2111}
2112#undef FUNC_NAME
2113
2114SCM
2115scm_string_upcase (SCM str)
2116{
2117 return scm_substring_upcase (str, SCM_UNDEFINED, SCM_UNDEFINED);
2118}
2119
2120/* Helper function for the string lowercase conversion functions.
2121 * No argument checking is performed. */
2122static SCM
1a82a460 2123string_downcase_x (SCM v, size_t start, size_t end)
63181a97
MV
2124{
2125 size_t k;
63181a97 2126
9c44cd45 2127 v = scm_i_string_start_writing (v);
63181a97 2128 for (k = start; k < end; ++k)
f846bd1a 2129 scm_i_string_set_x (v, k, uc_tolower (scm_i_string_ref (v, k)));
63181a97 2130 scm_i_string_stop_writing ();
3731fc67 2131 scm_remember_upto_here_1 (v);
63181a97
MV
2132
2133 return v;
2134}
2135
2136SCM_DEFINE (scm_substring_downcase_x, "string-downcase!", 1, 2, 0,
2137 (SCM str, SCM start, SCM end),
2138 "Destructively downcase every character in @var{str}.\n"
2139 "\n"
2140 "@lisp\n"
2141 "y\n"
2142 "@result{} \"ARRDEFG\"\n"
2143 "(string-downcase! y)\n"
2144 "@result{} \"arrdefg\"\n"
2145 "y\n"
2146 "@result{} \"arrdefg\"\n"
2147 "@end lisp")
2148#define FUNC_NAME s_scm_substring_downcase_x
2149{
63181a97
MV
2150 size_t cstart, cend;
2151
f846bd1a
MG
2152 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2153 2, start, cstart,
2154 3, end, cend);
63181a97
MV
2155 return string_downcase_x (str, cstart, cend);
2156}
2157#undef FUNC_NAME
2158
2159SCM
2160scm_string_downcase_x (SCM str)
2161{
2162 return scm_substring_downcase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
2163}
2164
2165SCM_DEFINE (scm_substring_downcase, "string-downcase", 1, 2, 0,
2166 (SCM str, SCM start, SCM end),
2167 "Downcase every character in @var{str}.")
2168#define FUNC_NAME s_scm_substring_downcase
2169{
63181a97
MV
2170 size_t cstart, cend;
2171
f846bd1a
MG
2172 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2173 2, start, cstart,
2174 3, end, cend);
63181a97
MV
2175 return string_downcase_x (scm_string_copy (str), cstart, cend);
2176}
2177#undef FUNC_NAME
2178
2179SCM
2180scm_string_downcase (SCM str)
2181{
2182 return scm_substring_downcase (str, SCM_UNDEFINED, SCM_UNDEFINED);
2183}
2184
2185/* Helper function for the string capitalization functions.
2186 * No argument checking is performed. */
2187static SCM
1a82a460 2188string_titlecase_x (SCM str, size_t start, size_t end)
63181a97 2189{
f846bd1a 2190 SCM ch;
63181a97
MV
2191 size_t i;
2192 int in_word = 0;
2193
9c44cd45 2194 str = scm_i_string_start_writing (str);
63181a97
MV
2195 for(i = start; i < end; i++)
2196 {
f846bd1a
MG
2197 ch = SCM_MAKE_CHAR (scm_i_string_ref (str, i));
2198 if (scm_is_true (scm_char_alphabetic_p (ch)))
63181a97
MV
2199 {
2200 if (!in_word)
2201 {
f846bd1a 2202 scm_i_string_set_x (str, i, uc_toupper (SCM_CHAR (ch)));
63181a97
MV
2203 in_word = 1;
2204 }
2205 else
2206 {
f846bd1a 2207 scm_i_string_set_x (str, i, uc_tolower (SCM_CHAR (ch)));
63181a97
MV
2208 }
2209 }
2210 else
2211 in_word = 0;
2212 }
2213 scm_i_string_stop_writing ();
3731fc67 2214 scm_remember_upto_here_1 (str);
63181a97
MV
2215
2216 return str;
2217}
2218
2219
2220SCM_DEFINE (scm_string_titlecase_x, "string-titlecase!", 1, 2, 0,
2221 (SCM str, SCM start, SCM end),
2222 "Destructively titlecase every first character in a word in\n"
2223 "@var{str}.")
2224#define FUNC_NAME s_scm_string_titlecase_x
2225{
63181a97
MV
2226 size_t cstart, cend;
2227
f846bd1a
MG
2228 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2229 2, start, cstart,
2230 3, end, cend);
63181a97
MV
2231 return string_titlecase_x (str, cstart, cend);
2232}
2233#undef FUNC_NAME
2234
2235
2236SCM_DEFINE (scm_string_titlecase, "string-titlecase", 1, 2, 0,
2237 (SCM str, SCM start, SCM end),
2238 "Titlecase every first character in a word in @var{str}.")
2239#define FUNC_NAME s_scm_string_titlecase
2240{
63181a97
MV
2241 size_t cstart, cend;
2242
f846bd1a
MG
2243 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2244 2, start, cstart,
2245 3, end, cend);
63181a97
MV
2246 return string_titlecase_x (scm_string_copy (str), cstart, cend);
2247}
2248#undef FUNC_NAME
2249
63181a97
MV
2250SCM_DEFINE (scm_string_capitalize_x, "string-capitalize!", 1, 0, 0,
2251 (SCM str),
2252 "Upcase the first character of every word in @var{str}\n"
2253 "destructively and return @var{str}.\n"
2254 "\n"
2255 "@lisp\n"
2256 "y @result{} \"hello world\"\n"
2257 "(string-capitalize! y) @result{} \"Hello World\"\n"
2258 "y @result{} \"Hello World\"\n"
2259 "@end lisp")
2260#define FUNC_NAME s_scm_string_capitalize_x
2261{
2262 return scm_string_titlecase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
2263}
2264#undef FUNC_NAME
2265
2266
2267SCM_DEFINE (scm_string_capitalize, "string-capitalize", 1, 0, 0,
2268 (SCM str),
2269 "Return a freshly allocated string with the characters in\n"
2270 "@var{str}, where the first character of every word is\n"
2271 "capitalized.")
2272#define FUNC_NAME s_scm_string_capitalize
2273{
2274 return scm_string_capitalize_x (scm_string_copy (str));
2275}
2276#undef FUNC_NAME
2277
2278
2279/* Reverse the portion of @var{str} between str[cstart] (including)
2280 and str[cend] excluding. */
2281static void
f846bd1a 2282string_reverse_x (SCM str, size_t cstart, size_t cend)
63181a97 2283{
f846bd1a 2284 SCM tmp;
63181a97 2285
f846bd1a 2286 str = scm_i_string_start_writing (str);
1a82a460 2287 if (cend > 0)
63181a97 2288 {
63181a97 2289 cend--;
1a82a460
MV
2290 while (cstart < cend)
2291 {
f846bd1a
MG
2292 tmp = SCM_MAKE_CHAR (scm_i_string_ref (str, cstart));
2293 scm_i_string_set_x (str, cstart, scm_i_string_ref (str, cend));
2294 scm_i_string_set_x (str, cend, SCM_CHAR (tmp));
1a82a460
MV
2295 cstart++;
2296 cend--;
2297 }
63181a97 2298 }
f846bd1a 2299 scm_i_string_stop_writing ();
63181a97
MV
2300}
2301
2302
2303SCM_DEFINE (scm_string_reverse, "string-reverse", 1, 2, 0,
2304 (SCM str, SCM start, SCM end),
2305 "Reverse the string @var{str}. The optional arguments\n"
2306 "@var{start} and @var{end} delimit the region of @var{str} to\n"
2307 "operate on.")
2308#define FUNC_NAME s_scm_string_reverse
2309{
63181a97
MV
2310 size_t cstart, cend;
2311 SCM result;
2312
f846bd1a
MG
2313 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2314 2, start, cstart,
2315 3, end, cend);
63181a97 2316 result = scm_string_copy (str);
f846bd1a 2317 string_reverse_x (result, cstart, cend);
3731fc67 2318 scm_remember_upto_here_1 (str);
63181a97
MV
2319 return result;
2320}
2321#undef FUNC_NAME
2322
2323
2324SCM_DEFINE (scm_string_reverse_x, "string-reverse!", 1, 2, 0,
2325 (SCM str, SCM start, SCM end),
2326 "Reverse the string @var{str} in-place. The optional arguments\n"
2327 "@var{start} and @var{end} delimit the region of @var{str} to\n"
2328 "operate on. The return value is unspecified.")
2329#define FUNC_NAME s_scm_string_reverse_x
2330{
63181a97
MV
2331 size_t cstart, cend;
2332
2333 MY_VALIDATE_SUBSTRING_SPEC (1, str,
2334 2, start, cstart,
2335 3, end, cend);
2336
f846bd1a 2337 string_reverse_x (str, cstart, cend);
63181a97
MV
2338 scm_remember_upto_here_1 (str);
2339 return SCM_UNSPECIFIED;
2340}
2341#undef FUNC_NAME
2342
2343
2344SCM_DEFINE (scm_string_append_shared, "string-append/shared", 0, 0, 1,
a003f3eb 2345 (SCM rest),
63181a97
MV
2346 "Like @code{string-append}, but the result may share memory\n"
2347 "with the argument strings.")
2348#define FUNC_NAME s_scm_string_append_shared
2349{
a003f3eb
KR
2350 /* If "rest" contains just one non-empty string, return that.
2351 If it's entirely empty strings, then return scm_nullstr.
2352 Otherwise use scm_string_concatenate. */
63181a97 2353
a003f3eb
KR
2354 SCM ret = scm_nullstr;
2355 int seen_nonempty = 0;
2356 SCM l, s;
63181a97 2357
a003f3eb
KR
2358 SCM_VALIDATE_REST_ARGUMENT (rest);
2359
2360 for (l = rest; scm_is_pair (l); l = SCM_CDR (l))
2361 {
2362 s = SCM_CAR (l);
f846bd1a
MG
2363 if (!scm_is_string (s))
2364 scm_wrong_type_arg (FUNC_NAME, 0, s);
2365 if (scm_i_string_length (s) != 0)
a003f3eb
KR
2366 {
2367 if (seen_nonempty)
2368 /* two or more non-empty strings, need full concat */
2369 return scm_string_append (rest);
2370
2371 seen_nonempty = 1;
2372 ret = s;
2373 }
2374 }
2375 return ret;
63181a97
MV
2376}
2377#undef FUNC_NAME
2378
2379
2380SCM_DEFINE (scm_string_concatenate, "string-concatenate", 1, 0, 0,
2381 (SCM ls),
2382 "Append the elements of @var{ls} (which must be strings)\n"
2383 "together into a single string. Guaranteed to return a freshly\n"
2384 "allocated string.")
2385#define FUNC_NAME s_scm_string_concatenate
2386{
57d98034 2387 SCM_VALIDATE_LIST (SCM_ARG1, ls);
63181a97
MV
2388 return scm_string_append (ls);
2389}
2390#undef FUNC_NAME
2391
2392
2393SCM_DEFINE (scm_string_concatenate_reverse, "string-concatenate-reverse", 1, 2, 0,
2394 (SCM ls, SCM final_string, SCM end),
2395 "Without optional arguments, this procedure is equivalent to\n"
2396 "\n"
2397 "@smalllisp\n"
2398 "(string-concatenate (reverse ls))\n"
2399 "@end smalllisp\n"
2400 "\n"
2401 "If the optional argument @var{final_string} is specified, it is\n"
2402 "consed onto the beginning to @var{ls} before performing the\n"
2403 "list-reverse and string-concatenate operations. If @var{end}\n"
2404 "is given, only the characters of @var{final_string} up to index\n"
2405 "@var{end} are used.\n"
2406 "\n"
2407 "Guaranteed to return a freshly allocated string.")
2408#define FUNC_NAME s_scm_string_concatenate_reverse
2409{
3731fc67
MV
2410 if (!SCM_UNBNDP (end))
2411 final_string = scm_substring (final_string, SCM_INUM0, end);
63181a97 2412
63181a97 2413 if (!SCM_UNBNDP (final_string))
3731fc67 2414 ls = scm_cons (final_string, ls);
63181a97 2415
3731fc67 2416 return scm_string_concatenate (scm_reverse (ls));
63181a97
MV
2417}
2418#undef FUNC_NAME
2419
2420
2421SCM_DEFINE (scm_string_concatenate_shared, "string-concatenate/shared", 1, 0, 0,
2422 (SCM ls),
2423 "Like @code{string-concatenate}, but the result may share memory\n"
2424 "with the strings in the list @var{ls}.")
2425#define FUNC_NAME s_scm_string_concatenate_shared
2426{
57d98034 2427 SCM_VALIDATE_LIST (SCM_ARG1, ls);
63181a97
MV
2428 return scm_string_append_shared (ls);
2429}
2430#undef FUNC_NAME
2431
2432
2433SCM_DEFINE (scm_string_concatenate_reverse_shared, "string-concatenate-reverse/shared", 1, 2, 0,
2434 (SCM ls, SCM final_string, SCM end),
2435 "Like @code{string-concatenate-reverse}, but the result may\n"
2436 "share memory with the the strings in the @var{ls} arguments.")
2437#define FUNC_NAME s_scm_string_concatenate_reverse_shared
2438{
2439 /* Just call the non-sharing version. */
2440 return scm_string_concatenate_reverse (ls, final_string, end);
2441}
2442#undef FUNC_NAME
2443
2444
2445SCM_DEFINE (scm_string_map, "string-map", 2, 2, 0,
2446 (SCM proc, SCM s, SCM start, SCM end),
2447 "@var{proc} is a char->char procedure, it is mapped over\n"
2448 "@var{s}. The order in which the procedure is applied to the\n"
2449 "string elements is not specified.")
2450#define FUNC_NAME s_scm_string_map
2451{
f846bd1a 2452 size_t p;
63181a97
MV
2453 size_t cstart, cend;
2454 SCM result;
3540b915 2455 scm_t_trampoline_1 proc_tramp = scm_trampoline_1 (proc);
63181a97 2456
3540b915 2457 SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
3731fc67
MV
2458 MY_VALIDATE_SUBSTRING_SPEC (2, s,
2459 3, start, cstart,
2460 4, end, cend);
f846bd1a
MG
2461 result = scm_i_make_string (cend - cstart, NULL);
2462 p = 0;
63181a97
MV
2463 while (cstart < cend)
2464 {
3540b915 2465 SCM ch = proc_tramp (proc, scm_c_string_ref (s, cstart));
63181a97
MV
2466 if (!SCM_CHARP (ch))
2467 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (proc));
63181a97 2468 cstart++;
f846bd1a
MG
2469 result = scm_i_string_start_writing (result);
2470 scm_i_string_set_x (result, p, SCM_CHAR (ch));
2471 scm_i_string_stop_writing ();
2472 p++;
63181a97 2473 }
f846bd1a 2474
63181a97
MV
2475 return result;
2476}
2477#undef FUNC_NAME
2478
2479
2480SCM_DEFINE (scm_string_map_x, "string-map!", 2, 2, 0,
2481 (SCM proc, SCM s, SCM start, SCM end),
2482 "@var{proc} is a char->char procedure, it is mapped over\n"
2483 "@var{s}. The order in which the procedure is applied to the\n"
2484 "string elements is not specified. The string @var{s} is\n"
2485 "modified in-place, the return value is not specified.")
2486#define FUNC_NAME s_scm_string_map_x
2487{
2488 size_t cstart, cend;
3540b915 2489 scm_t_trampoline_1 proc_tramp = scm_trampoline_1 (proc);
63181a97 2490
3540b915 2491 SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
63181a97
MV
2492 MY_VALIDATE_SUBSTRING_SPEC (2, s,
2493 3, start, cstart,
2494 4, end, cend);
2495 while (cstart < cend)
2496 {
3540b915 2497 SCM ch = proc_tramp (proc, scm_c_string_ref (s, cstart));
63181a97
MV
2498 if (!SCM_CHARP (ch))
2499 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (proc));
f846bd1a
MG
2500 s = scm_i_string_start_writing (s);
2501 scm_i_string_set_x (s, cstart, SCM_CHAR (ch));
2502 scm_i_string_stop_writing ();
63181a97
MV
2503 cstart++;
2504 }
2505 return SCM_UNSPECIFIED;
2506}
2507#undef FUNC_NAME
2508
2509
2510SCM_DEFINE (scm_string_fold, "string-fold", 3, 2, 0,
2511 (SCM kons, SCM knil, SCM s, SCM start, SCM end),
2512 "Fold @var{kons} over the characters of @var{s}, with @var{knil}\n"
2513 "as the terminating element, from left to right. @var{kons}\n"
2514 "must expect two arguments: The actual character and the last\n"
2515 "result of @var{kons}' application.")
2516#define FUNC_NAME s_scm_string_fold
2517{
63181a97
MV
2518 size_t cstart, cend;
2519 SCM result;
2520
2521 SCM_VALIDATE_PROC (1, kons);
f846bd1a
MG
2522 MY_VALIDATE_SUBSTRING_SPEC (3, s,
2523 4, start, cstart,
2524 5, end, cend);
63181a97
MV
2525 result = knil;
2526 while (cstart < cend)
2527 {
f846bd1a 2528 result = scm_call_2 (kons, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)), result);
63181a97
MV
2529 cstart++;
2530 }
3731fc67
MV
2531
2532 scm_remember_upto_here_1 (s);
63181a97
MV
2533 return result;
2534}
2535#undef FUNC_NAME
2536
2537
2538SCM_DEFINE (scm_string_fold_right, "string-fold-right", 3, 2, 0,
2539 (SCM kons, SCM knil, SCM s, SCM start, SCM end),
2540 "Fold @var{kons} over the characters of @var{s}, with @var{knil}\n"
2541 "as the terminating element, from right to left. @var{kons}\n"
2542 "must expect two arguments: The actual character and the last\n"
2543 "result of @var{kons}' application.")
2544#define FUNC_NAME s_scm_string_fold_right
2545{
63181a97
MV
2546 size_t cstart, cend;
2547 SCM result;
2548
2549 SCM_VALIDATE_PROC (1, kons);
f846bd1a
MG
2550 MY_VALIDATE_SUBSTRING_SPEC (3, s,
2551 4, start, cstart,
2552 5, end, cend);
63181a97
MV
2553 result = knil;
2554 while (cstart < cend)
2555 {
f846bd1a 2556 result = scm_call_2 (kons, SCM_MAKE_CHAR (scm_i_string_ref (s, cend-1)), result);
63181a97
MV
2557 cend--;
2558 }
3731fc67
MV
2559
2560 scm_remember_upto_here_1 (s);
63181a97
MV
2561 return result;
2562}
2563#undef FUNC_NAME
2564
2565
2566SCM_DEFINE (scm_string_unfold, "string-unfold", 4, 2, 0,
2567 (SCM p, SCM f, SCM g, SCM seed, SCM base, SCM make_final),
2568 "@itemize @bullet\n"
2569 "@item @var{g} is used to generate a series of @emph{seed}\n"
2570 "values from the initial @var{seed}: @var{seed}, (@var{g}\n"
2571 "@var{seed}), (@var{g}^2 @var{seed}), (@var{g}^3 @var{seed}),\n"
2572 "@dots{}\n"
2573 "@item @var{p} tells us when to stop -- when it returns true\n"
2574 "when applied to one of these seed values.\n"
2575 "@item @var{f} maps each seed value to the corresponding\n"
2576 "character in the result string. These chars are assembled\n"
2577 "into the string in a left-to-right order.\n"
2578 "@item @var{base} is the optional initial/leftmost portion\n"
2579 "of the constructed string; it default to the empty\n"
2580 "string.\n"
2581 "@item @var{make_final} is applied to the terminal seed\n"
2582 "value (on which @var{p} returns true) to produce\n"
2583 "the final/rightmost portion of the constructed string.\n"
2584 "It defaults to @code{(lambda (x) "")}.\n"
2585 "@end itemize")
2586#define FUNC_NAME s_scm_string_unfold
2587{
2588 SCM res, ans;
2589
2590 SCM_VALIDATE_PROC (1, p);
2591 SCM_VALIDATE_PROC (2, f);
2592 SCM_VALIDATE_PROC (3, g);
2593 if (!SCM_UNBNDP (base))
2594 {
2595 SCM_VALIDATE_STRING (5, base);
2596 ans = base;
2597 }
2598 else
2599 ans = scm_i_make_string (0, NULL);
2600 if (!SCM_UNBNDP (make_final))
2601 SCM_VALIDATE_PROC (6, make_final);
2602
2603 res = scm_call_1 (p, seed);
2604 while (scm_is_false (res))
2605 {
2606 SCM str;
f846bd1a 2607 size_t i = 0;
63181a97
MV
2608 SCM ch = scm_call_1 (f, seed);
2609 if (!SCM_CHARP (ch))
2610 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (f));
f846bd1a
MG
2611 str = scm_i_make_string (1, NULL);
2612 str = scm_i_string_start_writing (str);
2613 scm_i_string_set_x (str, i, SCM_CHAR (ch));
2614 scm_i_string_stop_writing ();
2615 i++;
63181a97
MV
2616
2617 ans = scm_string_append (scm_list_2 (ans, str));
2618 seed = scm_call_1 (g, seed);
2619 res = scm_call_1 (p, seed);
2620 }
2621 if (!SCM_UNBNDP (make_final))
2622 {
2623 res = scm_call_1 (make_final, seed);
2624 return scm_string_append (scm_list_2 (ans, res));
2625 }
2626 else
2627 return ans;
2628}
2629#undef FUNC_NAME
2630
2631
2632SCM_DEFINE (scm_string_unfold_right, "string-unfold-right", 4, 2, 0,
2633 (SCM p, SCM f, SCM g, SCM seed, SCM base, SCM make_final),
2634 "@itemize @bullet\n"
2635 "@item @var{g} is used to generate a series of @emph{seed}\n"
2636 "values from the initial @var{seed}: @var{seed}, (@var{g}\n"
2637 "@var{seed}), (@var{g}^2 @var{seed}), (@var{g}^3 @var{seed}),\n"
2638 "@dots{}\n"
2639 "@item @var{p} tells us when to stop -- when it returns true\n"
2640 "when applied to one of these seed values.\n"
2641 "@item @var{f} maps each seed value to the corresponding\n"
2642 "character in the result string. These chars are assembled\n"
2643 "into the string in a right-to-left order.\n"
2644 "@item @var{base} is the optional initial/rightmost portion\n"
2645 "of the constructed string; it default to the empty\n"
2646 "string.\n"
2647 "@item @var{make_final} is applied to the terminal seed\n"
2648 "value (on which @var{p} returns true) to produce\n"
2649 "the final/leftmost portion of the constructed string.\n"
2650 "It defaults to @code{(lambda (x) "")}.\n"
2651 "@end itemize")
2652#define FUNC_NAME s_scm_string_unfold_right
2653{
2654 SCM res, ans;
2655
2656 SCM_VALIDATE_PROC (1, p);
2657 SCM_VALIDATE_PROC (2, f);
2658 SCM_VALIDATE_PROC (3, g);
2659 if (!SCM_UNBNDP (base))
2660 {
2661 SCM_VALIDATE_STRING (5, base);
2662 ans = base;
2663 }
2664 else
2665 ans = scm_i_make_string (0, NULL);
2666 if (!SCM_UNBNDP (make_final))
2667 SCM_VALIDATE_PROC (6, make_final);
2668
2669 res = scm_call_1 (p, seed);
2670 while (scm_is_false (res))
2671 {
2672 SCM str;
f846bd1a 2673 size_t i = 0;
63181a97
MV
2674 SCM ch = scm_call_1 (f, seed);
2675 if (!SCM_CHARP (ch))
2676 SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (f));
f846bd1a
MG
2677 str = scm_i_make_string (1, NULL);
2678 str = scm_i_string_start_writing (str);
2679 scm_i_string_set_x (str, i, SCM_CHAR (ch));
2680 scm_i_string_stop_writing ();
2681 i++;
63181a97
MV
2682
2683 ans = scm_string_append (scm_list_2 (str, ans));
2684 seed = scm_call_1 (g, seed);
2685 res = scm_call_1 (p, seed);
2686 }
2687 if (!SCM_UNBNDP (make_final))
2688 {
2689 res = scm_call_1 (make_final, seed);
2690 return scm_string_append (scm_list_2 (res, ans));
2691 }
2692 else
2693 return ans;
2694}
2695#undef FUNC_NAME
2696
2697
2698SCM_DEFINE (scm_string_for_each, "string-for-each", 2, 2, 0,
2699 (SCM proc, SCM s, SCM start, SCM end),
2700 "@var{proc} is mapped over @var{s} in left-to-right order. The\n"
2701 "return value is not specified.")
2702#define FUNC_NAME s_scm_string_for_each
2703{
63181a97 2704 size_t cstart, cend;
3540b915 2705 scm_t_trampoline_1 proc_tramp = scm_trampoline_1 (proc);
63181a97 2706
3540b915 2707 SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
9c44cd45
MG
2708 MY_VALIDATE_SUBSTRING_SPEC (2, s,
2709 3, start, cstart,
2710 4, end, cend);
63181a97
MV
2711 while (cstart < cend)
2712 {
9c44cd45 2713 proc_tramp (proc, SCM_MAKE_CHAR (scm_i_string_ref (s, cstart)));
63181a97
MV
2714 cstart++;
2715 }
3731fc67
MV
2716
2717 scm_remember_upto_here_1 (s);
63181a97
MV
2718 return SCM_UNSPECIFIED;
2719}
2720#undef FUNC_NAME
2721
2722SCM_DEFINE (scm_string_for_each_index, "string-for-each-index", 2, 2, 0,
2723 (SCM proc, SCM s, SCM start, SCM end),
1729055b
KR
2724 "Call @code{(@var{proc} i)} for each index i in @var{s}, from\n"
2725 "left to right.\n"
2726 "\n"
2727 "For example, to change characters to alternately upper and\n"
2728 "lower case,\n"
2729 "\n"
2730 "@example\n"
2731 "(define str (string-copy \"studly\"))\n"
2732 "(string-for-each-index\n"
2733 " (lambda (i)\n"
2734 " (string-set! str i\n"
2735 " ((if (even? i) char-upcase char-downcase)\n"
2736 " (string-ref str i))))\n"
2737 " str)\n"
2738 "str @result{} \"StUdLy\"\n"
2739 "@end example")
63181a97
MV
2740#define FUNC_NAME s_scm_string_for_each_index
2741{
63181a97 2742 size_t cstart, cend;
3540b915 2743 scm_t_trampoline_1 proc_tramp = scm_trampoline_1 (proc);
63181a97 2744
3540b915 2745 SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
3731fc67
MV
2746 MY_VALIDATE_SUBSTRING_SPEC (2, s,
2747 3, start, cstart,
2748 4, end, cend);
2749
63181a97
MV
2750 while (cstart < cend)
2751 {
3540b915 2752 proc_tramp (proc, scm_from_size_t (cstart));
63181a97
MV
2753 cstart++;
2754 }
3731fc67
MV
2755
2756 scm_remember_upto_here_1 (s);
63181a97
MV
2757 return SCM_UNSPECIFIED;
2758}
2759#undef FUNC_NAME
2760
2761SCM_DEFINE (scm_xsubstring, "xsubstring", 2, 3, 0,
2762 (SCM s, SCM from, SCM to, SCM start, SCM end),
2763 "This is the @emph{extended substring} procedure that implements\n"
2764 "replicated copying of a substring of some string.\n"
2765 "\n"
2766 "@var{s} is a string, @var{start} and @var{end} are optional\n"
2767 "arguments that demarcate a substring of @var{s}, defaulting to\n"
2768 "0 and the length of @var{s}. Replicate this substring up and\n"
2769 "down index space, in both the positive and negative directions.\n"
2770 "@code{xsubstring} returns the substring of this string\n"
2771 "beginning at index @var{from}, and ending at @var{to}, which\n"
2772 "defaults to @var{from} + (@var{end} - @var{start}).")
2773#define FUNC_NAME s_scm_xsubstring
2774{
f846bd1a 2775 size_t p;
1a82a460
MV
2776 size_t cstart, cend;
2777 int cfrom, cto;
63181a97
MV
2778 SCM result;
2779
3731fc67
MV
2780 MY_VALIDATE_SUBSTRING_SPEC (1, s,
2781 4, start, cstart,
2782 5, end, cend);
2783
1a82a460 2784 cfrom = scm_to_int (from);
63181a97
MV
2785 if (SCM_UNBNDP (to))
2786 cto = cfrom + (cend - cstart);
2787 else
1a82a460 2788 cto = scm_to_int (to);
63181a97
MV
2789 if (cstart == cend && cfrom != cto)
2790 SCM_MISC_ERROR ("start and end indices must not be equal", SCM_EOL);
2791
f846bd1a
MG
2792 result = scm_i_make_string (cto - cfrom, NULL);
2793 result = scm_i_string_start_writing (result);
63181a97 2794
f846bd1a 2795 p = 0;
63181a97
MV
2796 while (cfrom < cto)
2797 {
1a82a460 2798 size_t t = ((cfrom < 0) ? -cfrom : cfrom) % (cend - cstart);
63181a97 2799 if (cfrom < 0)
f846bd1a
MG
2800 scm_i_string_set_x (result, p,
2801 scm_i_string_ref (s, (cend - cstart) - t));
63181a97 2802 else
f846bd1a 2803 scm_i_string_set_x (result, p, scm_i_string_ref (s, t));
63181a97
MV
2804 cfrom++;
2805 p++;
2806 }
f846bd1a 2807 scm_i_string_stop_writing ();
3731fc67 2808
63181a97
MV
2809 scm_remember_upto_here_1 (s);
2810 return result;
2811}
2812#undef FUNC_NAME
2813
2814
2815SCM_DEFINE (scm_string_xcopy_x, "string-xcopy!", 4, 3, 0,
2816 (SCM target, SCM tstart, SCM s, SCM sfrom, SCM sto, SCM start, SCM end),
2817 "Exactly the same as @code{xsubstring}, but the extracted text\n"
2818 "is written into the string @var{target} starting at index\n"
2819 "@var{tstart}. The operation is not defined if @code{(eq?\n"
2820 "@var{target} @var{s})} or these arguments share storage -- you\n"
2821 "cannot copy a string on top of itself.")
2822#define FUNC_NAME s_scm_string_xcopy_x
2823{
f846bd1a 2824 size_t p;
1a82a460
MV
2825 size_t ctstart, cstart, cend;
2826 int csfrom, csto;
63181a97 2827 SCM dummy = SCM_UNDEFINED;
616775ed 2828 size_t cdummy;
63181a97
MV
2829
2830 MY_VALIDATE_SUBSTRING_SPEC (1, target,
2831 2, tstart, ctstart,
2832 2, dummy, cdummy);
3731fc67
MV
2833 MY_VALIDATE_SUBSTRING_SPEC (3, s,
2834 6, start, cstart,
2835 7, end, cend);
1a82a460 2836 csfrom = scm_to_int (sfrom);
63181a97
MV
2837 if (SCM_UNBNDP (sto))
2838 csto = csfrom + (cend - cstart);
2839 else
1a82a460 2840 csto = scm_to_int (sto);
63181a97
MV
2841 if (cstart == cend && csfrom != csto)
2842 SCM_MISC_ERROR ("start and end indices must not be equal", SCM_EOL);
2843 SCM_ASSERT_RANGE (1, tstart,
2844 ctstart + (csto - csfrom) <= scm_i_string_length (target));
2845
f846bd1a 2846 p = 0;
9c44cd45 2847 target = scm_i_string_start_writing (target);
63181a97
MV
2848 while (csfrom < csto)
2849 {
1a82a460 2850 size_t t = ((csfrom < 0) ? -csfrom : csfrom) % (cend - cstart);
63181a97 2851 if (csfrom < 0)
f846bd1a 2852 scm_i_string_set_x (target, p + cstart, scm_i_string_ref (s, (cend - cstart) - t));
63181a97 2853 else
f846bd1a 2854 scm_i_string_set_x (target, p + cstart, scm_i_string_ref (s, t));
63181a97
MV
2855 csfrom++;
2856 p++;
2857 }
2858 scm_i_string_stop_writing ();
2859
2860 scm_remember_upto_here_2 (target, s);
2861 return SCM_UNSPECIFIED;
2862}
2863#undef FUNC_NAME
2864
2865
2866SCM_DEFINE (scm_string_replace, "string-replace", 2, 4, 0,
2867 (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
2868 "Return the string @var{s1}, but with the characters\n"
2869 "@var{start1} @dots{} @var{end1} replaced by the characters\n"
2870 "@var{start2} @dots{} @var{end2} from @var{s2}.")
2871#define FUNC_NAME s_scm_string_replace
2872{
63181a97
MV
2873 size_t cstart1, cend1, cstart2, cend2;
2874 SCM result;
2875
3731fc67
MV
2876 MY_VALIDATE_SUBSTRING_SPEC (1, s1,
2877 3, start1, cstart1,
2878 4, end1, cend1);
2879 MY_VALIDATE_SUBSTRING_SPEC (2, s2,
2880 5, start2, cstart2,
2881 6, end2, cend2);
f846bd1a
MG
2882 return (scm_string_append
2883 (scm_list_3 (scm_i_substring (s1, 0, cstart1),
2884 scm_i_substring (s2, cstart2, cend2),
2885 scm_i_substring (s1, cend1, scm_i_string_length (s1)))));
63181a97
MV
2886 return result;
2887}
2888#undef FUNC_NAME
2889
2890
2891SCM_DEFINE (scm_string_tokenize, "string-tokenize", 1, 3, 0,
2892 (SCM s, SCM token_set, SCM start, SCM end),
2893 "Split the string @var{s} into a list of substrings, where each\n"
2894 "substring is a maximal non-empty contiguous sequence of\n"
2895 "characters from the character set @var{token_set}, which\n"
2896 "defaults to @code{char-set:graphic}.\n"
2897 "If @var{start} or @var{end} indices are provided, they restrict\n"
2898 "@code{string-tokenize} to operating on the indicated substring\n"
2899 "of @var{s}.")
2900#define FUNC_NAME s_scm_string_tokenize
2901{
63181a97
MV
2902 size_t cstart, cend;
2903 SCM result = SCM_EOL;
2904
f846bd1a
MG
2905 MY_VALIDATE_SUBSTRING_SPEC (1, s,
2906 3, start, cstart,
2907 4, end, cend);
63181a97
MV
2908
2909 if (SCM_UNBNDP (token_set))
2910 token_set = scm_char_set_graphic;
2911
2912 if (SCM_CHARSETP (token_set))
2913 {
1a82a460 2914 size_t idx;
63181a97
MV
2915
2916 while (cstart < cend)
2917 {
2918 while (cstart < cend)
2919 {
f846bd1a 2920 if (REF_IN_CHARSET (s, cend-1, token_set))
63181a97
MV
2921 break;
2922 cend--;
2923 }
2924 if (cstart >= cend)
2925 break;
2926 idx = cend;
2927 while (cstart < cend)
2928 {
f846bd1a 2929 if (!REF_IN_CHARSET (s, cend-1, token_set))
63181a97
MV
2930 break;
2931 cend--;
2932 }
f846bd1a 2933 result = scm_cons (scm_i_substring (s, cend, idx), result);
63181a97
MV
2934 }
2935 }
3731fc67
MV
2936 else
2937 SCM_WRONG_TYPE_ARG (2, token_set);
2938
63181a97
MV
2939 scm_remember_upto_here_1 (s);
2940 return result;
2941}
2942#undef FUNC_NAME
2943
2944SCM_DEFINE (scm_string_split, "string-split", 2, 0, 0,
2945 (SCM str, SCM chr),
2946 "Split the string @var{str} into the a list of the substrings delimited\n"
2947 "by appearances of the character @var{chr}. Note that an empty substring\n"
2948 "between separator characters will result in an empty string in the\n"
2949 "result list.\n"
2950 "\n"
2951 "@lisp\n"
2952 "(string-split \"root:x:0:0:root:/root:/bin/bash\" #\\:)\n"
2953 "@result{}\n"
2954 "(\"root\" \"x\" \"0\" \"0\" \"root\" \"/root\" \"/bin/bash\")\n"
2955 "\n"
2956 "(string-split \"::\" #\\:)\n"
2957 "@result{}\n"
2958 "(\"\" \"\" \"\")\n"
2959 "\n"
2960 "(string-split \"\" #\\:)\n"
2961 "@result{}\n"
2962 "(\"\")\n"
2963 "@end lisp")
2964#define FUNC_NAME s_scm_string_split
2965{
2966 long idx, last_idx;
f846bd1a 2967 int narrow;
63181a97
MV
2968 SCM res = SCM_EOL;
2969
2970 SCM_VALIDATE_STRING (1, str);
2971 SCM_VALIDATE_CHAR (2, chr);
f846bd1a
MG
2972
2973 /* This is explicit wide/narrow logic (instead of using
2974 scm_i_string_ref) is a speed optimization. */
63181a97 2975 idx = scm_i_string_length (str);
f846bd1a
MG
2976 narrow = scm_i_is_narrow_string (str);
2977 if (narrow)
2978 {
2979 const char *buf = scm_i_string_chars (str);
2980 while (idx >= 0)
2981 {
2982 last_idx = idx;
2983 while (idx > 0 && buf[idx-1] != (char) SCM_CHAR(chr))
2984 idx--;
2985 if (idx >= 0)
2986 {
2987 res = scm_cons (scm_i_substring (str, idx, last_idx), res);
2988 idx--;
2989 }
2990 }
2991 }
2992 else
2993 {
2994 const scm_t_wchar *buf = scm_i_string_wide_chars (str);
2995 while (idx >= 0)
2996 {
2997 last_idx = idx;
2998 while (idx > 0 && buf[idx-1] != SCM_CHAR(chr))
2999 idx--;
3000 if (idx >= 0)
3001 {
3002 res = scm_cons (scm_i_substring (str, idx, last_idx), res);
3003 idx--;
3004 }
3005 }
63181a97
MV
3006 }
3007 scm_remember_upto_here_1 (str);
3008 return res;
3009}
3010#undef FUNC_NAME
3011
3012
3013SCM_DEFINE (scm_string_filter, "string-filter", 2, 2, 0,
3014 (SCM s, SCM char_pred, SCM start, SCM end),
8753a993 3015 "Filter the string @var{s}, retaining only those characters\n"
85912343 3016 "which satisfy @var{char_pred}.\n"
8753a993
KR
3017 "\n"
3018 "If @var{char_pred} is a procedure, it is applied to each\n"
3019 "character as a predicate, if it is a character, it is tested\n"
3020 "for equality and if it is a character set, it is tested for\n"
3021 "membership.")
63181a97
MV
3022#define FUNC_NAME s_scm_string_filter
3023{
63181a97
MV
3024 size_t cstart, cend;
3025 SCM result;
3026 size_t idx;
3027
f846bd1a
MG
3028 MY_VALIDATE_SUBSTRING_SPEC (1, s,
3029 3, start, cstart,
3030 4, end, cend);
85912343
KR
3031
3032 /* The explicit loops below stripping leading and trailing non-matches
3033 mean we can return a substring if those are the only deletions, making
3034 string-filter as efficient as string-trim-both in that case. */
3035
63181a97
MV
3036 if (SCM_CHARP (char_pred))
3037 {
8753a993 3038 size_t count;
85912343
KR
3039
3040 /* strip leading non-matches by incrementing cstart */
f846bd1a 3041 while (cstart < cend && scm_i_string_ref (s, cstart) != SCM_CHAR (char_pred))
85912343
KR
3042 cstart++;
3043
3044 /* strip trailing non-matches by decrementing cend */
f846bd1a 3045 while (cend > cstart && scm_i_string_ref (s, cend-1) != SCM_CHAR (char_pred))
85912343
KR
3046 cend--;
3047
3048 /* count chars to keep */
8753a993
KR
3049 count = 0;
3050 for (idx = cstart; idx < cend; idx++)
f846bd1a 3051 if (scm_i_string_ref (s, idx) == SCM_CHAR (char_pred))
8753a993
KR
3052 count++;
3053
8753a993 3054 if (count == cend - cstart)
8230ee41 3055 {
85912343
KR
3056 /* whole of cstart to cend is to be kept, return a copy-on-write
3057 substring */
8230ee41
KR
3058 result_substring:
3059 result = scm_i_substring (s, cstart, cend);
3060 }
8753a993
KR
3061 else
3062 result = scm_c_make_string (count, char_pred);
63181a97
MV
3063 }
3064 else if (SCM_CHARSETP (char_pred))
3065 {
8753a993 3066 size_t count;
63181a97 3067
85912343 3068 /* strip leading non-matches by incrementing cstart */
f846bd1a 3069 while (cstart < cend && ! REF_IN_CHARSET (s, cstart, char_pred))
85912343
KR
3070 cstart++;
3071
3072 /* strip trailing non-matches by decrementing cend */
f846bd1a 3073 while (cend > cstart && ! REF_IN_CHARSET (s, cend-1, char_pred))
85912343
KR
3074 cend--;
3075
8753a993
KR
3076 /* count chars to be kept */
3077 count = 0;
3078 for (idx = cstart; idx < cend; idx++)
f846bd1a 3079 if (REF_IN_CHARSET (s, idx, char_pred))
8753a993
KR
3080 count++;
3081
8230ee41 3082 /* if whole of start to end kept then return substring */
8753a993 3083 if (count == cend - cstart)
8230ee41 3084 goto result_substring;
8753a993
KR
3085 else
3086 {
f846bd1a
MG
3087 size_t dst = 0;
3088 result = scm_i_make_string (count, NULL);
3089 result = scm_i_string_start_writing (result);
8753a993
KR
3090
3091 /* decrement "count" in this loop as well as using idx, so that if
3092 another thread is simultaneously changing "s" there's no chance
3093 it'll make us copy more than count characters */
3094 for (idx = cstart; idx < cend && count != 0; idx++)
3095 {
f846bd1a 3096 if (REF_IN_CHARSET (s, idx, char_pred))
8753a993 3097 {
f846bd1a
MG
3098 scm_i_string_set_x (result, dst, scm_i_string_ref (s, idx));
3099 dst ++;
8753a993
KR
3100 count--;
3101 }
3102 }
f846bd1a 3103 scm_i_string_stop_writing ();
8753a993 3104 }
63181a97
MV
3105 }
3106 else
3107 {
3108 SCM ls = SCM_EOL;
3540b915 3109 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
63181a97 3110
3540b915 3111 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
63181a97
MV
3112 idx = cstart;
3113 while (idx < cend)
3114 {
3731fc67 3115 SCM res, ch;
f846bd1a 3116 ch = SCM_MAKE_CHAR (scm_i_string_ref (s, idx));
3540b915 3117 res = pred_tramp (char_pred, ch);
63181a97 3118 if (scm_is_true (res))
3731fc67 3119 ls = scm_cons (ch, ls);
63181a97
MV
3120 idx++;
3121 }
3122 result = scm_reverse_list_to_string (ls);
3123 }
3731fc67 3124
63181a97
MV
3125 scm_remember_upto_here_1 (s);
3126 return result;
3127}
3128#undef FUNC_NAME
3129
3130
3131SCM_DEFINE (scm_string_delete, "string-delete", 2, 2, 0,
3132 (SCM s, SCM char_pred, SCM start, SCM end),
85912343 3133 "Delete characters satisfying @var{char_pred} from @var{s}.\n"
8753a993
KR
3134 "\n"
3135 "If @var{char_pred} is a procedure, it is applied to each\n"
3136 "character as a predicate, if it is a character, it is tested\n"
3137 "for equality and if it is a character set, it is tested for\n"
3138 "membership.")
63181a97
MV
3139#define FUNC_NAME s_scm_string_delete
3140{
63181a97
MV
3141 size_t cstart, cend;
3142 SCM result;
3143 size_t idx;
3144
f846bd1a
MG
3145 MY_VALIDATE_SUBSTRING_SPEC (1, s,
3146 3, start, cstart,
3147 4, end, cend);
85912343
KR
3148
3149 /* The explicit loops below stripping leading and trailing matches mean we
3150 can return a substring if those are the only deletions, making
3151 string-delete as efficient as string-trim-both in that case. */
3152
63181a97
MV
3153 if (SCM_CHARP (char_pred))
3154 {
8753a993 3155 size_t count;
8753a993 3156
85912343 3157 /* strip leading matches by incrementing cstart */
f846bd1a 3158 while (cstart < cend && scm_i_string_ref (s, cstart) == SCM_CHAR(char_pred))
85912343
KR
3159 cstart++;
3160
3161 /* strip trailing matches by decrementing cend */
f846bd1a 3162 while (cend > cstart && scm_i_string_ref (s, cend-1) == SCM_CHAR (char_pred))
85912343
KR
3163 cend--;
3164
8753a993
KR
3165 /* count chars to be kept */
3166 count = 0;
3167 for (idx = cstart; idx < cend; idx++)
f846bd1a 3168 if (scm_i_string_ref (s, idx) != SCM_CHAR (char_pred))
8753a993
KR
3169 count++;
3170
8753a993
KR
3171 if (count == cend - cstart)
3172 {
85912343
KR
3173 /* whole of cstart to cend is to be kept, return a copy-on-write
3174 substring */
8753a993 3175 result_substring:
8230ee41 3176 result = scm_i_substring (s, cstart, cend);
8753a993
KR
3177 }
3178 else
3179 {
f846bd1a 3180 int i = 0;
8753a993 3181 /* new string for retained portion */
f846bd1a
MG
3182 result = scm_i_make_string (count, NULL);
3183 result = scm_i_string_start_writing (result);
8753a993
KR
3184 /* decrement "count" in this loop as well as using idx, so that if
3185 another thread is simultaneously changing "s" there's no chance
3186 it'll make us copy more than count characters */
3187 for (idx = cstart; idx < cend && count != 0; idx++)
3188 {
f846bd1a
MG
3189 scm_t_wchar c = scm_i_string_ref (s, idx);
3190 if (c != SCM_CHAR (char_pred))
8753a993 3191 {
f846bd1a
MG
3192 scm_i_string_set_x (result, i, c);
3193 i++;
8753a993
KR
3194 count--;
3195 }
3196 }
f846bd1a 3197 scm_i_string_stop_writing ();
8753a993 3198 }
63181a97
MV
3199 }
3200 else if (SCM_CHARSETP (char_pred))
3201 {
8753a993 3202 size_t count;
63181a97 3203
85912343 3204 /* strip leading matches by incrementing cstart */
f846bd1a 3205 while (cstart < cend && REF_IN_CHARSET (s, cstart, char_pred))
85912343
KR
3206 cstart++;
3207
3208 /* strip trailing matches by decrementing cend */
f846bd1a 3209 while (cend > cstart && REF_IN_CHARSET (s, cend-1, char_pred))
85912343
KR
3210 cend--;
3211
8753a993
KR
3212 /* count chars to be kept */
3213 count = 0;
3214 for (idx = cstart; idx < cend; idx++)
f846bd1a 3215 if (!REF_IN_CHARSET (s, idx, char_pred))
8753a993
KR
3216 count++;
3217
8753a993
KR
3218 if (count == cend - cstart)
3219 goto result_substring;
3220 else
3221 {
f846bd1a 3222 size_t i = 0;
8753a993 3223 /* new string for retained portion */
f846bd1a
MG
3224 result = scm_i_make_string (count, NULL);
3225 result = scm_i_string_start_writing (result);
8753a993
KR
3226
3227 /* decrement "count" in this loop as well as using idx, so that if
3228 another thread is simultaneously changing "s" there's no chance
3229 it'll make us copy more than count characters */
3230 for (idx = cstart; idx < cend && count != 0; idx++)
3231 {
f846bd1a 3232 if (!REF_IN_CHARSET (s, idx, char_pred))
8753a993 3233 {
f846bd1a
MG
3234 scm_i_string_set_x (result, i, scm_i_string_ref (s, idx));
3235 i++;
8753a993
KR
3236 count--;
3237 }
3238 }
f846bd1a 3239 scm_i_string_stop_writing ();
8753a993 3240 }
63181a97
MV
3241 }
3242 else
3243 {
3244 SCM ls = SCM_EOL;
3540b915
KR
3245 scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
3246 SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
63181a97 3247
63181a97
MV
3248 idx = cstart;
3249 while (idx < cend)
3250 {
f846bd1a 3251 SCM res, ch = SCM_MAKE_CHAR (scm_i_string_ref (s, idx));
3540b915 3252 res = pred_tramp (char_pred, ch);
63181a97 3253 if (scm_is_false (res))
3731fc67 3254 ls = scm_cons (ch, ls);
63181a97
MV
3255 idx++;
3256 }
3257 result = scm_reverse_list_to_string (ls);
3258 }
3731fc67
MV
3259
3260 scm_remember_upto_here_1 (s);
63181a97
MV
3261 return result;
3262}
3263#undef FUNC_NAME
3264
63181a97
MV
3265void
3266scm_init_srfi_13 (void)
3267{
3268#include "libguile/srfi-13.x"
3269}
3270
3271/* End of srfi-13.c. */