/*   ncbistr.c
* ===========================================================================
*
*                            PUBLIC DOMAIN NOTICE                          
*               National Center for Biotechnology Information
*                                                                          
*  This software/database is a "United States Government Work" under the   
*  terms of the United States Copyright Act.  It was written as part of    
*  the author's official duties as a United States Government employee and 
*  thus cannot be copyrighted.  This software/database is freely available 
*  to the public for use. The National Library of Medicine and the U.S.    
*  Government have not placed any restriction on its use or reproduction.  
*                                                                          
*  Although all reasonable efforts have been taken to ensure the accuracy  
*  and reliability of the software and data, the NLM and the U.S.          
*  Government do not and cannot warrant the performance or results that    
*  may be obtained by using this software or data. The NLM and the U.S.    
*  Government disclaim all warranties, express or implied, including       
*  warranties of performance, merchantability or fitness for any particular
*  purpose.                                                                
*                                                                          
*  Please cite the author in any work or product based on this material.   
*
* ===========================================================================
*
* File Name:  ncbistr.c
*
* Author:  Gish, Kans, Ostell, Schuler, Brylawski
*
* Version Creation Date:   3/4/91
*
* $Revision: 5.0 $
*
* File Description: 
*   	portable string routines
*
* Modifications:  
* --------------------------------------------------------------------------
* Date     Name        Description of modification
* -------  ----------  -----------------------------------------------------
* 3/4/91   Kans        Stricter typecasting for GNU C and C++.
* 09-19-91 Schuler     Changed all types expressing sizes to size_t.
* 09-19-91 Schuler     Changed return type for compare functions to int.
* 09-19-91 Schuler     Changed all functions to _cdecl calling convention.
* 09-19-91 Schuler     Where possible, NCBI functions call the actual ANSI
*                       functions after checking for NULL pointers.
* 09-19-91 Schuler     Debug-class error posted on any NULL argument.
* 09-19-91 Schuler     StringSave() calls MemGet() instead of MemNew().
* 09-19-91 Schuler     StringSave(NULL) returns NULL.
* 10-17-91 Schuler     Removed ErrPost() calls on NULL arguments.
* 10-17-91 Schuler     Added Nlm_StringCnt(),Nlm_StringStr(),Nlm_StringTok()
* 11-18-91 Schuler     Added more ANSI-style functions
* 04-15-93 Schuler     Changed _cdecl to LIBCALL
* 05-27-93 Schuler     Added const qualifiers to match ANSI cognates
* 06-14-94 Schuler     Added StrUpper() and StrLower() functions
* 03-08-95 Kans        Added StringSearch and StringISearch
* 12-27-95 Brylawski   Added a variety of functions, including search-and-
*                      replace functions.
*
* $Log: ncbistr.c,v $
 * Revision 5.0  1996/05/28  13:18:57  ostell
 * Set to revision 5.0
 *
 * Revision 4.12  1996/05/22  18:04:14  kans
 * changed nulls to '\0' in new string functions
 *
 * Revision 4.11  1996/05/22  14:46:19  brandon
 * Fixed SkipToString, SkipPastString to work with short strings
 *
 * Revision 4.10  1996/05/07  13:22:37  kans
 * more protection for stringsearch
 *
 * Revision 4.9  1996/05/06  15:07:58  kans
 * fixed StringISearch to set d [] based on TO_UPPER, not to crash if nonASCII
 *
 * Revision 4.8  1996/03/14  03:42:44  epstein
 * change String variables to theString to work around SGI4 problem
 *
 * Revision 4.7  1996/01/05  02:29:37  ostell
 * aprovided return value for TruncateStringCopy()
 *
 * Revision 4.6  1996/01/03  21:04:46  epstein
 * modify StringSubString() API and add other new functions, per Brandon
 *
 * Revision 4.5  1996/01/02  14:17:32  ostell
 * added a number of Brandons functions
 *
 * Revision 4.4  1995/12/28  15:41:56  epstein
 * added Brylawskin to revision history and author list
 *
 * Revision 4.3  1995/12/27  20:53:48  epstein
 * add Brandon's string-management functions
 *
 * Revision 4.2  1995/10/28  15:03:20  ostell
 * added casts to quiet DOS compile warnings
 *
 * Revision 4.1  1995/10/16  13:43:29  epstein
 * fix brain-dmanaged string-compare logic to handle null strings correctly
 *
 * Revision 4.0  1995/07/26  13:46:50  ostell
 * force revision to 4.0
 *
 * Revision 2.12  1995/07/18  19:56:10  tatiana
 * add Nlm_LabelCopyNext()
 *
 * Revision 2.11  1995/05/30  13:19:37  kans
 * fixed StringSearch algorithm - check until i <= strLen, not just < strLen
 *
 * Revision 2.10  1995/05/15  18:45:58  ostell
 * added Log line
 *
*
* ==========================================================================
*/
 
#include <ncbi.h>
#include <ncbiwin.h>

/* ClearDestString clears the destination string if the source is NULL. */
static Nlm_CharPtr NEAR Nlm_ClearDestString (Nlm_CharPtr to, size_t max)
{
  if (to != NULL && max > 0) {
    Nlm_MemSet (to, 0, max);
    *to = '\0';
  }
  return to;
}

size_t LIBCALL  Nlm_StringLen (const char *str)
{
	return str ? StrLen (str) : 0;
}

Nlm_CharPtr LIBCALL  Nlm_StringCpy (char FAR *to, const char FAR *from)
{
	return (to && from) ? StrCpy (to, from) : Nlm_ClearDestString (to, 1);
}

Nlm_CharPtr LIBCALL  Nlm_StringNCpy (char FAR *to, const char FAR *from, size_t max)
{
	return (to && from) ? StrNCpy (to, from, max) : Nlm_ClearDestString (to, max);
}

Nlm_CharPtr LIBCALL  Nlm_StringNCpy_0 (char FAR *to, const char FAR *from, size_t max)
{
	if (to != NULL && max > 0)
		to[0] = '\0';

	if (from != NULL)
		StrNCat(to, from, max - 1);

	return to;
}


Nlm_CharPtr LIBCALL  Nlm_StringCat (char FAR *to, const char FAR *from)
{
	return (to && from) ? StrCat (to, from) : to;
}

Nlm_CharPtr LIBCALL  Nlm_StringNCat (char FAR *to, const char FAR *from, size_t max)
{       
    return (to && from) ? StrNCat (to, from, max) : to;
}

int LIBCALL  Nlm_StringCmp (const char FAR *a, const char FAR *b)
{
	return (a && b) ? StrCmp(a, b) : (a ? 1 : (b ? -1 : 0));
}

int LIBCALL  Nlm_StringNCmp (const char FAR *a, const char FAR *b, size_t max)
{
	return (a && b) ? StrNCmp(a, b, max) : (a ? 1 : (b ? -1 : 0));
}

int LIBCALL  Nlm_StringICmp (const char FAR *a, const char FAR *b)
{
	return (a && b) ? Nlm_StrICmp(a, b) : (a ? 1 : (b ? -1 : 0));
}

int LIBCALL  Nlm_StringNICmp (const char FAR *a, const char FAR *b, size_t max)
{
	return (a && b) ? Nlm_StrNICmp(a, b, max) : (a ? 1 : (b ? -1 : 0));
}

Nlm_CharPtr LIBCALL  Nlm_StringChr (const char FAR *str, int chr)
{
	return str ? Nlm_StrChr(str,chr) : NULL;
}

Nlm_CharPtr LIBCALL  Nlm_StringRChr (const char FAR *str, int chr)
{
	return str ? Nlm_StrRChr(str,chr) : NULL;
}

size_t LIBCALL  Nlm_StringSpn (const char FAR *a, const char FAR *b)
{
	return (a && b) ? Nlm_StrSpn (a, b) : 0;
}

size_t LIBCALL  Nlm_StringCSpn (const char FAR *a, const char FAR *b)
{
	return (a && b) ? Nlm_StrCSpn (a, b) : 0;
}

Nlm_CharPtr LIBCALL  Nlm_StringPBrk (const char FAR *a, const char FAR *b)
{
	return (a && b) ? Nlm_StrPBrk (a, b) : NULL;
}

Nlm_CharPtr LIBCALL  Nlm_StringStr (const char FAR *str1, const char FAR *str2)
{
	return (str1 && str2) ? Nlm_StrStr(str1,str2) : NULL;
}

Nlm_CharPtr LIBCALL  Nlm_StringTok (char FAR *str1, const char FAR *str2)
{
    return str2 ? Nlm_StrTok(str1,str2) : NULL;
}

Nlm_CharPtr LIBCALL  Nlm_StringMove (char FAR *to, const char FAR *from)
{
    return (to && from) ? Nlm_StrMove (to, from) : to;
}

Nlm_CharPtr LIBCALL  Nlm_StringSave (const char FAR *from)
{
    return from ? Nlm_StrSave (from) : NULL;
}

Nlm_CharPtr LIBCALL  Nlm_StringSaveNoNull (const char FAR *from)
{
	if (from == NULL) return NULL;
	if (*from == '\0') return NULL;
	return Nlm_StrSave (from);
}

size_t LIBCALL  Nlm_StringCnt (const char FAR *str, const char FAR *list)
{
    return (str && list) ? Nlm_StrCnt(str,list) : 0;
}

char * LIBCALL Nlm_StringUpper (char *string)
{
	return (string == NULL) ? NULL : StrUpper(string);
}

char * LIBCALL Nlm_StringLower (char *string)
{
	return (string == NULL) ? NULL : StrLower(string);
}





int LIBCALL  Nlm_StrICmp (const char FAR *a, const char FAR *b)
{
	int diff, done;

    if (a == b)   return 0;

	done = 0;
	while (! done)
	{
		diff = TO_UPPER(*a) - TO_UPPER(*b);
		if (diff)
			return (Nlm_Int2) diff;
		if (*a == '\0')
			done = 1;
		else
		{
			a++; b++;
		}
	}
	return 0;
}

int LIBCALL  Nlm_StrNICmp (const char FAR *a, const char FAR *b, size_t max)
{
	int diff, done;
	
    if (a == b)   return 0;

	done = 0;
	while (! done)
	{
		diff = TO_UPPER(*a) - TO_UPPER(*b);
		if (diff)
			return (Nlm_Int2) diff;
		if (*a == '\0')
			done = 1;
		else
		{
			a++; b++; max--;
			if (! max)
				done = 1;
		}
	}
	return 0;
}

Nlm_CharPtr LIBCALL  Nlm_StrSave (const char FAR *from)
{
	size_t len;
	Nlm_CharPtr to;

	len = Nlm_StringLen(from);
	if ((to = (Nlm_CharPtr) Nlm_MemGet(len +1, FALSE)) != NULL) {
    	Nlm_MemCpy (to, from, len +1);
	}
	return to;
}

Nlm_CharPtr LIBCALL  Nlm_StrMove (char FAR *to, const char FAR *from)
{
	while (*from != '\0')
	{
		*to = *from;
		to++; from++;
	}
	*to = '\0';
	return to;
}

size_t LIBCALL  Nlm_StrCnt (const char FAR *s, const char FAR *list)
{
	size_t	cmap[1<<CHAR_BIT];
	Nlm_Byte	c;
	size_t	u, cnt;
	const Nlm_Byte *bs = (const Nlm_Byte*)s;
	const Nlm_Byte *blist = (const Nlm_Byte*)list;

	if (s == NULL || list == NULL)
		return 0;

	for (u = 0; u < DIM(cmap); ++u)
		cmap[u] = 0;
	while (*blist != '\0')
		++cmap[*blist++];

	blist = (Nlm_BytePtr)cmap;

	cnt = 0;
	while ((c = *bs++) != '\0')
		cnt += blist[c];

	return cnt;
}

#ifndef COMP_MSC
char * LIBCALL Nlm_StrUpper (char *string)
{
	register char *p = string;
	ASSERT(string != NULL);
	while (*p)
	{
		if (isalpha(*p))
			*p = (char)toupper(*p);
		++p;
	}
	return string;
}
#endif

#ifndef COMP_MSC
char * LIBCALL Nlm_StrLower (char *string)
{
	register char *p = string;
	ASSERT(string != NULL);
	while (*p)
	{
		if (isalpha(*p))
			*p = (char)tolower(*p);
		++p;
	}
	return string;
}
#endif


/*  -------------------- MeshStringICmp() --------------------------------
 *  MeshStringICmp compares strings where / takes precedence to space.
 */

Nlm_Int2 LIBCALL Nlm_MeshStringICmp (const char FAR *str1, const char FAR *str2)
{
	Nlm_Char  ch1, ch2;

	if (str1 == NULL)
	{
		if (str2 == NULL)
			return (Nlm_Int2)0;
		else
			return (Nlm_Int2)1;
	}
	else if (str2 == NULL)
		return (Nlm_Int2)-1;

	while ((*str1 >= ' ') && (*str2 >= ' ') && (TO_LOWER(*str1) == TO_LOWER(*str2)))
	{
		str1++; str2++;
	}

	ch1 = *str1;
	ch2 = *str2;
	if ((ch1 < ' ') && (ch2 < ' '))
		return (Nlm_Int2)0;
	else if (ch1 < ' ')
		return (Nlm_Int2)-1;
	else if (ch2 < ' ')
		return (Nlm_Int2)1;

	if (ch1 == '/')
	  ch1 = '\31';
	if (ch2 == '/')
	  ch2 = '\31';

	if (TO_LOWER (ch1) > TO_LOWER (ch2))
	  return (Nlm_Int2)1;
	else if (TO_LOWER (ch1) < TO_LOWER (ch2))
	  return (Nlm_Int2)(-1);
	else
	  return (Nlm_Int2)0;
}

/* StringSearch and StringISearch use the Boyer-Moore algorithm, as described
   in Niklaus Wirth, Algorithms and Data Structures, Prentice- Hall, Inc.,
   Englewood Cliffs, NJ., 1986, p. 69.  The original had an error, where
   UNTIL (j < 0) OR (p[j] # s[i]) should be UNTIL (j < 0) OR (p[j] # s[k]). */

static Nlm_CharPtr LIBCALL Nlm_FindSubString (const char FAR *str, const char FAR *sub,
                                              Nlm_Boolean caseCounts)

{
  int      ch;
  int      d [256];
  int      i;
  int      j;
  int      k;
  size_t   strLen;
  size_t   subLen;

  if (sub != NULL && sub [0] != '\0' && str != NULL && str [0] != '\0') {
    strLen = Nlm_StringLen (str);
    subLen = Nlm_StringLen (sub);
    if (subLen <= strLen) {
      for (ch = 0; ch < 256; ch++) {
        d [ch] = subLen;
      }
      for (j = 0; j < (int)(subLen - 1); j++) {
        ch = (int) (caseCounts ? sub [j] : TO_UPPER (sub [j]));
        if (ch >= 0 && ch <= 255) {
          d [ch] = subLen - j - 1;
        }
     }
      i = subLen;
      do {
        j = subLen;
        k = i;
        do {
          k--;
          j--;
        } while (j >= 0 &&
                 (caseCounts ? sub [j] : TO_UPPER (sub [j])) ==
                 (caseCounts ? str [k] : TO_UPPER (str [k])));
        if (j >= 0) {
          ch = (int) (caseCounts ? str [i - 1] : TO_UPPER (str [i - 1]));
          if (ch >= 0 && ch <= 255) {
            i += d [ch];
          } else {
            i++;
          }
        }
      } while (j >= 0 && i <= (int) strLen);
      if (j < 0) {
        i -= subLen;
        return (Nlm_CharPtr) (str + i);
      }
    }
  }
  return NULL;
}

Nlm_CharPtr LIBCALL Nlm_StringSearch (const char FAR *str, const char FAR *sub)

{
  return Nlm_FindSubString (str, sub, TRUE);
}

Nlm_CharPtr LIBCALL Nlm_StringISearch (const char FAR *str, const char FAR *sub)

{
  return Nlm_FindSubString (str, sub, FALSE);
}

/*****************************************************************************
*
*   LabelCopy (to, from, buflen)
*   	Copies the string "from" into "to" for up to "buflen" chars
*   	if "from" is longer than buflen, makes the last character '>'
*   	always puts one '\0' to terminate the string in to
*   	to MUST be one character longer than buflen to leave room for the
*   		last '\0' if from = buflen.
*       returns number of characters transferred to "to"
*
*****************************************************************************/
Nlm_Int2 LIBCALL Nlm_LabelCopy (Nlm_CharPtr to, Nlm_CharPtr from, Nlm_Int2 buflen)
{
	Nlm_Int2 len;

	if ((to == NULL) || (from == NULL) || (buflen < 0)) return 0;
	
	if (buflen == 0)         /* this is a sign of multiple writes */
	{
		*(to-1) = '>';
		return 0;
	}
	
	len = buflen;

	while ((*from != '\0') && (buflen))
	{
		*to = *from;
		from++; to++; buflen--;
	}

	if (*from != '\0')
	{
		*(to - 1) = '>';
	}

	*to = '\0';      /* buffer is bufferlen+1 */
	return (len - buflen);
}

void LIBCALL Nlm_LabelCopyNext(Nlm_CharPtr PNTR to, Nlm_CharPtr from, Nlm_Int2 PNTR buflen)
{
	Nlm_Int2 diff;

	diff = Nlm_LabelCopy(*to, from, *buflen);
	*buflen -= diff; *to += diff;
	
}

/*****************************************************************************
*
*   LabelCopyExtra (to, from, buflen, prefix, suffix)
*   	Copies the string "from" into "to" for up to "buflen" chars
*   	if all together are longer than buflen, makes the last character '>'
*   	always puts one '\0' to terminate the string in to
*   	to MUST be one character longer than buflen to leave room for the
*   		last '\0' if from = buflen.
*       returns number of characters transferred to "to"
*
*   	if not NULL, puts prefix before from and suffix after from
*   		both contained within buflen
*  
*
*****************************************************************************/
Nlm_Int2 LIBCALL Nlm_LabelCopyExtra (Nlm_CharPtr to, Nlm_CharPtr from, Nlm_Int2 buflen, Nlm_CharPtr prefix, Nlm_CharPtr suffix)
{
	Nlm_Int2 len, diff;

	if ((to == NULL) || (buflen < 1) || (from == NULL)) return 0;

	len = buflen;
	diff = Nlm_LabelCopy(to, prefix, buflen);
	buflen -= diff; to += diff;

	diff = Nlm_LabelCopy(to, from, buflen);
	buflen -= diff; to += diff;

	diff = Nlm_LabelCopy(to, suffix, buflen);
	buflen -= diff;

	return (len-buflen);
}

#define NEWLINE '\n'
#define SPACE ' '

Nlm_CharPtr LIBCALL StrCpyPtr (char FAR *Dest, char FAR *Start, char FAR *Stop)
/* copies the string from Start (inclusive) to Stop (exclusive) to 
string Dest. Start and Stop MUST point to the same string! 
*/
{
  Nlm_CharPtr To = Dest;
  while ( *Start && ( Start < Stop ) )
    *To++ = *Start++;
  *To = NULLB;
  
  return(Dest);
}

Nlm_CharPtr LIBCALL StrDupPtr(char FAR *Start,char FAR *Stop)
/* copies the string from Start (inclusive) to Stop (exclusive) to 
   a new string and returns its pointer. 
   Start and Stop MUST point to the same string! 
*/
{
  Nlm_CharPtr Dest, DestPtr;
  
  DestPtr = Dest = (Nlm_CharPtr)Nlm_MemGet((Stop - Start + 1),MGET_ERRPOST);
  
  while ( *Start && ( Start < Stop ) )
    *DestPtr++ = *Start++;
  *DestPtr = NULLB;
  return(Dest);
}

Nlm_CharPtr LIBCALL SkipSpaces(char FAR *Line)
/* returns the next non-whitespace position in the line. */
{
  while( (*Line != NULLB) && isspace(*Line) )
    Line++;
  return(Line);
}


Nlm_CharPtr LIBCALL SkipToSpace(char FAR *theString)
/* returns a pointer to the leftmost whitespace character in theString, 
   or to the trailing NULL if no whitespace is found. */
{
  while (*theString && ( ! isspace(*theString) ))
    theString++;
  return(theString);
}


Nlm_CharPtr LIBCALL SkipChar(char FAR *theString,char Char)
/* returns a pointer to the next non-Char character in theString. */
{
  while (*theString && ( *theString == Char ) )
    theString++;
  return(theString);
}


Nlm_CharPtr LIBCALL SkipToChar(char FAR *theString,char Char)
/* returns a pointer to leftmost instance of Char in theString, or to
   the trailing NULL if none found. */
{
  while (*theString && ( *theString != Char ) )
    theString++;
  return(theString);
}


Nlm_CharPtr LIBCALL SkipPastChar(char FAR *theString,char Char)
/* returns a pointer to the next character after the leftmost instance
   of Char in theString, or to the trailing NULL if none found. */
{
  while (*theString && ( *theString != Char ) )
    theString++;

  if (*theString)
    return(theString+1);
  else
    return(theString);
}

Nlm_CharPtr LIBCALL SkipToString(char FAR *theString,char FAR *Find)
/* returns a pointer to leftmost instance of Find in theString, or to
   the trailing NULL if none found. */
{
  char *FindPtr,*theStringPtr;
  
  while (*theString)
    {
      FindPtr = Find;
      theStringPtr = theString;
      while (*FindPtr && ( *FindPtr == *theStringPtr))
	{
	  FindPtr++;
	  theStringPtr++;
	}
      
      if (*FindPtr == '\0')
	return(theString);
      else
	theString++;
    }
  
  return(theString);
}


Nlm_CharPtr LIBCALL NoCaseSkipToString(char FAR *theString,char FAR *Find)
/* returns a pointer to leftmost instance of Find in theString, 
   ignoring case, or to the trailing NULL if none found. */
{
  char *FindPtr,*theStringPtr;
  
  while (*theString)
    {
      FindPtr = Find;
      theStringPtr = theString;
      while (*FindPtr && (toupper(*FindPtr) == toupper(*theStringPtr)))
	{
	  FindPtr++;
	  theStringPtr++;
	}
      
      if (*FindPtr == '\0')
	return(theString);
      else
	theString++;
    }
  
  return(theString);
}


Nlm_CharPtr LIBCALL SkipPastString(char FAR *theString,char FAR *Find)
/* returns a pointer to the next character after the leftmost
   instance of Find in theString, or to the trailing NULL if none found. */
{
  Nlm_CharPtr Ptr = SkipToString(theString,Find);

  if (*Ptr == '\0')
    return (Ptr);

  return (Ptr + Nlm_StringLen(Find));
}

Nlm_CharPtr LIBCALL NoCaseSkipPastString(char FAR *theString,char FAR *Find)
/* returns a pointer to the next character after the leftmost
   instance of Find in theString, ignoring case,
   or to the trailing NULL if none found. */
{
  Nlm_CharPtr Ptr = SkipToString(theString,Find);

  if (*Ptr == '\0')
    return (Ptr);

  return (Ptr + Nlm_StringLen(Find));
}

Nlm_CharPtr LIBCALL SkipSet(char FAR *theString,char FAR *CharSet)
/* returns a pointer to the next character in theString that is
   not in CharSet. */
{
  Nlm_CharPtr CharSetPtr;
  while (*theString)
    {
      CharSetPtr = CharSet;
      while ( *CharSetPtr && *theString != *CharSetPtr )
        CharSetPtr++;
      if ( ! *CharSetPtr )
        return (theString);
      theString++;
    }
  return(theString);
}

Nlm_CharPtr LIBCALL SkipToSet(char FAR *theString,char FAR *CharSet)
/* returns a pointer to leftmost instance of any char in string
   CharSet in theString, or to the trailing NULL if none found. */
{
  Nlm_CharPtr CharSetPtr;
  while (*theString)
    {
      CharSetPtr = CharSet;
      while ( *CharSetPtr && (*theString != *CharSetPtr) )
        CharSetPtr++;
      if ( *CharSetPtr )
        return (theString);
      theString++;
    }
  return(theString);
}


Nlm_Boolean LIBCALL StringSub(char FAR *theString, char Find, char Replace)
/* replaces all instances of the character Find in the given theString with
   the character Replace. It returns TRUE if any characters were 
   replaced, else FALSE.   */
{
  Nlm_Boolean Replaced = FALSE;
  while ( *theString != NULLB )
    {
      if ( *theString == Find )
        {
          *theString = Replace;
          Replaced = TRUE;
        }
      theString++;
    }
  return(Replaced);
}

Nlm_Boolean LIBCALL StringSubSet(char FAR *theString,char FAR *FindSet, char Replace)
/* replaces all instances of any character in string FindSet found in
   theString with the character Replace.  It returns TRUE if any
   characters were replaced, else FALSE. */
{
  Nlm_CharPtr FindPtr;
  Nlm_Boolean Replaced = FALSE;

  while ( *theString != NULLB )
    {
      FindPtr = FindSet;
      while ( *FindPtr != NULLB )
        {
          if (*theString == *FindPtr )
            {
              *theString = Replace;
              Replaced = TRUE;
            }
          FindPtr++;
        }
      theString++;
    }
  return(Replaced);
}

Nlm_Boolean LIBCALL StringSubString(char FAR *theString, char FAR *Find, char FAR *Replace, Nlm_Int4 MaxLength)
/* replaces all non-overlapping instances of the string Find in the
   string theString with the string Replace. The strings do not have to be the 
   same size. The new string is truncated at MaxLength characters
   Including the final NULL). If MaxLength is zero, the string's current
   length is presumed to be the maximum.
   It returns TRUE if any strings were replaced, else FALSE.
   */
{
  Nlm_CharPtr FindPtr,ComparePtr,StringPtr,NewString, NewStringPtr;
  Nlm_Int4 SpaceNeeded,Len;
  Nlm_Boolean Replaced = FALSE;

  if (*Find == NULLB)
    return(FALSE);

  Len = Nlm_StringLen(theString);
  SpaceNeeded = MAX( (Nlm_Int4)((Len * Nlm_StringLen(Replace) 
				 * sizeof(Nlm_Char) )
				/ Nlm_StringLen(Find) + 1),Len) + 1;

  NewStringPtr = NewString = Nlm_MemGet(SpaceNeeded,MGET_ERRPOST);

  StringPtr = theString;
  while (*StringPtr != NULLB)
    {
      FindPtr = Find;
      ComparePtr = StringPtr;
      while ( (*FindPtr != NULLB) && (*FindPtr == *ComparePtr) )
	{
	  FindPtr++;
	  ComparePtr++;
	}
      
      /* if we found the entire string, replace it. */
      if (*FindPtr == NULLB)
	{
	  NewStringPtr = StringMove(NewStringPtr,Replace);
	  StringPtr = ComparePtr;
	  Replaced = TRUE;
	}
      else
	/* otherwise, move on to the next character. */
	*NewStringPtr++ = *StringPtr++;
    }
  *NewStringPtr = NULLB;

  if (MaxLength <= 0)
    MaxLength = strlen(theString) + 1;

  /* Truncate the string, if necessary.*/
  if ((Nlm_Int4)strlen(NewString) >= MaxLength - 1)
    {
      NewString[MaxLength-1] = NULLB;
    }
    
  Nlm_StringCpy(theString,NewString);
  Nlm_MemFree(NewString);

  return(Replaced);
}


Nlm_CharPtr LIBCALL StringEnd(char FAR *theString)
/* This returns a pointer to the terminating null of the given theString.
 */
{
  while (*theString != NULLB)
    theString++;
  return(theString);
}



Nlm_Int4 LIBCALL CountChar(char FAR *theString, char Char)
/* returns the number of times a given character appears in the given
   theString. */
{
  Nlm_Int4 CharCount = 0;

  while(*theString)
    if (*theString++ == Char)
      CharCount++;

  return(CharCount);
}

Nlm_Int4 LIBCALL CountStrings(char FAR *theString, char FAR *Find)
/*  This returns the number of non-overlapping instances of Find
    in theString. */
{
  Nlm_Int4 count = 0;
  Nlm_Int4 Len = Nlm_StringLen(Find);
  Nlm_CharPtr Ptr = theString;

  for(;;)
    {
      Ptr = SkipToString(Ptr,Find);
      if (*Ptr == NULLB)
        break;

      count++;
      Ptr += Len;
    }

  return count;
}

Nlm_Int4 LIBCALL CountSet(char FAR *theString, char FAR *Set)
/* returns the number of times any one of a given set of characters 
   appears in the given theString. */
{
  Nlm_Int4 CharCount = 0;
  Nlm_CharPtr SetPtr;

  while(*theString)
    {
      SetPtr = Set;

      while(*SetPtr)
	{
	  if (*theString == *SetPtr)
	    {
	      CharCount++;
	      break;
	    }
	  SetPtr++;
	}

      theString++;
    }

  return(CharCount);
}


Nlm_CharPtr LIBCALL StripSpaces(char FAR *Line)
/* returns a pointer to the next nonwhitespace character in the string
and also removes trailing whitespaces. */
{
  Nlm_CharPtr Ptr;

  Line = SkipSpaces(Line);
  if (*Line != NULLB)
    {
      Ptr = StringEnd(Line) - 1;
      while ( (Ptr > Line) && isspace(*Ptr) )
        Ptr--;
      *(Ptr+1) = NULLB;
    }

  return(Line);
}

void LIBCALL CleanSpaces(char FAR *Line)
/* This in-place deletes all leading and trailing whitespace and replaces
   all instances of one or more whitespace characters with one space,
   or one newline if the whitespace contained a newline.
   */
{
  Nlm_Boolean HasNewLine;
  Nlm_CharPtr LinePtr = SkipSpaces(Line);

  while (*LinePtr )
    {
      while ( *LinePtr &&  ! isspace (*LinePtr) )
        *Line++ = *LinePtr++;

      HasNewLine = FALSE;
      while ( isspace(*LinePtr) )
        {
          if (*LinePtr == NEWLINE)
            HasNewLine = TRUE;
          LinePtr++;
        }

      if (HasNewLine)
        *Line++ = NEWLINE;
      else
        if (*LinePtr)
          *Line++ = SPACE;
    }
  *Line = NULLB;
}


Nlm_Int4 LIBCALL StringDiff(char FAR *This, char FAR *That)
/* This returns the character offset where the strings differ, or -1 if
   the strings are the same. */
{
  Nlm_Int4 Diff = 0;

  while (*This && (*This == *That) )
    {
      Diff++;
      This++;
      That++;
    }

  if (*This || *That)
    return(Diff);
  else
    return (-1);
}

Nlm_Int4 LIBCALL StringDiffNum(char FAR *This, char FAR *That, Nlm_Int4 NumChars)
/* returns the character offset where the strings differ, examining only
   the first NumChars Characters. returns -1 if the two substrings 
   examined are equivalent. */
{
  Nlm_Int4 Diff = 0;

  while ((NumChars > 0) && *This &&  (*This == *That) )
    {
      This++;
      That++;
      NumChars--;
      Diff++;
    }

  if ( NumChars && (*This || *That) )
    return(Diff);
  else 
    return(-1);
}

void LIBCALL TruncateString(char FAR *theString, Nlm_Int4 Length)
/* truncates a string to fit into an array of Length characters, 
   including the trailing NULL. */
{
  if((Nlm_Int4)strlen(theString) >= Length - 1)
    theString [Length-1] = NULLB;
}

Nlm_CharPtr LIBCALL TruncateStringCopy(char FAR *theString, Nlm_Int4 Length)
/* Returns a new string consisting of at most the first length-1 
   characters of theString. */
{
  Nlm_CharPtr NewString = (Nlm_CharPtr)MemNew(sizeof(char) * Length);

  StrNCpy(NewString,theString,Length-1);
  NewString[Length-1] = NULLB;
  return NewString;
}


Nlm_Int4 LIBCALL BreakString(char FAR *theString, Nlm_CharPtr PNTR Words)
/* Breaks up a string at each occurrence of one or more spaces, placing
   each substring obtained into the array Words and returning the 
   number of substrings obtained. 
   */
{
  Nlm_CharPtr Start, Stop, *WordPtr;

  Start = SkipSpaces(theString);
  WordPtr = Words;

  while (*Start)
    {
      Stop = SkipToSpace(Start);
      StrCpyPtr(*WordPtr++,Start,Stop);
      Start = SkipSpaces(Stop);
    }

  return((Nlm_Int4) (WordPtr - Words) );
}

void LIBCALL DeleteChar(char FAR *theString,char Delete)
/* removes all instances of the character Delete from the theString. */
{
  Nlm_CharPtr StringPtr = theString;

  while(*StringPtr)
    {
      if (*StringPtr !=  Delete)
	*theString++ = *StringPtr++;
      else
	StringPtr++;
    }

  *theString = NULLB;
}


