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