WORD_LIST *
split_at_delims (string, slen, delims, sentinel, flags, nwp, cwp)
     char *string;
     int slen;
     char *delims;
     int sentinel, flags;
     int *nwp, *cwp;
{
  int ts, te, i, nw, cw, ifs_split, dflags;
  char *token, *d, *d2;
  WORD_LIST *ret, *tl;

  if (string == 0 || *string == '\0')
    {
      if (nwp)
	*nwp = 0;
      if (cwp)
	*cwp = 0;	
      return ((WORD_LIST *)NULL);
    }

  d = (delims == 0) ? ifs_value : delims;
  ifs_split = delims == 0;

  /* Make d2 the non-whitespace characters in delims */
  d2 = 0;
  if (delims)
    {
      size_t slength;
#if defined (HANDLE_MULTIBYTE)
      size_t mblength = 1;
#endif
      DECLARE_MBSTATE;

      slength = strlen (delims);
      d2 = (char *)xmalloc (slength + 1);
      i = ts = 0;
      while (delims[i])
	{
#if defined (HANDLE_MULTIBYTE)
	  mbstate_t state_bak;
	  state_bak = state;
	  mblength = MBRLEN (delims + i, slength, &state);
	  if (MB_INVALIDCH (mblength))
	    state = state_bak;
	  else if (mblength > 1)
	    {
	      memcpy (d2 + ts, delims + i, mblength);
	      ts += mblength;
	      i += mblength;
	      slength -= mblength;
	      continue;
	    }
#endif
	  if (whitespace (delims[i]) == 0)
	    d2[ts++] = delims[i];

	  i++;
	  slength--;
	}
      d2[ts] = '\0';
    }

  ret = (WORD_LIST *)NULL;

  /* Remove sequences of whitespace characters at the start of the string, as
     long as those characters are delimiters. */
  for (i = 0; member (string[i], d) && spctabnl (string[i]); i++)
    ;
  if (string[i] == '\0')
    {
      FREE (d2);
      return (ret);
    }

  ts = i;
  nw = 0;
  cw = -1;
  dflags = flags|SD_NOJMP;
  while (1)
    {
      te = skip_to_delim (string, ts, d, dflags);

      /* If we have a non-whitespace delimiter character, use it to make a
	 separate field.  This is just about what $IFS splitting does and
	 is closer to the behavior of the shell parser. */
      if (ts == te && d2 && member (string[ts], d2))
	{
	  te = ts + 1;
	  /* If we're using IFS splitting, the non-whitespace delimiter char
	     and any additional IFS whitespace delimits a field. */
	  if (ifs_split)
	    while (member (string[te], d) && spctabnl (string[te]))
	      te++;
	  else
	    while (member (string[te], d2))
	      te++;
	}

      token = substring (string, ts, te);

      ret = add_string_to_list (token, ret);
      free (token);
      nw++;

      if (sentinel >= ts && sentinel <= te)
	cw = nw;

      /* If the cursor is at whitespace just before word start, set the
	 sentinel word to the current word. */
      if (cwp && cw == -1 && sentinel == ts-1)
	cw = nw;

      /* If the cursor is at whitespace between two words, make a new, empty
	 word, add it before (well, after, since the list is in reverse order)
	 the word we just added, and set the current word to that one. */
      if (cwp && cw == -1 && sentinel < ts)
	{
	  tl = make_word_list (make_word (""), ret->next);
	  ret->next = tl;
	  cw = nw;
	  nw++;
	}

      if (string[te] == 0)
	break;

      i = te;
      while (member (string[i], d) && (ifs_split || spctabnl(string[i])))
	i++;

      if (string[i])
	ts = i;
      else
	break;
    }

  /* Special case for SENTINEL at the end of STRING.  If we haven't found
     the word containing SENTINEL yet, and the index we're looking for is at
     the end of STRING (or past the end of the previously-found token,
     possible if the end of the line is composed solely of IFS whitespace)
     add an additional null argument and set the current word pointer to that. */
  if (cwp && cw == -1 && (sentinel >= slen || sentinel >= te))
    {
      if (whitespace (string[sentinel - 1]))
	{
	  token = "";
	  ret = add_string_to_list (token, ret);
	  nw++;
	}
      cw = nw;
    }

  if (nwp)
    *nwp = nw;
  if (cwp)
    *cwp = cw;

  FREE (d2);

  return (REVERSE_LIST (ret, WORD_LIST *));