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