/*===========================================================================
  Copyright (C) 1995-2009 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program 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 General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++  
.MODULE    str1.c
.AUTHOR    Francois Ochsenbein [ESO]
.LANGUAGE  C
.CATEGORY  Basic string manipulation

.COMMENTS
\begin{TeX}
	Function names start with {\bf str} for case-sensitive functions,
	with {\bf stu} for case-insensitive functions.
	Declarations and corresponding macro definitions are in the header 
	STR.H.
The general features of all these functions are:
\begin{itemize}
\item The first argument is always a pointer to the string to scan or modify;
\item All functions in this module return an integer, which is
	\begin{itemize}
        \item either the length
        \item or an index, with values 0 to (length-1) for success,
        	length for failure (mismatch). 
		It is then easy to test the found byte, as in the
		following example:
\begin{verbatim}
        char  s[80];
        if (s[strloc(s,'.')])	
                printf("s contains a dot!");
\end{verbatim}
        \item	or the result of a comparison
        \end{itemize}
\item	The function name is followed by an underscore `\_' when a table is 
	required as the last parameter. The usage of 256-byte tables for 
	span / scan / translate operations allow higher speed.
\end{itemize}
\end{TeX}

.VERSION   1.0	25-Nov-1985: Creation 

.VERSION   2.0 	26-Jan-1988: Prepared for Midas, on top of osc routines.
.VERSION   2.1 	28-Mar-1989: Removed bug in stritem (if 2nd string is of length 0)
.VERSION   2.2	12-May-1989: Added strncopy.
.VERSION   2.3	16-Jun-1989: Check for null addresses
.VERSION   2.4	31-Jan-1990: Removed strid (now in str2). Cosmetic modifications
			Added strloc1, strscan1
.VERSION   2.5	30-Mar-1990: Added strred (replace macro),
.VERSION   2.6	11-Sep-1990: Added strins (Insert)

 090406		last modif
----------------------------------------------------------------------------*/
 
#include <macrogen.h>
#include <atype.h>
#include <string.h>
#include <sys/types.h>

#include <osdefos.h>
#include <proto_os.h>  

#define BITS_PER_CHAR	8
#define SIZE		(1<<BITS_PER_CHAR)

static unsigned char ttable[SIZE];
static char argstr[2] = "x";		/* for stuloc / stuskip	*/
static int (*f)();

static unsigned char *cpt(table, tabl0)
/*+++++++
.PURPOSE Copy table, inserting tabl0
.RETURNS Address of Copy
.REMARKS Copy because main_ascii is read-only ... can't just modify first byte
---------*/
	unsigned char *table;	/* IN: Table to copy	*/
	unsigned char  tabl0;	/* IN: Value of tabl[0]	*/
{
    oscopy((char *)ttable, (char *)table, SIZE);
    ttable[0] = tabl0;
    
    return(ttable);
}

/*===========================================================================
 *			Copy strings
 *===========================================================================*/
int strcopy(dest, source)
/*+++++++
.PURPOSE Copy strings, taking care of possible overlays.
.RETURNS Length of destination.
---------*/
	char *dest;	/* OUT: destination string 	*/
	char *source;	/* IN: source string 	*/
{
	register int len;

  if (source)
  	len = strlen(source), 	oscopy(dest, source, len+1);
  else	*dest = '\0', len = 0;

  return(len);
}

int strncopy(dest, maxsize, source)
/*+++++++
.PURPOSE Copy strings, taking care of possible overlays, but with max size.
	(forbid any overfill)
.RETURNS Length of destination.
---------*/
	char *dest;	/* OUT: destination string 	*/
	int   maxsize;	/* IN: Size of destination buffer (1+max.length) */
	char *source;	/* IN: source string 	*/
{
	register char *p;
	register int  len;

  p = dest;
  
  if (maxsize)
  {
  	if (source)
  	{	
	  	len = strlen(source) + 1;
	  	if (len > maxsize)	len = maxsize;
		len--;
	  	p += oscopy(p, source, len);
  	}
  	*p = '\0';
  }

  return(p-dest);
}


int strfill(dest, len, filler)
/*+++++++
.PURPOSE Fill a string with specified char, and terminates with EOS.
.RETURNS Length of destination.
---------*/
	char *dest;	/* OUT: destination string 	*/
	int   len;	/* IN: Size of destination buffer */
	int   filler;	/* IN: Character to be used for filling	*/
{
  *(dest+oscfill(dest,len,filler)) = '\0';
  return(len);
}

/*===========================================================================
 *			Locate / Skip a char
 *===========================================================================*/
int strloc(str, c)
/*+++++++
.PURPOSE Locate the first occurence of character `c'
.RETURNS Index of `c' in str; length of str if `c' not found.
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
	register char *p;

  for (p=str; *p; p++)
  	if (*p == c)	break;
  return(p-str);
}

int strloc1(str, c)
/*+++++++
.PURPOSE Locate the first occurence of character `c' --- but only if `c'
	is not escaped by a backslash
.RETURNS Index of `c' in str; length of str if `c' not found.
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
	register char *p;

  for (p=str; *p; p++)
  {
	if (*p == '\\')	{ p++; continue; }
	if (*p == c)	break;  
  }
  return(p-str);
}

int strbloc(str, c)
/*+++++++
.PURPOSE Locate the last occurence of character `c'
.RETURNS Index of `c' in str; -1 if `c' not found.
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
  return(oscbloc(str,strlen(str),c));
}	

/*===========================================================================*/
int strskip(str, c)
/*+++++++
.PURPOSE Locate the first character that differs from `c'
.RETURNS Index of `c' in str; length of str if str is made only of `c's
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
	register char *p;

  for (p=str; *p; p++)
  	if (*p != c)	break;
  return(p-str);
}
	
int strbskip(str, c)
/*+++++++
.PURPOSE Locate the last character that differs from `c'
.RETURNS Index of `c' in str; -1 if str is made only of `c's
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
  return(oscbskip(str, strlen(str), c));
}

/*===========================================================================*/
int stuset(table, list)
/*+++++++
.PURPOSE Prepare table for scan / span operations: value of 1 for characters
	in list, value 0 for other characters. Both upper and lower case
	are inserted in table.
.RETURNS Length of list
---------*/
	unsigned char *table;	/* OUT: Table with flagged characters	*/
	char *list;	/* IN: list of characters to flag	*/
{
	register char *p;

  oscfill((char *)table, SIZE, 0);
  for (p=list; *p; p++)	
  { 	*(table + *(unsigned char *)p) = 1; 
  	*(table + (unsigned)tocase(*p)) = 1;
  }
  
  return(p-list);
}

/*===========================================================================*/
int stuspans (str, list)
/*+++++++
.PURPOSE Match as many as possible chars specified in list, case insensitive.
.RETURNS Index of first character which is not in list;
	length of str if all characters are in list.
.REMARK	 To span e.g. alphabetic chars, use macro strspan(str, _ALPHA_)	
---------*/
	char *str;	/* IN: address of string to span	*/
	char *list;	/* IN: list of matching characters	*/
{
  stuset(ttable, list);
  return(oscspan((unsigned char *)str, (int) strlen(str), 1, ttable));
}
	
/*===========================================================================
 *			Scan / Span strings
 *===========================================================================*/

int strset(table, list)
/*+++++++
.PURPOSE Prepare table for scan / span operations: value of 1 for characters
	in list, value 0 for other characters.
.RETURNS Length of list
---------*/
	unsigned char *table;	/* OUT: Table [256] with flagged characters */
	char *list;		/* IN: list of characters to flag	*/
{
	register char *p;

  oscfill((char *)table, SIZE, 0);
  for (p=list; *p; p++)		*(table + *(unsigned char *)p) = 1;
  
  return(p-list);
}

/*===========================================================================*/
int strspan_ (str, mask, table)
/*+++++++
.PURPOSE Match as many as possible of flagged (non-zero in table) chars.
.RETURNS Index of first character which is not flagged; 
	length of str if all characters are flagged.
.REMARKS Table can be created with strset.
	If table[0] is not null, the result may be wrong.
---------*/
	char *str;	/* IN: address of string to span	*/
	unsigned char mask;	/* IN: attribute mask		*/
	unsigned char *table;	/* IN: attribute table 		*/
{
  if (table[0] & mask)	table = cpt(table, 0);	/* EOS MUST end	*/
  return(oscspan((unsigned char *)str, strlen(str), mask, table));
}

int strbspan_ (str, mask, table)
/*+++++++
.PURPOSE Match as many as possible of flagged chars, 
	starting from end (backwards matching)
.RETURNS Index of first character which is not flagged (RIHT to LEFT); 	-1 
	if all characters are flagged.
.REMARKS Table can be created with strset
---------*/
	char *str;	/* IN: address of string to span	*/
	unsigned char mask;	/* IN: attribute mask		*/
	unsigned char *table;	/* IN: attribute table 		*/
{
  return(oscbspan((unsigned char *)str, strlen(str), mask, table));
}

/*===========================================================================*/
int strspans (str, list)
/*+++++++
.PURPOSE Match as many as possible chars specified in list.
.RETURNS Index of first character which is not in list;
	length of str if all characters are in list.
---------*/
	char *str;	/* IN: address of string to span	*/
	char *list;	/* IN: list of matching characters	*/
{
  strset(ttable, list);		/* ttable[0] is zero	*/
  return(oscspan((unsigned char *)str, strlen(str), 1, ttable));
}

/*===========================================================================*/
int strbspans (str, list)
/*+++++++
.PURPOSE Match as many as possible chars specified in list, 
		looking from the end of the string.
.RETURNS Index of last character which is not in list; -1 if 
	all characters are in list.
---------*/
	char *str;	/* IN: address of string to span	*/
	char *list;	/* IN: list of matching characters	*/
{
  strset(ttable, list);
  return(oscbspan((unsigned char *)str, strlen(str), 1, ttable));
}

/*===========================================================================*/
int stubspans (str, list)
/*+++++++
.PURPOSE Match as many as possible chars specified in list, case insensitive,
		looking from the end of the string.
.RETURNS Index of last character which is not in list; -1 if 
		all characters are in list.
---------*/
	char *str;	/* IN: address of string to span	*/
	char *list;	/* IN: list of matching characters	*/
{
  stuset(ttable, list);
  return(oscbspan((unsigned char *)str, strlen(str), 1, ttable));
}


/*===========================================================================*/
int stuskip(str, c)
/*+++++++
.PURPOSE Locate the first character that differs from `c', case insensitive.
.RETURNS Index of `c' in str; length of str if str is made only of `c's
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
  argstr[0] = c;
  return(stuspans(str, argstr));
}
	
int stubskip(str, c)
/*+++++++
.PURPOSE Locate the last character that differs from `c', case insensitive.
.RETURNS Index of `c' in str; -1 if str is made only of `c's
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
  argstr[0] = c;
  return(stubspans(str, argstr));
}

/*===========================================================================*/
int strscan_ (str, mask, table)
/*+++++++
.PURPOSE Look for first flagged (non-zero in table) char.
.RETURNS Index of first flagged character; length of str if none was found.
.REMARKS Table can be created with strset
---------*/
	char *str;	/* IN: address of string to span	*/
	unsigned char mask;	/* IN: attribute mask		*/
	unsigned char *table;	/* IN: attribute table 		*/
{
  if_not(table[0] & mask)	
  	table = cpt(table, mask);	/* EOS MUST end	*/
  
  return(oscscan((unsigned char *)str, strlen(str), mask, table));
}

int strbscan_ (str, mask, table)
/*+++++++
.PURPOSE Look for last flagged (non-zero in table) char.
.RETURNS Index of last flagged character, -1 if none was found.
.REMARKS Table can be created with strset
---------*/
	char *str;	/* IN: address of string to span	*/
	unsigned char mask;	/* IN: attribute mask		*/
	unsigned char *table;	/* IN: attribute table 		*/
{
  return(oscbscan((unsigned char *)str, strlen(str), mask, table));
}

/*===========================================================================*/
int strscans (str, list)
/*+++++++
.PURPOSE Look for first char specified in list.
.RETURNS Index of first character in str which is in list;
	length of str if none was found.
.REMARK	 To scan e.g. alphabetic chars, use macro strscan(str, _ALPHA_)	
---------*/
	char *str;	/* IN: address of string to span	*/
	char *list;	/* IN: list of matching characters	*/
{
  strset(ttable, list);	ttable[0] = 1;
  return(oscscan((unsigned char *)str, strlen(str), 1, ttable));
}

int strscan1 (str, list)
/*+++++++
.PURPOSE Look for first char specified in list, only if not escaped
	by a backslash
.RETURNS Index of first character in str which is in list;
	length of str if none was found.
---------*/
	char *str;	/* IN: address of string to span	*/
	char *list;	/* IN: list of matching characters	*/
{
	char	*p;

  strset(ttable, list);	ttable[0] = 1;	ttable['\\'] = 1;

  for (p = str; *p; )
  {	p += oscscan((unsigned char *)p, strlen(p), 1, ttable);
  	if (*p != '\\')	break;
  	p += 2;
  }
  return(p - str);
}

/*===========================================================================*/
int stuscans (str, list)
/*+++++++
.PURPOSE Look for first flagged (non-zero in table) char, case insensitive
.RETURNS Index of first character in str which is in list;
	length of str if none was found.
---------*/
	char *str;	/* IN: address of string to scan	*/
	char *list;	/* IN: list of matching characters	*/
{
  stuset(ttable, list);	ttable[0] = 1;
  return(oscscan((unsigned char *)str, strlen(str), 1, ttable));
}

/*===========================================================================*/
int strbscans (str, list)
/*+++++++
.PURPOSE Retrieve last char specified in list.
.RETURNS Index of last char in str which is in list, -1 if none was found.
---------*/
	char *str;	/* IN: address of string to span	*/
	char *list;	/* IN: list of matching characters	*/
{
  strset(ttable, list);
  return(oscbscan((unsigned char *)str, strlen(str), 1, ttable));
}

/*===========================================================================*/
int stubscans (str, list)
/*+++++++
.PURPOSE Retrieve last char specified in list, case insensitive.
.RETURNS Index of last char in str which is in list, -1 if none was found.
---------*/
	char *str;	/* IN: address of string to scan	*/
	char *list;	/* IN: list of matching characters	*/
{
  stuset(ttable, list);
  return(oscbscan((unsigned char *)str, strlen(str), 1, ttable));
}

/*===========================================================================
 *			String Comparisons
 *===========================================================================*/
 
int strcomp(s1,s2)
/*++++++++++++++++++++++++
.PURPOSE Compare (or compute difference of) two strings.
.RETURNS A positive value if s1 > s2, zero if strings are identical,
	a negative value if s1 < s2.
-----------*/
     char *s1;	/* IN: address of first string 	*/
     char *s2;	/* IN: address of 2nd string  	*/
{
	register char *p, *q;

  for (p=s1, q=s2; *p == *q; p++, q++)
  	if (!(*p))	break;
  
  return(*p - *q);
}

/*===========================================================================*/
int stucomp(s1,s2)
/*++++++++++++++++++++++++
.PURPOSE Compare (or compute difference of) two strings, case insensitive.
.RETURNS A positive value if s1 > s2, zero if strings are identical,
	a negative value if s1 < s2.
-----------*/
     char *s1;	/* IN: address of first string 	*/
     char *s2;	/* IN: address of 2nd string  	*/
{
	char 	*p, *q;
	char 	cp, cq;

  for (p=s1, q=s2; ; p++, q++)
  {	cp = toupper(*p), cq = toupper(*q);
	if (cp != cq)	break;
    	if (!cp)	break;
  }
  
  return(cp - cq);
}

/*===========================================================================*/
int strmatch(s1,s2)
/*++++++++++++++++++++++++
.PURPOSE Look for longest matching string.
.RETURNS The number of characters common to s1 and s2. If strings are identical,
	the common length of both strings.
-----------*/
     char *s1;	/* IN: address of first string 	*/
     char *s2;	/* IN: address of 2nd string  	*/
{
	register char *p, *q;

  for (p=s1, q=s2; *p == *q; p++, q++)
  	if (!(*p))	break;
  
  return(p - s1);
}

/*===========================================================================*/
int stumatch(s1,s2)
/*++++++++++++++++++++++++
.PURPOSE Look for longest matching string, case insensitive.
.RETURNS The number of characters common to s1 and s2. If strings are identical,
	the common length of both strings.
-----------*/
     char *s1;	/* IN: address of first string 	*/
     char *s2;	/* IN: address of 2nd string  	*/
{
	char 	*p, *q;
	char 	cp, cq;

  for (p=s1, q=s2; ; p++, q++)
  {	cp = toupper(*p), cq = toupper(*q);
	if (cp != cq)	break;
    	if (!cp)	break;
  }
  
  return(p - s1);
}

/*===========================================================================*/
int strindex( s1, s2)
/*++++++++++++++++++++++++
.PURPOSE Locates a substring within a string
.RETURNS Index within first string of second string;
	  length of the first string for mismatch.
.METHOD  Use a scan on first char, then compare.
-----------*/
     char *s1;	/* IN: address of first string (source)	*/
     char *s2;	/* IN: address of 2nd string  (object to find)	*/
{   
	register char *s;

     s = strstr(s1,s2);
     if (s == NULL) return(strlen(s1));
     else           return(s-s1);
     
}

/*===========================================================================*/
int stuindex( s1, s2)
/*++++++++++++++++++++++++
.PURPOSE Locates a substring within a string, case insensitive
.RETURNS Index within first string of second string;
	  length of the first string for mismatch.
.METHOD  Use a scan on first char, then compare.
-----------*/
     char *s1;	/* IN: address of first string (source)	*/
     char *s2;	/* IN: address of 2nd string  (object to find)	*/
{   
	register char *p1;
	register int l2;

  p1 = s1;
  if ((l2 = strlen(s2)) == 0)	goto FIN;
  oscfill((char *)ttable, sizeof(ttable), 0);	
  ttable[0] = 1; 
  ttable[(int) *s2] = 1; 
  ttable[(unsigned)tocase(*s2)] = 1;

  for (; *p1; p1++)
  {	p1 += oscscan((unsigned char *)p1, strlen(p1), 1, ttable);
	if (!(*p1))			break;
  	if (osccomp(p1, s2, l2) == 0)	break;
  }

  FIN: return(p1-s1);
}

/*===========================================================================*/
static int _item( s1, s2, sep)
/*++++++++++++++++++++++++
.PURPOSE Locates a substring within a string, which must be followed / preceded
	by separator(s) characters
.RETURNS Index within first string of second string. The index
	  is the length of the first string for mismatch.
.METHOD  Use strindex, then check preceding / following char.
-----------*/
     char *s1;	/* IN: address of first string (source)	*/
     char *s2;	/* IN: address of 2nd string  (object to find)	*/
     char *sep;	/* IN: list of separators	*/
{   
	register char 	*p, *p1;
	register int 	i;

  i = strlen(s2);
  if (i == 0)	i = 1;
  
  for (p = p1 = s1; ; p1 += i)
  {	p1 += (*f)(p1, s2);		/* Index of s2 in p1	*/
	if_not(*p1)	break;		/* Not Found... 	*/
  	if (p1 > s1)			/* Check preceding char */
  	{	p = p1-1;
  		if_not(sep[strloc(sep, *p)])
  			continue;
  	}
	p = p1 + i;			/* Check following char	*/
	if_not(*p)	break;		/* Located at end : OK	*/
  	if (sep[strloc(sep, *p)])	/* Separator found	*/
			break;
  }

  return(p1-s1);
}

/*===========================================================================*/
int stritem( s1, s2, sep)
/*++++++++++++++++++++++++
.PURPOSE Locates a substring within a string, which must be followed / preceded
	by separator(s) characters
.RETURNS Index within first string of second string;
	  length of the first string for mismatch.
.METHOD  Use strindex, then check preceding / following char.
-----------*/
     char 	*s1;	/* IN: address of first string (source)	*/
     char 	*s2;	/* IN: address of 2nd string  (object to find)	*/
     char 	*sep;	/* IN: list of separators	*/
{   
	int strindex();
  
  f = strindex;
  
  return(_item(s1,s2,sep));
}

/*===========================================================================*/
int stuitem( s1, s2, sep)
/*++++++++++++++++++++++++
.PURPOSE Locates a substring within a string, which must be followed / preceded
	by separator(s) characters, case insensitive.
.RETURNS Index within first string of second string;
	  length of the first string for mismatch.
.METHOD  Use stuindex, then check preceding / following char.
-----------*/
     char *s1;	/* IN: address of first string (source)	*/
     char *s2;	/* IN: address of 2nd string  (object to find)	*/
     char *sep;	/* IN: list of separators	*/
{   
	int stuindex();
  
  f = stuindex;
  
  return(_item(s1,s2,sep));
}

/*===========================================================================
 *			String Conversions
 *===========================================================================*/

int strsetr (table, s1, s2)
/*+++++++
.PURPOSE  Create a table for character translations.
.RETURNS  Length of s1.
.REMARKS  Characters in s1 without counterpart in s2 (i.e. s1 is longer
	than s2) are replaced by a tilde (~).
---------*/
     unsigned char *table; 	/* OUT: translation table[256]	*/
     char *s1;		/* IN: list characters to translate 	*/
     char *s2;		/* IN: list of translated characters	*/
{
	register char *p, *q;
	register int i;

  for (i=0; i<SIZE; i++)	*(table + i) = i;	/* Fill table	*/
  for (p=s1, q=s2; *p; )
	*(table + *(p++)) = (*q ? *(q++) : '~');

  return(p-s1);
}

/*===========================================================================*/
int strtr_ (dest, source, table)
/*+++++++
.PURPOSE  Translate source into destination, according to translation table.
.RETURNS  Length of source / destination
.REMARKS  The translation table may be created by strsetr.
---------*/
     char *dest; 	/* OUT: the translated string		*/
     char *source; 	/* IN:  the string to translate		*/
     unsigned char *table; /* IN: translation table 		*/
{
  return(osctr((unsigned char *)dest ,(unsigned char *)source, strlen(source)+1, table));
}

/*===========================================================================*/
int strtrs (dest, source, s1, s2)
/*+++++++
.PURPOSE  Translate source into destination, transforming characters in s1
	into the corresponding character in s2.
.RETURNS  Length of source / dest.
.REMARKS  Characters in s1 without counterpart in s2 (i.e. s1 is longer
	than s2) are replaced by a tilde (~).
	The strings may overlap.
---------*/
     char *dest; 	/* OUT: the translated string		*/
     char *source; 	/* IN:  the string to translate		*/
     char *s1;		/* IN: list characters to translate 	*/
     char *s2;		/* IN: list of translated characters	*/
{
	register int len;

  strsetr(ttable, s1, s2);
  len = strlen(source);
  osctr((unsigned char *)dest, (unsigned char *)source, len+1, ttable);
  return(len);
}

/*===========================================================================
 *			Change Cases
 *===========================================================================*/
int strlower ( str )
/*+++++++++
.PURPOSE  Converts (in place) a string to lower case
.RETURNS  Length of string
--------*/
     char *str;	/* MOD: starting address        */
{
	register char *p;

  for (p = str; *p; p++)
	*p = tolower(*p); 

  return(p-str);
}

/*===========================================================================*/
int strupper ( str )
/*+++++++++
.PURPOSE  Converts (in place) a string to upper case
.RETURNS  Length of string
--------*/
     char *str;	/* MOD: starting address        */
{
	register char *p;

  for (p = str; *p; p++)
	*p = toupper(*p); 

  return(p-str);
}

/*===========================================================================*/
int strcase ( str )
/*+++++++++
.PURPOSE  Changes (in place) the case of a string, i.e. uppercase to lowercase
	and vice-versa.
.RETURNS  Length of string
--------*/
     char *str;	/* MOD: starting address        */
{
	register char *p;

  for (p = str; *p; p++)
	*p = tocase(*p); 

  return(p-str);
}

/*===========================================================================
 *			Reduce a string
 *===========================================================================*/

int strdel_ (str, mask, table)
/*+++++++
.PURPOSE  Delete all chars flagged in table
.RETURNS  New Length of string
.REMARKS  The string is EOS-terminated.
---------*/
     char *str;	/* MOD: string to modify		*/
     unsigned char mask;	/* IN: Mask to use (ANDed with table)	*/
     unsigned char *table;	/* IN: table of flags			*/
{
	register char *p, *q;

  for ( p=str, q=p; *p; p++)
   	if (!(ischar_(*p, mask, table)))	*(q++) = *p;

  *q = EOS;

  return(q-str);
}

/*===========================================================================*/
int strred_ (str, mask, table)
/*+++++++++++
.PURPOSE Reduces (in place) a string with suppression of redundant flagged
	characters (e.g. spaces).
	Leading and trailing spaces are also removed.
.RETURNS Length of modified (shorter) string.
-----------*/
     char *str;	/* MOD: string to modify		*/
     unsigned char mask;	/* IN: Mask to use (ANDed with table)	*/
     unsigned char *table;	/* IN: table of flags			*/
{
	register char *p, *q;
	char this_char, prec_char;

  this_char = prec_char = mask;	/* for a suppression of leading spaces */

  for (p=str, q=p; *p; prec_char = this_char, p++)
  { 	this_char = ischar_(*p, mask, table);
  	if (prec_char && this_char) continue;
	*(q++) = *p;
  }
                                     /* suppress last char if = fill */
  if(prec_char && (q != str)) 	q--; 
  *q = EOS;

  return(q-str);
}

/*===========================================================================*/
int strred (str)
/*+++++++++++
.PURPOSE Reduces (in place) a string with suppression of redundant spaces.
	Leading and trailing spaces are also removed.
.RETURNS Length of modified (shorter) string.
-----------*/
     char *str;			/* MOD: string to modify		*/
{
	register char *p, *q;
	char this_char, prec_char;

  this_char = prec_char = ' '; 	/* for a suppression of leading spaces */

  for (p=str, q=p; *p; prec_char = this_char, p++)
  { 	this_char = (isspace(*p) ? ' ' : *p);
  	if ((prec_char == ' ') && (this_char == ' ')) continue;
	*(q++) = *p;
  }
                                     /* suppress last char if = fill */
  if((prec_char == ' ') && (q != str)) 	q--; 
  *q = EOS;

  return(q-str);
}

/*===========================================================================*/
int strred1 ( str , escape )
/*+++++++++
.PURPOSE Reduces (in place) a string with suppression of `escape' char's,
	e.g. if escape is \  replace \\ by \, \% by %, etc.
.RETURNS Length of reduced string.
.REMARKS First and last `escape' are also removed. This function can be
	used e.g. to extract a string within quotes.
----------*/
     char *str;		/* MOD: String to modify 	*/
     char escape;	/* IN: escape char to be removed	*/
{
	register char *p, *q;

  for (p = str, q=p; *p; )
  { 	if (*p == escape)    if (*(++p) == EOS)	break;
	*(q++) = *(p++);
  }
  *q = EOS;

  return(q-str);
}


/*===========================================================================
 *			Other string utilities
 *===========================================================================*/
int strline_ ( str, lmax, mask, table)
/*+++++++++++++++
.PURPOSE Look for the longest match of a line in a string without 
	cutting words; a word is made of chars surrounded by sep_char(s).
.RETURNS Returns the length of the best match.
.REMARKS The rightestmost space is ignored.
----------------*/
     char *str;			/* MOD: string to scan			*/
     int lmax;			/* IN: max length of a line		*/
     unsigned char mask;	/* IN: Mask to use (ANDed with table)	*/
     unsigned char *table;	/* IN: table of flags			*/
{
 	register int len;
	register char *p;

  if_not(table[0] & mask)	
  	table = cpt(table, mask);	/* EOS MUST end	*/

  for (p = str; *p; )
  {	len = oscscan((unsigned char *)p, lmax, mask, table);
  	if (((p-str)+len) > lmax)	break;
	p += len;
	p += oscspan((unsigned char *)p, lmax, mask, table);	/* Don't span EOS! */
  }
				/* If no space was found, set to lmax	*/
  if ((*p) && (p == str))	p = str + lmax;

  return(p-str);
}

/*===========================================================================*/
int stuloc(str, c)
/*+++++++
.PURPOSE Locate the first occurence of character `c', case insensitive.
.RETURNS Index of `c' or `C' in str; length of str if `c' not found.
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
  argstr[0] = c;
  return(stuscans(str, argstr));
}


int stubloc(str, c)
/*+++++++
.PURPOSE Locate the last occurence of character `c', case insensitive.
.RETURNS Index of `c' or `C' in str; -1 if `c' not found.
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
{
  argstr[0] = c;
  return(stubscans(str, argstr));
}

