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