]> git.donarmstrong.com Git - lilypond.git/blobdiff - guile18/libguile/srfi-13.c
Import guile-1.8 as multiple upstream tarball component
[lilypond.git] / guile18 / libguile / srfi-13.c
diff --git a/guile18/libguile/srfi-13.c b/guile18/libguile/srfi-13.c
new file mode 100644 (file)
index 0000000..c8ca780
--- /dev/null
@@ -0,0 +1,3585 @@
+/* srfi-13.c --- SRFI-13 procedures for Guile
+ *
+ * Copyright (C) 2001, 2004, 2005, 2006, 2008 Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <ctype.h>
+
+#include "libguile.h"
+
+#include "libguile/srfi-13.h"
+#include "libguile/srfi-14.h"
+
+/* SCM_VALIDATE_SUBSTRING_SPEC_COPY is deprecated since it encourages
+   messing with the internal representation of strings.  We define our
+   own version since we use it so much and are messing with Guile
+   internals anyway.
+*/
+
+#define MY_VALIDATE_SUBSTRING_SPEC_COPY(pos_str, str, c_str,        \
+                                        pos_start, start, c_start,  \
+                                        pos_end, end, c_end)        \
+  do {                                                              \
+    SCM_VALIDATE_STRING (pos_str, str);                             \
+    c_str = scm_i_string_chars (str);                               \
+    scm_i_get_substring_spec (scm_i_string_length (str),            \
+                             start, &c_start, end, &c_end);        \
+  } while (0)
+
+/* Expecting "unsigned char *c_str" */
+#define MY_VALIDATE_SUBSTRING_SPEC_UCOPY(pos_str, str, c_str,           \
+                                         pos_start, start, c_start,     \
+                                         pos_end, end, c_end)           \
+  do {                                                                  \
+    const char *signed_c_str;                                           \
+    MY_VALIDATE_SUBSTRING_SPEC_COPY(pos_str, str, signed_c_str,         \
+                                    pos_start, start, c_start,          \
+                                    pos_end, end, c_end);               \
+    c_str = (unsigned char *) signed_c_str;                             \
+  } while (0)
+
+#define MY_VALIDATE_SUBSTRING_SPEC(pos_str, str,              \
+                                   pos_start, start, c_start, \
+                                   pos_end, end, c_end)       \
+  do {                                                        \
+    SCM_VALIDATE_STRING (pos_str, str);                       \
+    scm_i_get_substring_spec (scm_i_string_length (str),      \
+                             start, &c_start, end, &c_end);  \
+  } while (0)
+
+SCM_DEFINE (scm_string_null_p, "string-null?", 1, 0, 0,
+           (SCM str),
+           "Return @code{#t} if @var{str}'s length is zero, and\n"
+           "@code{#f} otherwise.\n"
+           "@lisp\n"
+           "(string-null? \"\")  @result{} #t\n"
+           "y                    @result{} \"foo\"\n"
+           "(string-null? y)     @result{} #f\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_string_null_p
+{
+  SCM_VALIDATE_STRING (1, str);
+  return scm_from_bool (scm_i_string_length (str) == 0);
+}
+#undef FUNC_NAME
+
+#if 0
+static void
+race_error ()
+{
+  scm_misc_error (NULL, "race condition detected", SCM_EOL);
+}
+#endif
+
+SCM_DEFINE (scm_string_any, "string-any-c-code", 2, 2, 0,
+            (SCM char_pred, SCM s, SCM start, SCM end),
+"Check if @var{char_pred} is true for any character in string @var{s}.\n"
+"\n"
+"@var{char_pred} can be a character to check for any equal to that, or\n"
+"a character set (@pxref{Character Sets}) to check for any in that set,\n"
+"or a predicate procedure to call.\n"
+"\n"
+"For a procedure, calls @code{(@var{char_pred} c)} are made\n"
+"successively on the characters from @var{start} to @var{end}.  If\n"
+"@var{char_pred} returns true (ie.@: non-@code{#f}), @code{string-any}\n"
+"stops and that return value is the return from @code{string-any}.  The\n"
+"call on the last character (ie.@: at @math{@var{end}-1}), if that\n"
+"point is reached, is a tail call.\n"
+"\n"
+"If there are no characters in @var{s} (ie.@: @var{start} equals\n"
+"@var{end}) then the return is @code{#f}.\n")
+#define FUNC_NAME s_scm_string_any
+{
+  const char *cstr;
+  size_t cstart, cend;
+  SCM res = SCM_BOOL_F;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+
+  if (SCM_CHARP (char_pred))
+    {
+      res = (memchr (cstr+cstart, (int) SCM_CHAR (char_pred),
+                    cend-cstart) == NULL
+            ? SCM_BOOL_F : SCM_BOOL_T);
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      size_t i;
+      for (i = cstart; i < cend; i++)
+        if (SCM_CHARSET_GET (char_pred, cstr[i]))
+         {
+           res = SCM_BOOL_T;
+           break;
+         }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG1, FUNC_NAME);
+
+      while (cstart < cend)
+        {
+          res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cstart]));
+          if (scm_is_true (res))
+            break;
+         cstr = scm_i_string_chars (s);
+          cstart++;
+        }
+    }
+
+  scm_remember_upto_here_1 (s);
+  return res;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_every, "string-every-c-code", 2, 2, 0,
+            (SCM char_pred, SCM s, SCM start, SCM end),
+"Check if @var{char_pred} is true for every character in string\n"
+"@var{s}.\n"
+"\n"
+"@var{char_pred} can be a character to check for every character equal\n"
+"to that, or a character set (@pxref{Character Sets}) to check for\n"
+"every character being in that set, or a predicate procedure to call.\n"
+"\n"
+"For a procedure, calls @code{(@var{char_pred} c)} are made\n"
+"successively on the characters from @var{start} to @var{end}.  If\n"
+"@var{char_pred} returns @code{#f}, @code{string-every} stops and\n"
+"returns @code{#f}.  The call on the last character (ie.@: at\n"
+"@math{@var{end}-1}), if that point is reached, is a tail call and the\n"
+"return from that call is the return from @code{string-every}.\n"
+"\n"
+"If there are no characters in @var{s} (ie.@: @var{start} equals\n"
+"@var{end}) then the return is @code{#t}.\n")
+#define FUNC_NAME s_scm_string_every
+{
+  const char *cstr;
+  size_t cstart, cend;
+  SCM res = SCM_BOOL_T;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_CHARP (char_pred))
+    {
+      char cchr = SCM_CHAR (char_pred);
+      size_t i;
+      for (i = cstart; i < cend; i++)
+        if (cstr[i] != cchr)
+         {
+           res = SCM_BOOL_F;
+           break;
+         }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      size_t i;
+      for (i = cstart; i < cend; i++)
+        if (!SCM_CHARSET_GET (char_pred, cstr[i]))
+         {
+           res = SCM_BOOL_F;
+           break;
+         }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG1, FUNC_NAME);
+
+      while (cstart < cend)
+        {
+          res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cstart]));
+          if (scm_is_false (res))
+            break;
+          cstr = scm_i_string_chars (s);
+          cstart++;
+        }
+    }
+
+  scm_remember_upto_here_1 (s);
+  return res;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_tabulate, "string-tabulate", 2, 0, 0,
+            (SCM proc, SCM len),
+           "@var{proc} is an integer->char procedure.  Construct a string\n"
+           "of size @var{len} by applying @var{proc} to each index to\n"
+           "produce the corresponding string element.  The order in which\n"
+           "@var{proc} is applied to the indices is not specified.")
+#define FUNC_NAME s_scm_string_tabulate
+{
+  size_t clen, i;
+  SCM res;
+  SCM ch;
+  char *p;
+  scm_t_trampoline_1 proc_tramp;
+
+  proc_tramp = scm_trampoline_1 (proc);
+  SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
+
+  clen = scm_to_size_t (len);
+  SCM_ASSERT_RANGE (2, len, clen >= 0);
+
+  res = scm_i_make_string (clen, &p);
+  i = 0;
+  while (i < clen)
+    {
+      /* The RES string remains untouched since nobody knows about it
+        yet. No need to refetch P.
+      */
+      ch = proc_tramp (proc, scm_from_size_t (i));
+      if (!SCM_CHARP (ch))
+       SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (proc));
+      *p++ = SCM_CHAR (ch);
+      i++;
+    }
+  return res;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_substring_to_list, "string->list", 1, 2, 0,
+           (SCM str, SCM start, SCM end),
+           "Convert the string @var{str} into a list of characters.")
+#define FUNC_NAME s_scm_substring_to_list
+{
+  const char *cstr;
+  size_t cstart, cend;
+  SCM result = SCM_EOL;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  while (cstart < cend)
+    {
+      cend--;
+      result = scm_cons (SCM_MAKE_CHAR (cstr[cend]), result);
+      cstr = scm_i_string_chars (str);
+    }
+  scm_remember_upto_here_1 (str);
+  return result;
+}
+#undef FUNC_NAME
+
+/* We export scm_substring_to_list as "string->list" since it is
+   compatible and more general.  This function remains for the benefit
+   of C code that used it.
+*/
+
+SCM
+scm_string_to_list (SCM str)
+{
+  return scm_substring_to_list (str, SCM_UNDEFINED, SCM_UNDEFINED);
+}
+
+SCM_DEFINE (scm_reverse_list_to_string, "reverse-list->string", 1, 0, 0,
+            (SCM chrs),
+           "An efficient implementation of @code{(compose string->list\n"
+           "reverse)}:\n"
+           "\n"
+           "@smalllisp\n"
+           "(reverse-list->string '(#\\a #\\B #\\c)) @result{} \"cBa\"\n"
+           "@end smalllisp")
+#define FUNC_NAME s_scm_reverse_list_to_string
+{
+  SCM result;
+  long i = scm_ilength (chrs);
+  char *data;
+
+  if (i < 0)
+    SCM_WRONG_TYPE_ARG (1, chrs);
+  result = scm_i_make_string (i, &data);
+
+  {
+    
+    data += i;
+    while (i > 0 && scm_is_pair (chrs))
+      {
+       SCM elt = SCM_CAR (chrs);
+
+       SCM_VALIDATE_CHAR (SCM_ARGn, elt);
+       data--;
+       *data = SCM_CHAR (elt);
+       chrs = SCM_CDR (chrs);
+       i--;
+      }
+  }
+
+  return result;
+}
+#undef FUNC_NAME
+
+
+SCM_SYMBOL (scm_sym_infix, "infix");
+SCM_SYMBOL (scm_sym_strict_infix, "strict-infix");
+SCM_SYMBOL (scm_sym_suffix, "suffix");
+SCM_SYMBOL (scm_sym_prefix, "prefix");
+
+static void
+append_string (char **sp, size_t *lp, SCM str)
+{
+  size_t len;
+  len = scm_c_string_length (str);
+  if (len > *lp)
+    len = *lp;
+  memcpy (*sp, scm_i_string_chars (str), len);
+  *lp -= len;
+  *sp += len;
+}
+
+SCM_DEFINE (scm_string_join, "string-join", 1, 2, 0,
+            (SCM ls, SCM delimiter, SCM grammar),
+           "Append the string in the string list @var{ls}, using the string\n"
+           "@var{delim} as a delimiter between the elements of @var{ls}.\n"
+           "@var{grammar} is a symbol which specifies how the delimiter is\n"
+           "placed between the strings, and defaults to the symbol\n"
+           "@code{infix}.\n"
+           "\n"
+           "@table @code\n"
+           "@item infix\n"
+           "Insert the separator between list elements.  An empty string\n"
+           "will produce an empty list.\n"
+           "@item string-infix\n"
+           "Like @code{infix}, but will raise an error if given the empty\n"
+           "list.\n"
+           "@item suffix\n"
+           "Insert the separator after every list element.\n"
+           "@item prefix\n"
+           "Insert the separator before each list element.\n"
+           "@end table")
+#define FUNC_NAME s_scm_string_join
+{
+#define GRAM_INFIX        0
+#define GRAM_STRICT_INFIX 1
+#define GRAM_SUFFIX       2
+#define GRAM_PREFIX       3
+  SCM tmp;
+  SCM result;
+  int gram = GRAM_INFIX;
+  size_t del_len = 0;
+  size_t len = 0;
+  char *p;
+  long strings = scm_ilength (ls);
+
+  /* Validate the string list.  */
+  if (strings < 0)
+    SCM_WRONG_TYPE_ARG (1, ls);
+
+  /* Validate the delimiter and record its length.  */
+  if (SCM_UNBNDP (delimiter))
+    {
+      delimiter = scm_from_locale_string (" ");
+      del_len = 1;
+    }
+  else
+    del_len = scm_c_string_length (delimiter);
+
+  /* Validate the grammar symbol and remember the grammar.  */
+  if (SCM_UNBNDP (grammar))
+    gram = GRAM_INFIX;
+  else if (scm_is_eq (grammar, scm_sym_infix))
+    gram = GRAM_INFIX;
+  else if (scm_is_eq (grammar, scm_sym_strict_infix))
+    gram = GRAM_STRICT_INFIX;
+  else if (scm_is_eq (grammar, scm_sym_suffix))
+    gram = GRAM_SUFFIX;
+  else if (scm_is_eq (grammar, scm_sym_prefix))
+    gram = GRAM_PREFIX;
+  else
+    SCM_WRONG_TYPE_ARG (3, grammar);
+
+  /* Check grammar constraints and calculate the space required for
+     the delimiter(s).  */
+  switch (gram)
+    {
+    case GRAM_INFIX:
+      if (!scm_is_null (ls))
+       len = (strings > 0) ? ((strings - 1) * del_len) : 0;
+      break;
+    case GRAM_STRICT_INFIX:
+      if (strings == 0)
+       SCM_MISC_ERROR ("strict-infix grammar requires non-empty list",
+                       SCM_EOL);
+      len = (strings - 1) * del_len;
+      break;
+    default:
+      len = strings * del_len;
+      break;
+    }
+
+  tmp = ls;
+  while (scm_is_pair (tmp))
+    {
+      len += scm_c_string_length (SCM_CAR (tmp));
+      tmp = SCM_CDR (tmp);
+    }
+
+  result = scm_i_make_string (len, &p);
+
+  tmp = ls;
+  switch (gram)
+    {
+    case GRAM_INFIX:
+    case GRAM_STRICT_INFIX:
+      while (scm_is_pair (tmp))
+       {
+         append_string (&p, &len, SCM_CAR (tmp));
+         if (!scm_is_null (SCM_CDR (tmp)) && del_len > 0)
+           append_string (&p, &len, delimiter);
+         tmp = SCM_CDR (tmp);
+       }
+      break;
+    case GRAM_SUFFIX:
+      while (scm_is_pair (tmp))
+       {
+         append_string (&p, &len, SCM_CAR (tmp));
+         if (del_len > 0)
+           append_string (&p, &len, delimiter);
+         tmp = SCM_CDR (tmp);
+       }
+      break;
+    case GRAM_PREFIX:
+      while (scm_is_pair (tmp))
+       {
+         if (del_len > 0)
+           append_string (&p, &len, delimiter);
+         append_string (&p, &len, SCM_CAR (tmp));
+         tmp = SCM_CDR (tmp);
+       }
+      break;
+    }
+
+  return result;
+#undef GRAM_INFIX
+#undef GRAM_STRICT_INFIX
+#undef GRAM_SUFFIX
+#undef GRAM_PREFIX
+}
+#undef FUNC_NAME
+
+
+/* There are a number of functions to consider here for Scheme and C:
+
+   string-copy STR [start [end]]    ;; SRFI-13 variant of R5RS string-copy
+   substring/copy STR start [end]   ;; Guile variant of R5RS substring
+
+   scm_string_copy (str)            ;; Old function from Guile
+   scm_substring_copy (str, [start, [end]])
+                                    ;; C version of SRFI-13 string-copy
+                                    ;; and C version of substring/copy
+
+   The C function underlying string-copy is not exported to C
+   programs.  scm_substring_copy is defined in strings.c as the
+   underlying function of substring/copy and allows an optional START
+   argument.
+*/
+
+SCM scm_srfi13_substring_copy (SCM str, SCM start, SCM end);
+
+SCM_DEFINE (scm_srfi13_substring_copy, "string-copy", 1, 2, 0,
+           (SCM str, SCM start, SCM end),
+           "Return a freshly allocated copy of the string @var{str}.  If\n"
+           "given, @var{start} and @var{end} delimit the portion of\n"
+           "@var{str} which is copied.")
+#define FUNC_NAME s_scm_srfi13_substring_copy
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  return scm_c_substring_copy (str, cstart, cend);
+}
+#undef FUNC_NAME
+
+SCM 
+scm_string_copy (SCM str)
+{
+  return scm_c_substring (str, 0, scm_c_string_length (str));
+}
+
+SCM_DEFINE (scm_string_copy_x, "string-copy!", 3, 2, 0,
+            (SCM target, SCM tstart, SCM s, SCM start, SCM end),
+           "Copy the sequence of characters from index range [@var{start},\n"
+           "@var{end}) in string @var{s} to string @var{target}, beginning\n"
+           "at index @var{tstart}.  The characters are copied left-to-right\n"
+           "or right-to-left as needed -- the copy is guaranteed to work,\n"
+           "even if @var{target} and @var{s} are the same string.  It is an\n"
+           "error if the copy operation runs off the end of the target\n"
+           "string.")
+#define FUNC_NAME s_scm_string_copy_x
+{
+  const char *cstr;
+  char *ctarget;
+  size_t cstart, cend, ctstart, dummy, len;
+  SCM sdummy = SCM_UNDEFINED;
+
+  MY_VALIDATE_SUBSTRING_SPEC (1, target,
+                             2, tstart, ctstart,
+                             2, sdummy, dummy);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (3, s, cstr,
+                                  4, start, cstart,
+                                  5, end, cend);
+  len = cend - cstart;
+  SCM_ASSERT_RANGE (3, s, len <= scm_i_string_length (target) - ctstart);
+
+  ctarget = scm_i_string_writable_chars (target);
+  memmove (ctarget + ctstart, cstr + cstart, len);
+  scm_i_string_stop_writing ();
+  scm_remember_upto_here_1 (target);
+
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_substring_move_x, "substring-move!", 5, 0, 0,
+           (SCM str1, SCM start1, SCM end1, SCM str2, SCM start2),
+           "Copy the substring of @var{str1} bounded by @var{start1} and @var{end1}\n"
+           "into @var{str2} beginning at position @var{start2}.\n"
+           "@var{str1} and @var{str2} can be the same string.")
+#define FUNC_NAME s_scm_substring_move_x
+{
+  return scm_string_copy_x (str2, start2, str1, start1, end1);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_take, "string-take", 2, 0, 0,
+            (SCM s, SCM n),
+           "Return the @var{n} first characters of @var{s}.")
+#define FUNC_NAME s_scm_string_take
+{
+  return scm_substring (s, SCM_INUM0, n);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_drop, "string-drop", 2, 0, 0,
+            (SCM s, SCM n),
+           "Return all but the first @var{n} characters of @var{s}.")
+#define FUNC_NAME s_scm_string_drop
+{
+  return scm_substring (s, n, SCM_UNDEFINED);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_take_right, "string-take-right", 2, 0, 0,
+            (SCM s, SCM n),
+           "Return the @var{n} last characters of @var{s}.")
+#define FUNC_NAME s_scm_string_take_right
+{
+  return scm_substring (s,
+                       scm_difference (scm_string_length (s), n),
+                       SCM_UNDEFINED);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_drop_right, "string-drop-right", 2, 0, 0,
+            (SCM s, SCM n),
+           "Return all but the last @var{n} characters of @var{s}.")
+#define FUNC_NAME s_scm_string_drop_right
+{
+  return scm_substring (s,
+                       SCM_INUM0,
+                       scm_difference (scm_string_length (s), n));
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_pad, "string-pad", 2, 3, 0,
+           (SCM s, SCM len, SCM chr, SCM start, SCM end),
+           "Take that characters from @var{start} to @var{end} from the\n"
+           "string @var{s} and return a new string, right-padded by the\n"
+           "character @var{chr} to length @var{len}.  If the resulting\n"
+           "string is longer than @var{len}, it is truncated on the right.")
+#define FUNC_NAME s_scm_string_pad
+{
+  char cchr;
+  size_t cstart, cend, clen;
+
+  MY_VALIDATE_SUBSTRING_SPEC (1, s,
+                             4, start, cstart,
+                             5, end, cend);
+  clen = scm_to_size_t (len);
+
+  if (SCM_UNBNDP (chr))
+    cchr = ' ';
+  else
+    {
+      SCM_VALIDATE_CHAR (3, chr);
+      cchr = SCM_CHAR (chr);
+    }
+  if (clen < (cend - cstart))
+    return scm_c_substring (s, cend - clen, cend);
+  else
+    {
+      SCM result;
+      char *dst;
+
+      result = scm_i_make_string (clen, &dst);
+      memset (dst, cchr, (clen - (cend - cstart)));
+      memmove (dst + clen - (cend - cstart),
+              scm_i_string_chars (s) + cstart, cend - cstart);
+      return result;
+    }
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_pad_right, "string-pad-right", 2, 3, 0,
+           (SCM s, SCM len, SCM chr, SCM start, SCM end),
+           "Take that characters from @var{start} to @var{end} from the\n"
+           "string @var{s} and return a new string, left-padded by the\n"
+           "character @var{chr} to length @var{len}.  If the resulting\n"
+           "string is longer than @var{len}, it is truncated on the left.")
+#define FUNC_NAME s_scm_string_pad_right
+{
+  char cchr;
+  size_t cstart, cend, clen;
+
+  MY_VALIDATE_SUBSTRING_SPEC (1, s,
+                             4, start, cstart,
+                             5, end, cend);
+  clen = scm_to_size_t (len);
+
+  if (SCM_UNBNDP (chr))
+    cchr = ' ';
+  else
+    {
+      SCM_VALIDATE_CHAR (3, chr);
+      cchr = SCM_CHAR (chr);
+    }
+  if (clen < (cend - cstart))
+    return scm_c_substring (s, cstart, cstart + clen);
+  else
+    {
+      SCM result;
+      char *dst;
+
+      result = scm_i_make_string (clen, &dst);
+      memset (dst + (cend - cstart), cchr, clen - (cend - cstart));
+      memmove (dst, scm_i_string_chars (s) + cstart, cend - cstart);
+      return result;
+    }
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_trim, "string-trim", 1, 3, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Trim @var{s} by skipping over all characters on the left\n"
+           "that satisfy the parameter @var{char_pred}:\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "if it is the character @var{ch}, characters equal to\n"
+           "@var{ch} are trimmed,\n"
+           "\n"
+           "@item\n"
+           "if it is a procedure @var{pred} characters that\n"
+           "satisfy @var{pred} are trimmed,\n"
+           "\n"
+           "@item\n"
+           "if it is a character set, characters in that set are trimmed.\n"
+           "@end itemize\n"
+           "\n"
+           "If called without a @var{char_pred} argument, all whitespace is\n"
+           "trimmed.")
+#define FUNC_NAME s_scm_string_trim
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_UNBNDP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (!isspace((int) (unsigned char) cstr[cstart]))
+           break;
+         cstart++;
+       }
+    }
+  else if (SCM_CHARP (char_pred))
+    {
+      char chr = SCM_CHAR (char_pred);
+      while (cstart < cend)
+       {
+         if (chr != cstr[cstart])
+           break;
+         cstart++;
+       }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (!SCM_CHARSET_GET (char_pred, cstr[cstart]))
+           break;
+         cstart++;
+       }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      while (cstart < cend)
+       {
+         SCM res;
+
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cstart]));
+         if (scm_is_false (res))
+           break;
+         cstr = scm_i_string_chars (s);
+         cstart++;
+       }
+    }
+  return scm_c_substring (s, cstart, cend);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_trim_right, "string-trim-right", 1, 3, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Trim @var{s} by skipping over all characters on the rightt\n"
+           "that satisfy the parameter @var{char_pred}:\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "if it is the character @var{ch}, characters equal to @var{ch}\n"
+           "are trimmed,\n"
+           "\n"
+           "@item\n"
+           "if it is a procedure @var{pred} characters that satisfy\n"
+           "@var{pred} are trimmed,\n"
+           "\n"
+           "@item\n"
+           "if it is a character sets, all characters in that set are\n"
+           "trimmed.\n"
+           "@end itemize\n"
+           "\n"
+           "If called without a @var{char_pred} argument, all whitespace is\n"
+           "trimmed.")
+#define FUNC_NAME s_scm_string_trim_right
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_UNBNDP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (!isspace((int) (unsigned char) cstr[cend - 1]))
+           break;
+         cend--;
+       }
+    }
+  else if (SCM_CHARP (char_pred))
+    {
+      char chr = SCM_CHAR (char_pred);
+      while (cstart < cend)
+       {
+         if (chr != cstr[cend - 1])
+           break;
+         cend--;
+       }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (!SCM_CHARSET_GET (char_pred, cstr[cend - 1]))
+           break;
+         cend--;
+       }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      while (cstart < cend)
+       {
+         SCM res;
+
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cend - 1]));
+         if (scm_is_false (res))
+           break;
+         cstr = scm_i_string_chars (s);
+         cend--;
+       }
+    }
+  return scm_c_substring (s, cstart, cend);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_trim_both, "string-trim-both", 1, 3, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Trim @var{s} by skipping over all characters on both sides of\n"
+           "the string that satisfy the parameter @var{char_pred}:\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "if it is the character @var{ch}, characters equal to @var{ch}\n"
+           "are trimmed,\n"
+           "\n"
+           "@item\n"
+           "if it is a procedure @var{pred} characters that satisfy\n"
+           "@var{pred} are trimmed,\n"
+           "\n"
+           "@item\n"
+           "if it is a character set, the characters in the set are\n"
+           "trimmed.\n"
+           "@end itemize\n"
+           "\n"
+           "If called without a @var{char_pred} argument, all whitespace is\n"
+           "trimmed.")
+#define FUNC_NAME s_scm_string_trim_both
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_UNBNDP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (!isspace((int) (unsigned char) cstr[cstart]))
+           break;
+         cstart++;
+       }
+      while (cstart < cend)
+       {
+         if (!isspace((int) (unsigned char) cstr[cend - 1]))
+           break;
+         cend--;
+       }
+    }
+  else if (SCM_CHARP (char_pred))
+    {
+      char chr = SCM_CHAR (char_pred);
+      while (cstart < cend)
+       {
+         if (chr != cstr[cstart])
+           break;
+         cstart++;
+       }
+      while (cstart < cend)
+       {
+         if (chr != cstr[cend - 1])
+           break;
+         cend--;
+       }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (!SCM_CHARSET_GET (char_pred, cstr[cstart]))
+           break;
+         cstart++;
+       }
+      while (cstart < cend)
+       {
+         if (!SCM_CHARSET_GET (char_pred, cstr[cend - 1]))
+           break;
+         cend--;
+       }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      while (cstart < cend)
+       {
+         SCM res;
+
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cstart]));
+         if (scm_is_false (res))
+           break;
+         cstr = scm_i_string_chars (s);
+         cstart++;
+       }
+      while (cstart < cend)
+       {
+         SCM res;
+
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cend - 1]));
+         if (scm_is_false (res))
+           break;
+         cstr = scm_i_string_chars (s);
+         cend--;
+       }
+    }
+  return scm_c_substring (s, cstart, cend);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_substring_fill_x, "string-fill!", 2, 2, 0,
+           (SCM str, SCM chr, SCM start, SCM end),
+           "Stores @var{chr} in every element of the given @var{str} and\n"
+           "returns an unspecified value.")
+#define FUNC_NAME s_scm_substring_fill_x
+{
+  char *cstr;
+  size_t cstart, cend;
+  int c;
+  size_t k;
+
+  /* Older versions of Guile provided the function
+     scm_substring_fill_x with the following order of arguments:
+
+         str, start, end, chr
+
+     We accomodate this here by detecting such a usage and reordering
+     the arguments.
+  */
+  if (SCM_CHARP (end))
+    {
+      SCM tmp = end;
+      end = start;
+      start = chr;
+      chr = tmp;
+    }
+
+  MY_VALIDATE_SUBSTRING_SPEC (1, str,
+                             3, start, cstart,
+                             4, end, cend);
+  SCM_VALIDATE_CHAR_COPY (2, chr, c);
+
+  cstr = scm_i_string_writable_chars (str);
+  for (k = cstart; k < cend; k++)
+    cstr[k] = c;
+  scm_i_string_stop_writing ();
+  scm_remember_upto_here_1 (str);
+
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+SCM
+scm_string_fill_x (SCM str, SCM chr)
+{
+  return scm_substring_fill_x (str, chr, SCM_UNDEFINED, SCM_UNDEFINED);
+}
+
+SCM_DEFINE (scm_string_compare, "string-compare", 5, 4, 0,
+           (SCM s1, SCM s2, SCM proc_lt, SCM proc_eq, SCM proc_gt, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Apply @var{proc_lt}, @var{proc_eq}, @var{proc_gt} to the\n"
+           "mismatch index, depending upon whether @var{s1} is less than,\n"
+           "equal to, or greater than @var{s2}.  The mismatch index is the\n"
+           "largest index @var{i} such that for every 0 <= @var{j} <\n"
+           "@var{i}, @var{s1}[@var{j}] = @var{s2}[@var{j}] -- that is,\n"
+           "@var{i} is the first position that does not match.")
+#define FUNC_NAME s_scm_string_compare
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  SCM proc;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    6, start1, cstart1,
+                                    7, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    8, start2, cstart2,
+                                    9, end2, cend2);
+  SCM_VALIDATE_PROC (3, proc_lt);
+  SCM_VALIDATE_PROC (4, proc_eq);
+  SCM_VALIDATE_PROC (5, proc_gt);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (cstr1[cstart1] < cstr2[cstart2])
+       {
+         proc = proc_lt;
+         goto ret;
+       }
+      else if (cstr1[cstart1] > cstr2[cstart2])
+       {
+         proc = proc_gt;
+         goto ret;
+       }
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    proc = proc_gt;
+  else if (cstart2 < cend2)
+    proc = proc_lt;
+  else
+    proc = proc_eq;
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_call_1 (proc, scm_from_size_t (cstart1));
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_compare_ci, "string-compare-ci", 5, 4, 0,
+           (SCM s1, SCM s2, SCM proc_lt, SCM proc_eq, SCM proc_gt, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Apply @var{proc_lt}, @var{proc_eq}, @var{proc_gt} to the\n"
+           "mismatch index, depending upon whether @var{s1} is less than,\n"
+           "equal to, or greater than @var{s2}.  The mismatch index is the\n"
+           "largest index @var{i} such that for every 0 <= @var{j} <\n"
+           "@var{i}, @var{s1}[@var{j}] = @var{s2}[@var{j}] -- that is,\n"
+           "@var{i} is the first position that does not match.  The\n"
+           "character comparison is done case-insensitively.")
+#define FUNC_NAME s_scm_string_compare_ci
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  SCM proc;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    6, start1, cstart1,
+                                    7, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    8, start2, cstart2,
+                                    9, end2, cend2);
+  SCM_VALIDATE_PROC (3, proc_lt);
+  SCM_VALIDATE_PROC (4, proc_eq);
+  SCM_VALIDATE_PROC (5, proc_gt);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) < scm_c_downcase (cstr2[cstart2]))
+       {
+         proc = proc_lt;
+         goto ret;
+       }
+      else if (scm_c_downcase (cstr1[cstart1]) 
+              > scm_c_downcase (cstr2[cstart2]))
+       {
+         proc = proc_gt;
+         goto ret;
+       }
+      cstart1++;
+      cstart2++;
+    }
+
+  if (cstart1 < cend1)
+    proc = proc_gt;
+  else if (cstart2 < cend2)
+    proc = proc_lt;
+  else
+    proc = proc_eq;
+
+ ret:
+  scm_remember_upto_here (s1, s2);
+  return scm_call_1 (proc, scm_from_size_t (cstart1));
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_eq, "string=", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} and @var{s2} are not equal, a true\n"
+           "value otherwise.")
+#define FUNC_NAME s_scm_string_eq
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+
+  if ((cend1 - cstart1) != (cend2 - cstart2))
+    goto false;
+
+  while (cstart1 < cend1)
+    {
+      if (cstr1[cstart1] < cstr2[cstart2])
+       goto false;
+      else if (cstr1[cstart1] > cstr2[cstart2])
+       goto false;
+      cstart1++;
+      cstart2++;
+    }
+  
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_neq, "string<>", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} and @var{s2} are equal, a true\n"
+           "value otherwise.")
+#define FUNC_NAME s_scm_string_neq
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (cstr1[cstart1] < cstr2[cstart2])
+       goto true;
+      else if (cstr1[cstart1] > cstr2[cstart2])
+       goto true;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto true;
+  else if (cstart2 < cend2)
+    goto true;
+  else
+    goto false;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_lt, "string<", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} is greater or equal to @var{s2}, a\n"
+           "true value otherwise.")
+#define FUNC_NAME s_scm_string_lt
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    3, start1, cstart1,
+                                    4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    5, start2, cstart2,
+                                    6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (cstr1[cstart1] < cstr2[cstart2])
+       goto true;
+      else if (cstr1[cstart1] > cstr2[cstart2])
+       goto false;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto false;
+  else if (cstart2 < cend2)
+    goto true;
+  else
+    goto false;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_gt, "string>", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} is less or equal to @var{s2}, a\n"
+           "true value otherwise.")
+#define FUNC_NAME s_scm_string_gt
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    3, start1, cstart1,
+                                    4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    5, start2, cstart2,
+                                    6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (cstr1[cstart1] < cstr2[cstart2])
+       goto false;
+      else if (cstr1[cstart1] > cstr2[cstart2])
+       goto true;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto true;
+  else if (cstart2 < cend2)
+    goto false;
+  else
+    goto false;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_le, "string<=", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} is greater to @var{s2}, a true\n"
+           "value otherwise.")
+#define FUNC_NAME s_scm_string_le
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    3, start1, cstart1,
+                                    4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    5, start2, cstart2,
+                                    6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (cstr1[cstart1] < cstr2[cstart2])
+       goto true;
+      else if (cstr1[cstart1] > cstr2[cstart2])
+       goto false;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto false;
+  else if (cstart2 < cend2)
+    goto true;
+  else
+    goto true;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_ge, "string>=", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} is less to @var{s2}, a true value\n"
+           "otherwise.")
+#define FUNC_NAME s_scm_string_ge
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    3, start1, cstart1,
+                                    4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    5, start2, cstart2,
+                                    6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (cstr1[cstart1] < cstr2[cstart2])
+       goto false;
+      else if (cstr1[cstart1] > cstr2[cstart2])
+       goto true;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto true;
+  else if (cstart2 < cend2)
+    goto false;
+  else
+    goto true;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_ci_eq, "string-ci=", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} and @var{s2} are not equal, a true\n"
+           "value otherwise.  The character comparison is done\n"
+           "case-insensitively.")
+#define FUNC_NAME s_scm_string_ci_eq
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) < scm_c_downcase (cstr2[cstart2]))
+       goto false;
+      else if (scm_c_downcase (cstr1[cstart1]) > scm_c_downcase (cstr2[cstart2]))
+       goto false;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto false;
+  else if (cstart2 < cend2)
+    goto false;
+  else
+    goto true;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_ci_neq, "string-ci<>", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} and @var{s2} are equal, a true\n"
+           "value otherwise.  The character comparison is done\n"
+           "case-insensitively.")
+#define FUNC_NAME s_scm_string_ci_neq
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) < scm_c_downcase (cstr2[cstart2]))
+       goto true;
+      else if (scm_c_downcase (cstr1[cstart1]) > scm_c_downcase (cstr2[cstart2]))
+       goto true;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto true;
+  else if (cstart2 < cend2)
+    goto true;
+  else
+    goto false;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_ci_lt, "string-ci<", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} is greater or equal to @var{s2}, a\n"
+           "true value otherwise.  The character comparison is done\n"
+           "case-insensitively.")
+#define FUNC_NAME s_scm_string_ci_lt
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    3, start1, cstart1,
+                                    4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    5, start2, cstart2,
+                                    6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) < scm_c_downcase (cstr2[cstart2]))
+       goto true;
+      else if (scm_c_downcase (cstr1[cstart1]) > scm_c_downcase (cstr2[cstart2]))
+       goto false;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto false;
+  else if (cstart2 < cend2)
+    goto true;
+  else
+    goto false;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_ci_gt, "string-ci>", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} is less or equal to @var{s2}, a\n"
+           "true value otherwise.  The character comparison is done\n"
+           "case-insensitively.")
+#define FUNC_NAME s_scm_string_ci_gt
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    3, start1, cstart1,
+                                    4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    5, start2, cstart2,
+                                    6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) < scm_c_downcase (cstr2[cstart2]))
+       goto false;
+      else if (scm_c_downcase (cstr1[cstart1]) > scm_c_downcase (cstr2[cstart2]))
+       goto true;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto true;
+  else if (cstart2 < cend2)
+    goto false;
+  else
+    goto false;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_ci_le, "string-ci<=", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} is greater to @var{s2}, a true\n"
+           "value otherwise.  The character comparison is done\n"
+           "case-insensitively.")
+#define FUNC_NAME s_scm_string_ci_le
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    3, start1, cstart1,
+                                    4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    5, start2, cstart2,
+                                    6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) < scm_c_downcase (cstr2[cstart2]))
+       goto true;
+      else if (scm_c_downcase (cstr1[cstart1]) > scm_c_downcase (cstr2[cstart2]))
+       goto false;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto false;
+  else if (cstart2 < cend2)
+    goto true;
+  else
+    goto true;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_ci_ge, "string-ci>=", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return @code{#f} if @var{s1} is less to @var{s2}, a true value\n"
+           "otherwise.  The character comparison is done\n"
+           "case-insensitively.")
+#define FUNC_NAME s_scm_string_ci_ge
+{
+  const unsigned char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (1, s1, cstr1,
+                                    3, start1, cstart1,
+                                    4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_UCOPY (2, s2, cstr2,
+                                    5, start2, cstart2,
+                                    6, end2, cend2);
+
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) < scm_c_downcase (cstr2[cstart2]))
+       goto false;
+      else if (scm_c_downcase (cstr1[cstart1]) > scm_c_downcase (cstr2[cstart2]))
+       goto true;
+      cstart1++;
+      cstart2++;
+    }
+  if (cstart1 < cend1)
+    goto true;
+  else if (cstart2 < cend2)
+    goto false;
+  else
+    goto true;
+
+ true:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (cstart1);
+
+ false:
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_substring_hash, "string-hash", 1, 3, 0,
+           (SCM s, SCM bound, SCM start, SCM end),
+           "Compute a hash value for @var{S}.  the optional argument "
+           "@var{bound} is a non-negative exact "
+            "integer specifying the range of the hash function. "
+           "A positive value restricts the return value to the "
+           "range [0,bound).")
+#define FUNC_NAME s_scm_substring_hash
+{
+  if (SCM_UNBNDP (bound))
+    bound = scm_from_intmax (SCM_MOST_POSITIVE_FIXNUM);
+  if (SCM_UNBNDP (start))
+    start = SCM_INUM0;
+  return scm_hash (scm_substring_shared (s, start, end), bound);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_substring_hash_ci, "string-hash-ci", 1, 3, 0,
+           (SCM s, SCM bound, SCM start, SCM end),
+           "Compute a hash value for @var{S}.  the optional argument "
+           "@var{bound} is a non-negative exact "
+            "integer specifying the range of the hash function. "
+           "A positive value restricts the return value to the "
+           "range [0,bound).")
+#define FUNC_NAME s_scm_substring_hash_ci
+{
+  return scm_substring_hash (scm_substring_downcase (s, start, end),
+                            bound,
+                            SCM_UNDEFINED, SCM_UNDEFINED);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_prefix_length, "string-prefix-length", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return the length of the longest common prefix of the two\n"
+           "strings.")
+#define FUNC_NAME s_scm_string_prefix_length
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len = 0;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (cstr1[cstart1] != cstr2[cstart2])
+       goto ret;
+      len++;
+      cstart1++;
+      cstart2++;
+    }
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (len);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_prefix_length_ci, "string-prefix-length-ci", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return the length of the longest common prefix of the two\n"
+           "strings, ignoring character case.")
+#define FUNC_NAME s_scm_string_prefix_length_ci
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len = 0;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) != scm_c_downcase (cstr2[cstart2]))
+       goto ret;
+      len++;
+      cstart1++;
+      cstart2++;
+    }
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (len);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_suffix_length, "string-suffix-length", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return the length of the longest common suffix of the two\n"
+           "strings.")
+#define FUNC_NAME s_scm_string_suffix_length
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len = 0;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      cend1--;
+      cend2--;
+      if (cstr1[cend1] != cstr2[cend2])
+       goto ret;
+      len++;
+    }
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (len);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_suffix_length_ci, "string-suffix-length-ci", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return the length of the longest common suffix of the two\n"
+           "strings, ignoring character case.")
+#define FUNC_NAME s_scm_string_suffix_length_ci
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len = 0;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      cend1--;
+      cend2--;
+      if (scm_c_downcase (cstr1[cend1]) != scm_c_downcase (cstr2[cend2]))
+       goto ret;
+      len++;
+    }
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_size_t (len);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_prefix_p, "string-prefix?", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Is @var{s1} a prefix of @var{s2}?")
+#define FUNC_NAME s_scm_string_prefix_p
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len = 0, len1;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  len1 = cend1 - cstart1;
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (cstr1[cstart1] != cstr2[cstart2])
+       goto ret;
+      len++;
+      cstart1++;
+      cstart2++;
+    }
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_bool (len == len1);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_prefix_ci_p, "string-prefix-ci?", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Is @var{s1} a prefix of @var{s2}, ignoring character case?")
+#define FUNC_NAME s_scm_string_prefix_ci_p
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len = 0, len1;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  len1 = cend1 - cstart1;
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      if (scm_c_downcase (cstr1[cstart1]) != scm_c_downcase (cstr2[cstart2]))
+       goto ret;
+      len++;
+      cstart1++;
+      cstart2++;
+    }
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_bool (len == len1);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_suffix_p, "string-suffix?", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Is @var{s1} a suffix of @var{s2}?")
+#define FUNC_NAME s_scm_string_suffix_p
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len = 0, len1;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  len1 = cend1 - cstart1;
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      cend1--;
+      cend2--;
+      if (cstr1[cend1] != cstr2[cend2])
+       goto ret;
+      len++;
+    }
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_bool (len == len1);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_suffix_ci_p, "string-suffix-ci?", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Is @var{s1} a suffix of @var{s2}, ignoring character case?")
+#define FUNC_NAME s_scm_string_suffix_ci_p
+{
+  const char *cstr1, *cstr2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len = 0, len1;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cstr1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cstr2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  len1 = cend1 - cstart1;
+  while (cstart1 < cend1 && cstart2 < cend2)
+    {
+      cend1--;
+      cend2--;
+      if (scm_c_downcase (cstr1[cend1]) != scm_c_downcase (cstr2[cend2]))
+       goto ret;
+      len++;
+    }
+
+ ret:
+  scm_remember_upto_here_2 (s1, s2);
+  return scm_from_bool (len == len1);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_index, "string-index", 2, 2, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Search through the string @var{s} from left to right, returning\n"
+           "the index of the first occurence of a character which\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "equals @var{char_pred}, if it is character,\n"
+           "\n"
+           "@item\n"
+           "satisifies the predicate @var{char_pred}, if it is a procedure,\n"
+           "\n"
+           "@item\n"
+           "is in the set @var{char_pred}, if it is a character set.\n"
+           "@end itemize")
+#define FUNC_NAME s_scm_string_index
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_CHARP (char_pred))
+    {
+      char cchr = SCM_CHAR (char_pred);
+      while (cstart < cend)
+       {
+         if (cchr == cstr[cstart])
+           goto found;
+         cstart++;
+       }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (SCM_CHARSET_GET (char_pred, cstr[cstart]))
+           goto found;
+         cstart++;
+       }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      while (cstart < cend)
+       {
+         SCM res;
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cstart]));
+         if (scm_is_true (res))
+           goto found;
+         cstr = scm_i_string_chars (s);
+         cstart++;
+       }
+    }
+  
+  scm_remember_upto_here_1 (s);
+  return SCM_BOOL_F;
+  
+ found:
+  scm_remember_upto_here_1 (s);
+  return scm_from_size_t (cstart);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_index_right, "string-index-right", 2, 2, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Search through the string @var{s} from right to left, returning\n"
+           "the index of the last occurence of a character which\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "equals @var{char_pred}, if it is character,\n"
+           "\n"
+           "@item\n"
+           "satisifies the predicate @var{char_pred}, if it is a procedure,\n"
+           "\n"
+           "@item\n"
+           "is in the set if @var{char_pred} is a character set.\n"
+           "@end itemize")
+#define FUNC_NAME s_scm_string_index_right
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_CHARP (char_pred))
+    {
+      char cchr = SCM_CHAR (char_pred);
+      while (cstart < cend)
+       {
+         cend--;
+         if (cchr == cstr[cend])
+           goto found;
+       }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         cend--;
+         if (SCM_CHARSET_GET (char_pred, cstr[cend]))
+           goto found;
+       }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      while (cstart < cend)
+       {
+         SCM res;
+         cend--;
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cend]));
+         if (scm_is_true (res))
+           goto found;
+         cstr = scm_i_string_chars (s);
+       }
+    }
+
+  scm_remember_upto_here_1 (s);
+  return SCM_BOOL_F;
+
+ found:
+  scm_remember_upto_here_1 (s);
+  return scm_from_size_t (cend);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_rindex, "string-rindex", 2, 2, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Search through the string @var{s} from right to left, returning\n"
+           "the index of the last occurence of a character which\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "equals @var{char_pred}, if it is character,\n"
+           "\n"
+           "@item\n"
+           "satisifies the predicate @var{char_pred}, if it is a procedure,\n"
+           "\n"
+           "@item\n"
+           "is in the set if @var{char_pred} is a character set.\n"
+           "@end itemize")
+#define FUNC_NAME s_scm_string_rindex
+{
+  return scm_string_index_right (s, char_pred, start, end);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_skip, "string-skip", 2, 2, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Search through the string @var{s} from left to right, returning\n"
+           "the index of the first occurence of a character which\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "does not equal @var{char_pred}, if it is character,\n"
+           "\n"
+           "@item\n"
+           "does not satisify the predicate @var{char_pred}, if it is a\n"
+           "procedure,\n"
+           "\n"
+           "@item\n"
+           "is not in the set if @var{char_pred} is a character set.\n"
+           "@end itemize")
+#define FUNC_NAME s_scm_string_skip
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_CHARP (char_pred))
+    {
+      char cchr = SCM_CHAR (char_pred);
+      while (cstart < cend)
+       {
+         if (cchr != cstr[cstart])
+           goto found;
+         cstart++;
+       }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (!SCM_CHARSET_GET (char_pred, cstr[cstart]))
+           goto found;
+         cstart++;
+       }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      while (cstart < cend)
+       {
+         SCM res;
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cstart]));
+         if (scm_is_false (res))
+           goto found;
+         cstr = scm_i_string_chars (s);
+         cstart++;
+       }
+    }
+
+  scm_remember_upto_here_1 (s);
+  return SCM_BOOL_F;
+
+ found:
+  scm_remember_upto_here_1 (s);
+  return scm_from_size_t (cstart);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_skip_right, "string-skip-right", 2, 2, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Search through the string @var{s} from right to left, returning\n"
+           "the index of the last occurence of a character which\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "does not equal @var{char_pred}, if it is character,\n"
+           "\n"
+           "@item\n"
+           "does not satisfy the predicate @var{char_pred}, if it is a\n"
+           "procedure,\n"
+           "\n"
+           "@item\n"
+           "is not in the set if @var{char_pred} is a character set.\n"
+           "@end itemize")
+#define FUNC_NAME s_scm_string_skip_right
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_CHARP (char_pred))
+    {
+      char cchr = SCM_CHAR (char_pred);
+      while (cstart < cend)
+       {
+         cend--;
+         if (cchr != cstr[cend])
+           goto found;
+       }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         cend--;
+         if (!SCM_CHARSET_GET (char_pred, cstr[cend]))
+           goto found;
+       }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      while (cstart < cend)
+       {
+         SCM res;
+         cend--;
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cend]));
+         if (scm_is_false (res))
+           goto found;
+         cstr = scm_i_string_chars (s);
+       }
+    }
+
+  scm_remember_upto_here_1 (s);
+  return SCM_BOOL_F;
+
+ found:
+  scm_remember_upto_here_1 (s);
+  return scm_from_size_t (cend);
+
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_count, "string-count", 2, 2, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Return the count of the number of characters in the string\n"
+           "@var{s} which\n"
+           "\n"
+           "@itemize @bullet\n"
+           "@item\n"
+           "equals @var{char_pred}, if it is character,\n"
+           "\n"
+           "@item\n"
+           "satisifies the predicate @var{char_pred}, if it is a procedure.\n"
+           "\n"
+           "@item\n"
+           "is in the set @var{char_pred}, if it is a character set.\n"
+           "@end itemize")
+#define FUNC_NAME s_scm_string_count
+{
+  const char *cstr;
+  size_t cstart, cend;
+  size_t count = 0;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  if (SCM_CHARP (char_pred))
+    {
+      char cchr = SCM_CHAR (char_pred);
+      while (cstart < cend)
+       {
+         if (cchr == cstr[cstart])
+           count++;
+         cstart++;
+       }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      while (cstart < cend)
+       {
+         if (SCM_CHARSET_GET (char_pred, cstr[cstart]))
+           count++;
+         cstart++;
+       }
+    }
+  else
+    {
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      while (cstart < cend)
+       {
+         SCM res;
+         res = pred_tramp (char_pred, SCM_MAKE_CHAR (cstr[cstart]));
+         if (scm_is_true (res))
+           count++;
+         cstr = scm_i_string_chars (s);
+         cstart++;
+       }
+    }
+
+  scm_remember_upto_here_1 (s);
+  return scm_from_size_t (count);
+}
+#undef FUNC_NAME
+
+
+/* FIXME::martin: This should definitely get implemented more
+   efficiently -- maybe with Knuth-Morris-Pratt, like in the reference
+   implementation.  */
+SCM_DEFINE (scm_string_contains, "string-contains", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Does string @var{s1} contain string @var{s2}?  Return the index\n"
+           "in @var{s1} where @var{s2} occurs as a substring, or false.\n"
+           "The optional start/end indices restrict the operation to the\n"
+           "indicated substrings.")
+#define FUNC_NAME s_scm_string_contains
+{
+  const char *cs1, * cs2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len2, i, j;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cs1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cs2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  len2 = cend2 - cstart2;
+  if (cend1 - cstart1 >= len2)
+    while (cstart1 <= cend1 - len2)
+      {
+       i = cstart1;
+       j = cstart2;
+       while (i < cend1 && j < cend2 && cs1[i] == cs2[j])
+         {
+           i++;
+           j++;
+         }
+       if (j == cend2)
+         {
+           scm_remember_upto_here_2 (s1, s2);
+           return scm_from_size_t (cstart1);
+         }
+       cstart1++;
+      }
+
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+/* FIXME::martin: This should definitely get implemented more
+   efficiently -- maybe with Knuth-Morris-Pratt, like in the reference
+   implementation.  */
+SCM_DEFINE (scm_string_contains_ci, "string-contains-ci", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Does string @var{s1} contain string @var{s2}?  Return the index\n"
+           "in @var{s1} where @var{s2} occurs as a substring, or false.\n"
+           "The optional start/end indices restrict the operation to the\n"
+           "indicated substrings.  Character comparison is done\n"
+           "case-insensitively.")
+#define FUNC_NAME s_scm_string_contains_ci
+{
+  const char *cs1, * cs2;
+  size_t cstart1, cend1, cstart2, cend2;
+  size_t len2, i, j;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s1, cs1,
+                                  3, start1, cstart1,
+                                  4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s2, cs2,
+                                  5, start2, cstart2,
+                                  6, end2, cend2);
+  len2 = cend2 - cstart2;
+  if (cend1 - cstart1 >= len2)
+    while (cstart1 <= cend1 - len2)
+      {
+       i = cstart1;
+       j = cstart2;
+       while (i < cend1 && j < cend2 &&
+              scm_c_downcase (cs1[i]) == scm_c_downcase (cs2[j]))
+         {
+           i++;
+           j++;
+         }
+       if (j == cend2)
+         {
+           scm_remember_upto_here_2 (s1, s2);
+           return scm_from_size_t (cstart1);
+         }
+       cstart1++;
+      }
+  
+  scm_remember_upto_here_2 (s1, s2);
+  return SCM_BOOL_F;
+}
+#undef FUNC_NAME
+
+
+/* Helper function for the string uppercase conversion functions.
+ * No argument checking is performed.  */
+static SCM
+string_upcase_x (SCM v, size_t start, size_t end)
+{
+  size_t k;
+  char *dst;
+
+  dst = scm_i_string_writable_chars (v);
+  for (k = start; k < end; ++k)
+    dst[k] = scm_c_upcase (dst[k]);
+  scm_i_string_stop_writing ();
+  scm_remember_upto_here_1 (v);
+
+  return v;
+}
+
+SCM_DEFINE (scm_substring_upcase_x, "string-upcase!", 1, 2, 0,
+           (SCM str, SCM start, SCM end),
+           "Destructively upcase every character in @code{str}.\n"
+           "\n"
+           "@lisp\n"
+           "(string-upcase! y)\n"
+           "@result{} \"ARRDEFG\"\n"
+           "y\n"
+           "@result{} \"ARRDEFG\"\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_substring_upcase_x
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  return string_upcase_x (str, cstart, cend);
+}
+#undef FUNC_NAME
+
+SCM
+scm_string_upcase_x (SCM str)
+{
+  return scm_substring_upcase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
+}
+
+SCM_DEFINE (scm_substring_upcase, "string-upcase", 1, 2, 0,
+           (SCM str, SCM start, SCM end),
+           "Upcase every character in @code{str}.")
+#define FUNC_NAME s_scm_substring_upcase
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  return string_upcase_x (scm_string_copy (str), cstart, cend);
+}
+#undef FUNC_NAME
+
+SCM
+scm_string_upcase (SCM str)
+{
+  return scm_substring_upcase (str, SCM_UNDEFINED, SCM_UNDEFINED);
+}
+
+/* Helper function for the string lowercase conversion functions.
+ * No argument checking is performed.  */
+static SCM
+string_downcase_x (SCM v, size_t start, size_t end)
+{
+  size_t k;
+  char *dst;
+
+  dst = scm_i_string_writable_chars (v);
+  for (k = start; k < end; ++k)
+    dst[k] = scm_c_downcase (dst[k]);
+  scm_i_string_stop_writing ();
+  scm_remember_upto_here_1 (v);
+
+  return v;
+}
+
+SCM_DEFINE (scm_substring_downcase_x, "string-downcase!", 1, 2, 0,
+           (SCM str, SCM start, SCM end),
+           "Destructively downcase every character in @var{str}.\n"
+           "\n"
+           "@lisp\n"
+           "y\n"
+           "@result{} \"ARRDEFG\"\n"
+           "(string-downcase! y)\n"
+           "@result{} \"arrdefg\"\n"
+           "y\n"
+           "@result{} \"arrdefg\"\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_substring_downcase_x
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  return string_downcase_x (str, cstart, cend);
+}
+#undef FUNC_NAME
+
+SCM
+scm_string_downcase_x (SCM str)
+{
+  return scm_substring_downcase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
+}
+
+SCM_DEFINE (scm_substring_downcase, "string-downcase", 1, 2, 0,
+           (SCM str, SCM start, SCM end),
+           "Downcase every character in @var{str}.")
+#define FUNC_NAME s_scm_substring_downcase
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  return string_downcase_x (scm_string_copy (str), cstart, cend);
+}
+#undef FUNC_NAME
+
+SCM
+scm_string_downcase (SCM str)
+{
+  return scm_substring_downcase (str, SCM_UNDEFINED, SCM_UNDEFINED);
+}
+
+/* Helper function for the string capitalization functions.
+ * No argument checking is performed.  */
+static SCM
+string_titlecase_x (SCM str, size_t start, size_t end)
+{
+  unsigned char *sz;
+  size_t i;
+  int in_word = 0;
+
+  sz = (unsigned char *) scm_i_string_writable_chars (str);
+  for(i = start; i < end;  i++)
+    {
+      if (scm_is_true (scm_char_alphabetic_p (SCM_MAKE_CHAR (sz[i]))))
+       {
+         if (!in_word)
+           {
+             sz[i] = scm_c_upcase(sz[i]);
+             in_word = 1;
+           }
+         else
+           {
+             sz[i] = scm_c_downcase(sz[i]);
+           }
+       }
+      else
+       in_word = 0;
+    }
+  scm_i_string_stop_writing ();
+  scm_remember_upto_here_1 (str);
+
+  return str;
+}
+
+
+SCM_DEFINE (scm_string_titlecase_x, "string-titlecase!", 1, 2, 0,
+           (SCM str, SCM start, SCM end),
+           "Destructively titlecase every first character in a word in\n"
+           "@var{str}.")
+#define FUNC_NAME s_scm_string_titlecase_x
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  return string_titlecase_x (str, cstart, cend);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_titlecase, "string-titlecase", 1, 2, 0,
+           (SCM str, SCM start, SCM end),
+           "Titlecase every first character in a word in @var{str}.")
+#define FUNC_NAME s_scm_string_titlecase
+{
+  const char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  return string_titlecase_x (scm_string_copy (str), cstart, cend);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_capitalize_x, "string-capitalize!", 1, 0, 0,
+           (SCM str),
+           "Upcase the first character of every word in @var{str}\n"
+           "destructively and return @var{str}.\n"
+           "\n"
+           "@lisp\n"
+           "y                      @result{} \"hello world\"\n"
+           "(string-capitalize! y) @result{} \"Hello World\"\n"
+           "y                      @result{} \"Hello World\"\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_string_capitalize_x
+{
+  return scm_string_titlecase_x (str, SCM_UNDEFINED, SCM_UNDEFINED);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_capitalize, "string-capitalize", 1, 0, 0,
+           (SCM str),
+           "Return a freshly allocated string with the characters in\n"
+           "@var{str}, where the first character of every word is\n"
+           "capitalized.")
+#define FUNC_NAME s_scm_string_capitalize
+{
+  return scm_string_capitalize_x (scm_string_copy (str));
+}
+#undef FUNC_NAME
+
+
+/* Reverse the portion of @var{str} between str[cstart] (including)
+   and str[cend] excluding.  */
+static void
+string_reverse_x (char * str, size_t cstart, size_t cend)
+{
+  char tmp;
+
+  if (cend > 0)
+    {
+      cend--;
+      while (cstart < cend)
+       {
+         tmp = str[cstart];
+         str[cstart] = str[cend];
+         str[cend] = tmp;
+         cstart++;
+         cend--;
+       }
+    }
+}
+
+
+SCM_DEFINE (scm_string_reverse, "string-reverse", 1, 2, 0,
+            (SCM str, SCM start, SCM end),
+           "Reverse the string @var{str}.  The optional arguments\n"
+           "@var{start} and @var{end} delimit the region of @var{str} to\n"
+           "operate on.")
+#define FUNC_NAME s_scm_string_reverse
+{
+  const char *cstr;
+  char *ctarget;
+  size_t cstart, cend;
+  SCM result;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, str, cstr,
+                                  2, start, cstart,
+                                  3, end, cend);
+  result = scm_string_copy (str);
+  ctarget = scm_i_string_writable_chars (result);
+  string_reverse_x (ctarget, cstart, cend);
+  scm_i_string_stop_writing ();
+  scm_remember_upto_here_1 (str);
+  return result;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_reverse_x, "string-reverse!", 1, 2, 0,
+            (SCM str, SCM start, SCM end),
+           "Reverse the string @var{str} in-place.  The optional arguments\n"
+           "@var{start} and @var{end} delimit the region of @var{str} to\n"
+           "operate on.  The return value is unspecified.")
+#define FUNC_NAME s_scm_string_reverse_x
+{
+  char *cstr;
+  size_t cstart, cend;
+
+  MY_VALIDATE_SUBSTRING_SPEC (1, str,
+                             2, start, cstart,
+                             3, end, cend);
+
+  cstr = scm_i_string_writable_chars (str);
+  string_reverse_x (cstr, cstart, cend);
+  scm_i_string_stop_writing ();
+  scm_remember_upto_here_1 (str);
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_append_shared, "string-append/shared", 0, 0, 1,
+            (SCM rest),
+           "Like @code{string-append}, but the result may share memory\n"
+           "with the argument strings.")
+#define FUNC_NAME s_scm_string_append_shared
+{
+  /* If "rest" contains just one non-empty string, return that.
+     If it's entirely empty strings, then return scm_nullstr.
+     Otherwise use scm_string_concatenate.  */
+
+  SCM ret = scm_nullstr;
+  int seen_nonempty = 0;
+  SCM l, s;
+
+  SCM_VALIDATE_REST_ARGUMENT (rest);
+
+  for (l = rest; scm_is_pair (l); l = SCM_CDR (l))
+    {
+      s = SCM_CAR (l);
+      if (scm_c_string_length (s) != 0)
+        {
+          if (seen_nonempty)
+            /* two or more non-empty strings, need full concat */
+            return scm_string_append (rest);
+
+          seen_nonempty = 1;
+          ret = s;
+        }
+    }
+  return ret;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_concatenate, "string-concatenate", 1, 0, 0,
+            (SCM ls),
+           "Append the elements of @var{ls} (which must be strings)\n"
+           "together into a single string.  Guaranteed to return a freshly\n"
+           "allocated string.")
+#define FUNC_NAME s_scm_string_concatenate
+{
+  SCM_VALIDATE_LIST (SCM_ARG1, ls);
+  return scm_string_append (ls);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_concatenate_reverse, "string-concatenate-reverse", 1, 2, 0,
+            (SCM ls, SCM final_string, SCM end),
+           "Without optional arguments, this procedure is equivalent to\n"
+           "\n"
+           "@smalllisp\n"
+           "(string-concatenate (reverse ls))\n"
+           "@end smalllisp\n"
+           "\n"
+           "If the optional argument @var{final_string} is specified, it is\n"
+           "consed onto the beginning to @var{ls} before performing the\n"
+           "list-reverse and string-concatenate operations.  If @var{end}\n"
+           "is given, only the characters of @var{final_string} up to index\n"
+           "@var{end} are used.\n"
+           "\n"
+           "Guaranteed to return a freshly allocated string.")
+#define FUNC_NAME s_scm_string_concatenate_reverse
+{
+  if (!SCM_UNBNDP (end))
+    final_string = scm_substring (final_string, SCM_INUM0, end);
+
+  if (!SCM_UNBNDP (final_string))
+    ls = scm_cons (final_string, ls);
+
+  return scm_string_concatenate (scm_reverse (ls));
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_concatenate_shared, "string-concatenate/shared", 1, 0, 0,
+            (SCM ls),
+           "Like @code{string-concatenate}, but the result may share memory\n"
+           "with the strings in the list @var{ls}.")
+#define FUNC_NAME s_scm_string_concatenate_shared
+{
+  SCM_VALIDATE_LIST (SCM_ARG1, ls);
+  return scm_string_append_shared (ls);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_concatenate_reverse_shared, "string-concatenate-reverse/shared", 1, 2, 0,
+            (SCM ls, SCM final_string, SCM end),
+           "Like @code{string-concatenate-reverse}, but the result may\n"
+           "share memory with the the strings in the @var{ls} arguments.")
+#define FUNC_NAME s_scm_string_concatenate_reverse_shared
+{
+  /* Just call the non-sharing version.  */
+  return scm_string_concatenate_reverse (ls, final_string, end);
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_map, "string-map", 2, 2, 0,
+           (SCM proc, SCM s, SCM start, SCM end),
+           "@var{proc} is a char->char procedure, it is mapped over\n"
+           "@var{s}.  The order in which the procedure is applied to the\n"
+           "string elements is not specified.")
+#define FUNC_NAME s_scm_string_map
+{
+  char *p;
+  size_t cstart, cend;
+  SCM result;
+  scm_t_trampoline_1 proc_tramp = scm_trampoline_1 (proc);
+
+  SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
+  MY_VALIDATE_SUBSTRING_SPEC (2, s,
+                             3, start, cstart,
+                             4, end, cend);
+  result = scm_i_make_string (cend - cstart, &p);
+  while (cstart < cend)
+    {
+      SCM ch = proc_tramp (proc, scm_c_string_ref (s, cstart));
+      if (!SCM_CHARP (ch))
+       SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (proc));
+      cstart++;
+      *p++ = SCM_CHAR (ch);
+    }
+  return result;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_map_x, "string-map!", 2, 2, 0,
+           (SCM proc, SCM s, SCM start, SCM end),
+           "@var{proc} is a char->char procedure, it is mapped over\n"
+           "@var{s}.  The order in which the procedure is applied to the\n"
+           "string elements is not specified.  The string @var{s} is\n"
+           "modified in-place, the return value is not specified.")
+#define FUNC_NAME s_scm_string_map_x
+{
+  size_t cstart, cend;
+  scm_t_trampoline_1 proc_tramp = scm_trampoline_1 (proc);
+
+  SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
+  MY_VALIDATE_SUBSTRING_SPEC (2, s,
+                             3, start, cstart,
+                             4, end, cend);
+  while (cstart < cend)
+    {
+      SCM ch = proc_tramp (proc, scm_c_string_ref (s, cstart));
+      if (!SCM_CHARP (ch))
+       SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (proc));
+      scm_c_string_set_x (s, cstart, ch);
+      cstart++;
+    }
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_fold, "string-fold", 3, 2, 0,
+           (SCM kons, SCM knil, SCM s, SCM start, SCM end),
+           "Fold @var{kons} over the characters of @var{s}, with @var{knil}\n"
+           "as the terminating element, from left to right.  @var{kons}\n"
+           "must expect two arguments: The actual character and the last\n"
+           "result of @var{kons}' application.")
+#define FUNC_NAME s_scm_string_fold
+{
+  const char *cstr;
+  size_t cstart, cend;
+  SCM result;
+
+  SCM_VALIDATE_PROC (1, kons);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (3, s, cstr,
+                                  4, start, cstart,
+                                  5, end, cend);
+  result = knil;
+  while (cstart < cend)
+    {
+      unsigned int c = (unsigned char) cstr[cstart];
+      result = scm_call_2 (kons, SCM_MAKE_CHAR (c), result);
+      cstr = scm_i_string_chars (s);
+      cstart++;
+    }
+
+  scm_remember_upto_here_1 (s);
+  return result;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_fold_right, "string-fold-right", 3, 2, 0,
+           (SCM kons, SCM knil, SCM s, SCM start, SCM end),
+           "Fold @var{kons} over the characters of @var{s}, with @var{knil}\n"
+           "as the terminating element, from right to left.  @var{kons}\n"
+           "must expect two arguments: The actual character and the last\n"
+           "result of @var{kons}' application.")
+#define FUNC_NAME s_scm_string_fold_right
+{
+  const char *cstr;
+  size_t cstart, cend;
+  SCM result;
+
+  SCM_VALIDATE_PROC (1, kons);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (3, s, cstr,
+                                  4, start, cstart,
+                                  5, end, cend);
+  result = knil;
+  while (cstart < cend)
+    {
+      unsigned int c  = (unsigned char) cstr[cend - 1];
+      result = scm_call_2 (kons, SCM_MAKE_CHAR (c), result);
+      cstr = scm_i_string_chars (s);
+      cend--;
+    }
+
+  scm_remember_upto_here_1 (s);
+  return result;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_unfold, "string-unfold", 4, 2, 0,
+           (SCM p, SCM f, SCM g, SCM seed, SCM base, SCM make_final),
+           "@itemize @bullet\n"
+           "@item @var{g} is used to generate a series of @emph{seed}\n"
+           "values from the initial @var{seed}: @var{seed}, (@var{g}\n"
+           "@var{seed}), (@var{g}^2 @var{seed}), (@var{g}^3 @var{seed}),\n"
+           "@dots{}\n"
+           "@item @var{p} tells us when to stop -- when it returns true\n"
+           "when applied to one of these seed values.\n"
+           "@item @var{f} maps each seed value to the corresponding\n"
+           "character in the result string.  These chars are assembled\n"
+           "into the string in a left-to-right order.\n"
+           "@item @var{base} is the optional initial/leftmost portion\n"
+           "of the constructed string; it default to the empty\n"
+           "string.\n"
+           "@item @var{make_final} is applied to the terminal seed\n"
+           "value (on which @var{p} returns true) to produce\n"
+           "the final/rightmost portion of the constructed string.\n"
+           "It defaults to @code{(lambda (x) "")}.\n"
+           "@end itemize")
+#define FUNC_NAME s_scm_string_unfold
+{
+  SCM res, ans;
+
+  SCM_VALIDATE_PROC (1, p);
+  SCM_VALIDATE_PROC (2, f);
+  SCM_VALIDATE_PROC (3, g);
+  if (!SCM_UNBNDP (base))
+    {
+      SCM_VALIDATE_STRING (5, base);
+      ans = base;
+    }
+  else
+    ans = scm_i_make_string (0, NULL);
+  if (!SCM_UNBNDP (make_final))
+    SCM_VALIDATE_PROC (6, make_final);
+
+  res = scm_call_1 (p, seed);
+  while (scm_is_false (res))
+    {
+      SCM str;
+      char *ptr;
+      SCM ch = scm_call_1 (f, seed);
+      if (!SCM_CHARP (ch))
+       SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (f));
+      str = scm_i_make_string (1, &ptr);
+      *ptr = SCM_CHAR (ch);
+
+      ans = scm_string_append (scm_list_2 (ans, str));
+      seed = scm_call_1 (g, seed);
+      res = scm_call_1 (p, seed);
+    }
+  if (!SCM_UNBNDP (make_final))
+    {
+      res = scm_call_1 (make_final, seed);
+      return scm_string_append (scm_list_2 (ans, res));
+    }
+  else
+    return ans;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_unfold_right, "string-unfold-right", 4, 2, 0,
+           (SCM p, SCM f, SCM g, SCM seed, SCM base, SCM make_final),
+           "@itemize @bullet\n"
+           "@item @var{g} is used to generate a series of @emph{seed}\n"
+           "values from the initial @var{seed}: @var{seed}, (@var{g}\n"
+           "@var{seed}), (@var{g}^2 @var{seed}), (@var{g}^3 @var{seed}),\n"
+           "@dots{}\n"
+           "@item @var{p} tells us when to stop -- when it returns true\n"
+           "when applied to one of these seed values.\n"
+           "@item @var{f} maps each seed value to the corresponding\n"
+           "character in the result string.  These chars are assembled\n"
+           "into the string in a right-to-left order.\n"
+           "@item @var{base} is the optional initial/rightmost portion\n"
+           "of the constructed string; it default to the empty\n"
+           "string.\n"
+           "@item @var{make_final} is applied to the terminal seed\n"
+           "value (on which @var{p} returns true) to produce\n"
+           "the final/leftmost portion of the constructed string.\n"
+           "It defaults to @code{(lambda (x) "")}.\n"
+           "@end itemize")
+#define FUNC_NAME s_scm_string_unfold_right
+{
+  SCM res, ans;
+
+  SCM_VALIDATE_PROC (1, p);
+  SCM_VALIDATE_PROC (2, f);
+  SCM_VALIDATE_PROC (3, g);
+  if (!SCM_UNBNDP (base))
+    {
+      SCM_VALIDATE_STRING (5, base);
+      ans = base;
+    }
+  else
+    ans = scm_i_make_string (0, NULL);
+  if (!SCM_UNBNDP (make_final))
+    SCM_VALIDATE_PROC (6, make_final);
+
+  res = scm_call_1 (p, seed);
+  while (scm_is_false (res))
+    {
+      SCM str;
+      char *ptr;
+      SCM ch = scm_call_1 (f, seed);
+      if (!SCM_CHARP (ch))
+       SCM_MISC_ERROR ("procedure ~S returned non-char", scm_list_1 (f));
+      str = scm_i_make_string (1, &ptr);
+      *ptr = SCM_CHAR (ch);
+
+      ans = scm_string_append (scm_list_2 (str, ans));
+      seed = scm_call_1 (g, seed);
+      res = scm_call_1 (p, seed);
+    }
+  if (!SCM_UNBNDP (make_final))
+    {
+      res = scm_call_1 (make_final, seed);
+      return scm_string_append (scm_list_2 (res, ans));
+    }
+  else
+    return ans;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_for_each, "string-for-each", 2, 2, 0,
+           (SCM proc, SCM s, SCM start, SCM end),
+           "@var{proc} is mapped over @var{s} in left-to-right order.  The\n"
+           "return value is not specified.")
+#define FUNC_NAME s_scm_string_for_each
+{
+  const char *cstr;
+  size_t cstart, cend;
+  scm_t_trampoline_1 proc_tramp = scm_trampoline_1 (proc);
+
+  SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (2, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+  while (cstart < cend)
+    {
+      unsigned int c = (unsigned char) cstr[cstart];
+      proc_tramp (proc, SCM_MAKE_CHAR (c));
+      cstr = scm_i_string_chars (s);
+      cstart++;
+    }
+
+  scm_remember_upto_here_1 (s);
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_for_each_index, "string-for-each-index", 2, 2, 0,
+           (SCM proc, SCM s, SCM start, SCM end),
+           "Call @code{(@var{proc} i)} for each index i in @var{s}, from\n"
+           "left to right.\n"
+           "\n"
+           "For example, to change characters to alternately upper and\n"
+           "lower case,\n"
+           "\n"
+           "@example\n"
+           "(define str (string-copy \"studly\"))\n"
+           "(string-for-each-index\n"
+           "    (lambda (i)\n"
+           "      (string-set! str i\n"
+           "        ((if (even? i) char-upcase char-downcase)\n"
+           "         (string-ref str i))))\n"
+           "    str)\n"
+           "str @result{} \"StUdLy\"\n"
+           "@end example")
+#define FUNC_NAME s_scm_string_for_each_index
+{
+  size_t cstart, cend;
+  scm_t_trampoline_1 proc_tramp = scm_trampoline_1 (proc);
+
+  SCM_ASSERT (proc_tramp, proc, SCM_ARG1, FUNC_NAME);
+  MY_VALIDATE_SUBSTRING_SPEC (2, s,
+                             3, start, cstart,
+                             4, end, cend);
+
+  while (cstart < cend)
+    {
+      proc_tramp (proc, scm_from_size_t (cstart));
+      cstart++;
+    }
+
+  scm_remember_upto_here_1 (s);
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_xsubstring, "xsubstring", 2, 3, 0,
+           (SCM s, SCM from, SCM to, SCM start, SCM end),
+           "This is the @emph{extended substring} procedure that implements\n"
+           "replicated copying of a substring of some string.\n"
+           "\n"
+           "@var{s} is a string, @var{start} and @var{end} are optional\n"
+           "arguments that demarcate a substring of @var{s}, defaulting to\n"
+           "0 and the length of @var{s}.  Replicate this substring up and\n"
+           "down index space, in both the positive and negative directions.\n"
+           "@code{xsubstring} returns the substring of this string\n"
+           "beginning at index @var{from}, and ending at @var{to}, which\n"
+           "defaults to @var{from} + (@var{end} - @var{start}).")
+#define FUNC_NAME s_scm_xsubstring
+{
+  const char *cs;
+  char *p;
+  size_t cstart, cend;
+  int cfrom, cto;
+  SCM result;
+
+  MY_VALIDATE_SUBSTRING_SPEC (1, s,
+                             4, start, cstart,
+                             5, end, cend);
+
+  cfrom = scm_to_int (from);
+  if (SCM_UNBNDP (to))
+    cto = cfrom + (cend - cstart);
+  else
+    cto = scm_to_int (to);
+  if (cstart == cend && cfrom != cto)
+    SCM_MISC_ERROR ("start and end indices must not be equal", SCM_EOL);
+
+  result = scm_i_make_string (cto - cfrom, &p);
+
+  cs = scm_i_string_chars (s);
+  while (cfrom < cto)
+    {
+      size_t t = ((cfrom < 0) ? -cfrom : cfrom) % (cend - cstart);
+      if (cfrom < 0)
+       *p = cs[(cend - cstart) - t];
+      else
+       *p = cs[t];
+      cfrom++;
+      p++;
+    }
+
+  scm_remember_upto_here_1 (s);
+  return result;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_xcopy_x, "string-xcopy!", 4, 3, 0,
+           (SCM target, SCM tstart, SCM s, SCM sfrom, SCM sto, SCM start, SCM end),
+           "Exactly the same as @code{xsubstring}, but the extracted text\n"
+           "is written into the string @var{target} starting at index\n"
+           "@var{tstart}.  The operation is not defined if @code{(eq?\n"
+           "@var{target} @var{s})} or these arguments share storage -- you\n"
+           "cannot copy a string on top of itself.")
+#define FUNC_NAME s_scm_string_xcopy_x
+{
+  char *p;
+  const char *cs;
+  size_t ctstart, cstart, cend;
+  int csfrom, csto;
+  SCM dummy = SCM_UNDEFINED;
+  size_t cdummy;
+
+  MY_VALIDATE_SUBSTRING_SPEC (1, target,
+                             2, tstart, ctstart,
+                             2, dummy, cdummy);
+  MY_VALIDATE_SUBSTRING_SPEC (3, s,
+                             6, start, cstart,
+                             7, end, cend);
+  csfrom = scm_to_int (sfrom);
+  if (SCM_UNBNDP (sto))
+    csto = csfrom + (cend - cstart);
+  else
+    csto = scm_to_int (sto); 
+  if (cstart == cend && csfrom != csto)
+    SCM_MISC_ERROR ("start and end indices must not be equal", SCM_EOL);
+  SCM_ASSERT_RANGE (1, tstart,
+                   ctstart + (csto - csfrom) <= scm_i_string_length (target));
+
+  p = scm_i_string_writable_chars (target) + ctstart;
+  cs = scm_i_string_chars (s);
+  while (csfrom < csto)
+    {
+      size_t t = ((csfrom < 0) ? -csfrom : csfrom) % (cend - cstart);
+      if (csfrom < 0)
+       *p = cs[(cend - cstart) - t];
+      else
+       *p = cs[t];
+      csfrom++;
+      p++;
+    }
+  scm_i_string_stop_writing ();
+
+  scm_remember_upto_here_2 (target, s);
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_replace, "string-replace", 2, 4, 0,
+           (SCM s1, SCM s2, SCM start1, SCM end1, SCM start2, SCM end2),
+           "Return the string @var{s1}, but with the characters\n"
+           "@var{start1} @dots{} @var{end1} replaced by the characters\n"
+           "@var{start2} @dots{} @var{end2} from @var{s2}.")
+#define FUNC_NAME s_scm_string_replace
+{
+  const char *cstr1, *cstr2;
+  char *p;
+  size_t cstart1, cend1, cstart2, cend2;
+  SCM result;
+
+  MY_VALIDATE_SUBSTRING_SPEC (1, s1,
+                             3, start1, cstart1,
+                             4, end1, cend1);
+  MY_VALIDATE_SUBSTRING_SPEC (2, s2,
+                             5, start2, cstart2,
+                             6, end2, cend2);
+  result = scm_i_make_string (cstart1 + (cend2 - cstart2) +
+                             scm_i_string_length (s1) - cend1, &p);
+  cstr1 = scm_i_string_chars (s1);
+  cstr2 = scm_i_string_chars (s2);
+  memmove (p, cstr1, cstart1 * sizeof (char));
+  memmove (p + cstart1, cstr2 + cstart2, (cend2 - cstart2) * sizeof (char));
+  memmove (p + cstart1 + (cend2 - cstart2),
+          cstr1 + cend1,
+          (scm_i_string_length (s1) - cend1) * sizeof (char));
+  scm_remember_upto_here_2 (s1, s2);
+  return result;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_tokenize, "string-tokenize", 1, 3, 0,
+           (SCM s, SCM token_set, SCM start, SCM end),
+           "Split the string @var{s} into a list of substrings, where each\n"
+           "substring is a maximal non-empty contiguous sequence of\n"
+           "characters from the character set @var{token_set}, which\n"
+           "defaults to @code{char-set:graphic}.\n"
+           "If @var{start} or @var{end} indices are provided, they restrict\n"
+           "@code{string-tokenize} to operating on the indicated substring\n"
+           "of @var{s}.")
+#define FUNC_NAME s_scm_string_tokenize
+{
+  const char *cstr;
+  size_t cstart, cend;
+  SCM result = SCM_EOL;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+
+  if (SCM_UNBNDP (token_set))
+    token_set = scm_char_set_graphic;
+
+  if (SCM_CHARSETP (token_set))
+    {
+      size_t idx;
+
+      while (cstart < cend)
+       {
+         while (cstart < cend)
+           {
+             if (SCM_CHARSET_GET (token_set, cstr[cend - 1]))
+               break;
+             cend--;
+           }
+         if (cstart >= cend)
+           break;
+         idx = cend;
+         while (cstart < cend)
+           {
+             if (!SCM_CHARSET_GET (token_set, cstr[cend - 1]))
+               break;
+             cend--;
+           }
+         result = scm_cons (scm_c_substring (s, cend, idx), result);
+         cstr = scm_i_string_chars (s);
+       }
+    }
+  else
+    SCM_WRONG_TYPE_ARG (2, token_set);
+
+  scm_remember_upto_here_1 (s);
+  return result;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_split, "string-split", 2, 0, 0,
+           (SCM str, SCM chr),
+           "Split the string @var{str} into the a list of the substrings delimited\n"
+           "by appearances of the character @var{chr}.  Note that an empty substring\n"
+           "between separator characters will result in an empty string in the\n"
+           "result list.\n"
+           "\n"
+           "@lisp\n"
+           "(string-split \"root:x:0:0:root:/root:/bin/bash\" #\\:)\n"
+           "@result{}\n"
+           "(\"root\" \"x\" \"0\" \"0\" \"root\" \"/root\" \"/bin/bash\")\n"
+           "\n"
+           "(string-split \"::\" #\\:)\n"
+           "@result{}\n"
+           "(\"\" \"\" \"\")\n"
+           "\n"
+           "(string-split \"\" #\\:)\n"
+           "@result{}\n"
+           "(\"\")\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_string_split
+{
+  long idx, last_idx;
+  const char * p;
+  char ch;
+  SCM res = SCM_EOL;
+
+  SCM_VALIDATE_STRING (1, str);
+  SCM_VALIDATE_CHAR (2, chr);
+
+  idx = scm_i_string_length (str);
+  p = scm_i_string_chars (str);
+  ch = SCM_CHAR (chr);
+  while (idx >= 0)
+    {
+      last_idx = idx;
+      while (idx > 0 && p[idx - 1] != ch)
+       idx--;
+      if (idx >= 0)
+       {
+         res = scm_cons (scm_c_substring (str, idx, last_idx), res);
+         p = scm_i_string_chars (str);
+         idx--;
+       }
+    }
+  scm_remember_upto_here_1 (str);
+  return res;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_filter, "string-filter", 2, 2, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Filter the string @var{s}, retaining only those characters\n"
+           "which satisfy @var{char_pred}.\n"
+           "\n"
+           "If @var{char_pred} is a procedure, it is applied to each\n"
+           "character as a predicate, if it is a character, it is tested\n"
+           "for equality and if it is a character set, it is tested for\n"
+           "membership.")
+#define FUNC_NAME s_scm_string_filter
+{
+  const char *cstr;
+  size_t cstart, cend;
+  SCM result;
+  size_t idx;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+
+  /* The explicit loops below stripping leading and trailing non-matches
+     mean we can return a substring if those are the only deletions, making
+     string-filter as efficient as string-trim-both in that case.  */
+
+  if (SCM_CHARP (char_pred))
+    {
+      size_t count;
+      char chr;
+
+      chr = SCM_CHAR (char_pred);
+
+      /* strip leading non-matches by incrementing cstart */
+      while (cstart < cend && cstr[cstart] != chr)
+        cstart++;
+
+      /* strip trailing non-matches by decrementing cend */
+      while (cend > cstart && cstr[cend-1] != chr)
+        cend--;
+
+      /* count chars to keep */
+      count = 0;
+      for (idx = cstart; idx < cend; idx++)
+        if (cstr[idx] == chr)
+          count++;
+
+      if (count == cend - cstart)
+        {
+          /* whole of cstart to cend is to be kept, return a copy-on-write
+             substring */
+        result_substring:
+          result = scm_i_substring (s, cstart, cend);
+        }
+      else
+        result = scm_c_make_string (count, char_pred);
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      size_t count;
+
+      /* strip leading non-matches by incrementing cstart */
+      while (cstart < cend && ! SCM_CHARSET_GET (char_pred, cstr[cstart]))
+        cstart++;
+
+      /* strip trailing non-matches by decrementing cend */
+      while (cend > cstart && ! SCM_CHARSET_GET (char_pred, cstr[cend-1]))
+        cend--;
+
+      /* count chars to be kept */
+      count = 0;
+      for (idx = cstart; idx < cend; idx++)
+        if (SCM_CHARSET_GET (char_pred, cstr[idx]))
+          count++;
+
+      /* if whole of start to end kept then return substring */
+      if (count == cend - cstart)
+        goto result_substring;
+      else
+        {
+          char *dst;
+          result = scm_i_make_string (count, &dst);
+          cstr = scm_i_string_chars (s);
+
+          /* decrement "count" in this loop as well as using idx, so that if
+             another thread is simultaneously changing "s" there's no chance
+             it'll make us copy more than count characters */
+          for (idx = cstart; idx < cend && count != 0; idx++)
+            {
+              if (SCM_CHARSET_GET (char_pred, cstr[idx]))
+                {
+                  *dst++ = cstr[idx];
+                  count--;
+                }
+            }
+        }
+    }
+  else
+    {
+      SCM ls = SCM_EOL;
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+      idx = cstart;
+      while (idx < cend)
+       {
+         SCM res, ch;
+         ch = SCM_MAKE_CHAR (cstr[idx]);
+         res = pred_tramp (char_pred, ch);
+         if (scm_is_true (res))
+           ls = scm_cons (ch, ls);
+         cstr = scm_i_string_chars (s);
+         idx++;
+       }
+      result = scm_reverse_list_to_string (ls);
+    }
+
+  scm_remember_upto_here_1 (s);
+  return result;
+}
+#undef FUNC_NAME
+
+
+SCM_DEFINE (scm_string_delete, "string-delete", 2, 2, 0,
+           (SCM s, SCM char_pred, SCM start, SCM end),
+           "Delete characters satisfying @var{char_pred} from @var{s}.\n"
+           "\n"
+           "If @var{char_pred} is a procedure, it is applied to each\n"
+           "character as a predicate, if it is a character, it is tested\n"
+           "for equality and if it is a character set, it is tested for\n"
+           "membership.")
+#define FUNC_NAME s_scm_string_delete
+{
+  const char *cstr;
+  size_t cstart, cend;
+  SCM result;
+  size_t idx;
+
+  MY_VALIDATE_SUBSTRING_SPEC_COPY (1, s, cstr,
+                                  3, start, cstart,
+                                  4, end, cend);
+
+  /* The explicit loops below stripping leading and trailing matches mean we
+     can return a substring if those are the only deletions, making
+     string-delete as efficient as string-trim-both in that case.  */
+
+  if (SCM_CHARP (char_pred))
+    {
+      size_t count;
+      char chr;
+
+      chr = SCM_CHAR (char_pred);
+
+      /* strip leading matches by incrementing cstart */
+      while (cstart < cend && cstr[cstart] == chr)
+        cstart++;
+
+      /* strip trailing matches by decrementing cend */
+      while (cend > cstart && cstr[cend-1] == chr)
+        cend--;
+
+      /* count chars to be kept */
+      count = 0;
+      for (idx = cstart; idx < cend; idx++)
+        if (cstr[idx] != chr)
+          count++;
+
+      if (count == cend - cstart)
+        {
+          /* whole of cstart to cend is to be kept, return a copy-on-write
+             substring */
+        result_substring:
+          result = scm_i_substring (s, cstart, cend);
+        }
+      else
+        {
+          /* new string for retained portion */
+          char *dst;
+          result = scm_i_make_string (count, &dst);
+          cstr = scm_i_string_chars (s);
+
+          /* decrement "count" in this loop as well as using idx, so that if
+             another thread is simultaneously changing "s" there's no chance
+             it'll make us copy more than count characters */
+          for (idx = cstart; idx < cend && count != 0; idx++)
+            {
+              if (cstr[idx] != chr)
+                {
+                  *dst++ = cstr[idx];
+                  count--;
+                }
+            }
+        }
+    }
+  else if (SCM_CHARSETP (char_pred))
+    {
+      size_t count;
+
+      /* strip leading matches by incrementing cstart */
+      while (cstart < cend && SCM_CHARSET_GET (char_pred, cstr[cstart]))
+        cstart++;
+
+      /* strip trailing matches by decrementing cend */
+      while (cend > cstart && SCM_CHARSET_GET (char_pred, cstr[cend-1]))
+        cend--;
+
+      /* count chars to be kept */
+      count = 0;
+      for (idx = cstart; idx < cend; idx++)
+        if (! SCM_CHARSET_GET (char_pred, cstr[idx]))
+          count++;
+
+      if (count == cend - cstart)
+        goto result_substring;
+      else
+        {
+          /* new string for retained portion */
+          char *dst;
+          result = scm_i_make_string (count, &dst);
+          cstr = scm_i_string_chars (s);
+
+          /* decrement "count" in this loop as well as using idx, so that if
+             another thread is simultaneously changing "s" there's no chance
+             it'll make us copy more than count characters */
+          for (idx = cstart; idx < cend && count != 0; idx++)
+            {
+              if (! SCM_CHARSET_GET (char_pred, cstr[idx]))
+                {
+                  *dst++ = cstr[idx];
+                  count--;
+                }
+            }
+        }
+    }
+  else
+    {
+      SCM ls = SCM_EOL;
+      scm_t_trampoline_1 pred_tramp = scm_trampoline_1 (char_pred);
+      SCM_ASSERT (pred_tramp, char_pred, SCM_ARG2, FUNC_NAME);
+
+      idx = cstart;
+      while (idx < cend)
+       {
+         SCM res, ch = SCM_MAKE_CHAR (cstr[idx]);
+         res = pred_tramp (char_pred, ch);
+         if (scm_is_false (res))
+           ls = scm_cons (ch, ls);
+         cstr = scm_i_string_chars (s);
+         idx++;
+       }
+      result = scm_reverse_list_to_string (ls);
+    }
+
+  scm_remember_upto_here_1 (s);
+  return result;
+}
+#undef FUNC_NAME
+
+void
+scm_init_srfi_13 (void)
+{
+#include "libguile/srfi-13.x"
+}
+
+/* End of srfi-13.c.  */