/**************************************************************************
** ^FILE: strfuncs.c - Miscellaneous string functions for parseargs
**
** ^DESCRIPTION:
**    This file implements a wide variety of functions to manipulate
**    strings in way way or another. Some of the functions may already
**    be included in the standard library of some C compilers.
**
**    The following functions are implemented:
**
**       strucpy() -- copy a string and map to uppercase
**       strlcpy() -- copy a string and map to lowercase
**       strupr() -- convert a string to uppercase
**       strlwr() -- convert a string to lowercase
**       strdup() -- return a (newly allocated) copy of a string
**       strpbrk() -- return first occurrence of character in a set
**       strspn() -- return length of initial prefix of a character set
**       strcspn() -- return length of initial prefix of a character set
**       strltrim() -- trim leftmost (leading) characters in a string
**       strrtrim() -- trim rightmost (trailing) characters in a string
**       strtrim() -- trim leading and trailing characters in a string
**       strsplit() -- split a string up into a vector of tokens
**       strjoin() -- join a vector of token into a single string
**       get_name() -- return the aname (argument-name) of an argument
**       get_keyword() -- return the sname (keyword-name) of an argument
**       match() -- match two keywords (case insensitive) upto a unique prefix
**       stricmp() -- case insensitive comparison of strings
**       strnicmp() -- case insensitive length-limited comparison of strings
**       basename() -- remove the leading directories (and disks) from a path
**       indent_para() -- print an indented hanging paragraph
**
** ^HISTORY:
**    01/02/91 	Brad Appleton 	<brad@ssd.csd.harris.com> 	Created
**    - changed from file misc.c to this name and added all of the strxxx
**      functions (plus got rid of some unused functions).
**
**    --/--/--	Peter da Silva	<peter@ferranti.com>	
**
**    --/--/--	Eric P. Allman	<eric@Berkeley.EDU> 	Created
***^^**********************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <useful.h>

EXTERN  VOID   syserr  ARGS((const char *, ...));

static CONST char WhiteSpace[] = " \t\n\r\v\f";


#if ( defined(unix_style)  ||  defined(ibm_style) )
# define  TO_KWDCASE(c)  TOLOWER(c)
#else
# define  TO_KWDCASE(c)  TOUPPER(c)
#endif


/***************************************************************************
** ^FUNCTION: strucpy, strlcpy - copy dest to src, mapping to upper/lower case
**
** ^SYNOPSIS:
**
**    char *strucpy( dest, src )
**    char *strlcpy( dest, src )
**
** ^PARAMETERS:
**    char *dest;
**    -- the address to start copying to
**
**    char *src;
**    -- the address to start copying from
**
** ^DESCRIPTION:
**    Strlcpy (strucpy) copies src into dest (upto and including the
**    terminating NUL byte) and all uppercase (lowercase) characters in
**    src are mapped to lowercase (uppercase) before being copied into dest.
**
** ^REQUIREMENTS:
**    Dest must be non-null, and large enough to hold the copied result.
**
** ^SIDE-EFECTS:
**    Dest is (re)written
**
** ^RETURN-VALUE:
**    Address of dest.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
   char *strucpy( char *dest, const char *src )
#else
   char *strucpy( dest, src )  char *dest, *src;
#endif
{
   register char  *s1 = dest;
   register CONST char  *s2 = src;

   if ( !s2 )  return  CHARNULL;

   for ( ; *s2 ; s1++, s2++ ) {
      *s1 = TOUPPER( *s2 );
   }
   *s1 = '\0';

   return   s1;
}


#ifdef __ANSI_C__
   char *strlcpy( char *dest, const char *src )
#else
   char *strlcpy( dest, src )  char *dest, *src;
#endif
{
   register char  *s1 = dest;
   register CONST char  *s2 = src;

   if ( !s2 )  return  CHARNULL;

   for ( ; *s2 ; s1++, s2++ ) {
      *s1 = TOLOWER( *s2 );
   }
   *s1 = '\0';

   return   s1;
}


/***************************************************************************
** ^FUNCTION: strupr, strlwr - convert a string to all upper/lower case
**
** ^SYNOPSIS:
**    char *strupr( str )
**    char *strlwr( str )
**
** ^PARAMETERS:
**    char *str;
**    -- the string to be converted
**
** ^DESCRIPTION:
**    Strupr (strlwr) converts all lowercase (uppercase) characters in <str>
**    to uppercase (lowercase) and returns the address of <str>.
**
** ^REQUIREMENTS:
**    str should be non-null and non-empty.
**
** ^SIDE-EFECTS:
**    str is overwritten with the uppercase (lowercase) result.
**
** ^RETURN-VALUE:
**    Address of str.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
   char *strupr( char *str )
#else
   char *strupr( str )  char *str;
#endif
{
   char *p = str;

   for ( ; p && *p ; p++ ) {
      if ( islower(*p) )  *p = toupper(*p);
   }

   return   str;
}


#ifdef __ANSI_C__
   char *strlwr( char *str )
#else
   char *strlwr( str )  char *str;
#endif
{
   char *p = str;

   for ( ; p && *p ; p++ ) {
      if ( isupper(*p) )  *p = tolower(*p);
   }

   return   str;
}


/***************************************************************************
** ^FUNCTION: stricmp, strnicmp - case insensitive string comparison
**
** ^SYNOPSIS:
**    int stricmp( s1, s2 )
**    int strnicmp( s1, s2, n )
**
** ^PARAMETERS:
**    char *s1;
**    -- first string to compare
**
**    char *s2;
**    -- second string to compare
**
**    size_t  n;
**    -- The number of characters to compare
**
** ^DESCRIPTION:
**    Stricmp (strnicmp) is identical to strcmp (strncmp) except that it
**    it performs a case-insensitive comparison of characters.
**
** ^REQUIREMENTS:
**    Both s1 and s2 should be non-null and non-empty
**
** ^SIDE-EFECTS:
**    None.
**
** ^RETURN-VALUE:
**    < 0    if s1 < s2
**    = 0    if s1 matches s2
**    > 0    if s1 > s2
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
   int stricmp( const char *str1, const char *str2 )
#else
   int stricmp( str1, str2 ) char *str1, *str2;
#endif
{
   register  CONST char *s1 = str1, *s2 = str2;
   register  char  c1, c2;

   if ( s1 == s2 )  return   0;
   if ( !s1 )       return  -1;
   if ( !s2 )       return   1;

   for ( ; *s1 && *s2 ; s1++ , s2++ ) {
      c1 = TOLOWER( *s1 );
      c2 = TOLOWER( *s2 );

      if (c1 != c2)  return  (int)(c1 -c2);
   }
   return   (*s1 == *s2) ? 0 : (int)(*s1 - *s2);
}


#ifdef __ANSI_C__
   int strnicmp( const char *str1, const char *str2, size_t len )
#else
   int strnicmp( str1, str2, len ) char *str1, *str2; size_t  len;
#endif
{
   register  CONST char *s1 = str1, *s2 = str2;
   register  char  c1, c2;

   if ( s1 == s2 )  return   0;
   if ( !s1 )       return  -1;
   if ( !s2 )       return   1;

   for ( ; *s1 && *s2 && len ; s1++ , s2++ , len-- ) {
      c1 = TOLOWER( *s1 );
      c2 = TOLOWER( *s2 );

      if (c1 != c2)  return  (int)(c1 -c2);
   }
   return   (*s1 == *s2) ? 0 : (int)(*s1 - *s2);
}


#ifdef BSD

/***************************************************************************
** ^FUNCTION: strdup - copy a string
**
** ^SYNOPSIS:
*/
# ifndef __ANSI_C__
   char *strdup( str )
/*
** ^PARAMETERS:
*/
   char *str;
/*    -- the string to replicate
*/
# endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Strdup allocates storrage and copies the given string into the
**    newly acquired space (returning its address). The returned result
**    should be deallocated using free().
**
** ^REQUIREMENTS:
**    str should be non-null
**
** ^SIDE-EFFECTS:
**    None.
**
** ^RETURN-VALUE:
**    Address of the newly allocated string.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
# ifdef __ANSI_C__
   char *strdup( const char *str )
# endif
{
  unsigned len = strlen(str) + 1;
  char *p = malloc( len * sizeof(char) );

  if ( !p )  syserr( "Fatal Error -- out of memory" );
  strcpy(p, str);

  return p;
}


/***************************************************************************
** ^FUNCTION: strpbrk - return the first occurrence of characters in a string
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   char *strpbrk( str1, str2 )
/*
** ^PARAMETERS:
*/
   char *str1;
/*    -- the string to be searched
*/
   char *str2;
/*    -- the set of characters to be located
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Strpbrk will attempt to locate the first occurence in str1 of any 
**    character from str2 and return the address of the first such
**    occurrence. If str1 contains NO characters from str2, then NULL
**    is returned.
**
** ^REQUIREMENTS:
**    Both str1 and str2 should be non-null and non-empty
**
** ^SIDE-EFECTS:
**    None.
**
** ^RETURN-VALUE:
**    A pointer to the first occurence in str1 of any char from str2.
**
** ^ALGORITHM:
**    - foreach char in str1
**       - if char is in str2, return the address of char
**      end-for
**    - if we have reached the end of str1, return NULL
***^^**********************************************************************/
#ifdef __ANSI_C__
   char *strpbrk( const char *str1, const char *str2 )
#endif
{
   register CONST char *s1 =  str1, *s2 = str2;

   if ( !s1 || !*s1 || !s2 || !*s2 )  return  CHARNULL;

   for ( ; *s1 ; s1++ )  {
      if ( strchr(s2, *s1) )  return (char *)s1;
   }

   return  CHARNULL;
}


/***************************************************************************
** ^FUNCTION: strspn, strcspn - identify leading runs of characters
**
** ^SYNOPSIS:
**    char *strspn( str1, str2 )
**    char *strcspn( str1, str2 )
**
** ^PARAMETERS:
**    char *str1;
**    -- the string to be searched
**
**    char *str2;
**    -- the string to be searched
**
** ^DESCRIPTION:
**    Strspn (strcspn) attempts to determine the length of the longest
**    leading prefix of str1 that consists entirely of character from
**    (not from) str2.
**
** ^REQUIREMENTS:
**    Both str1 and str2 should be non-null and non-empty.
**
** ^SIDE-EFECTS:
**    None.
**
** ^RETURN-VALUE:
**    The length of the initial prefix in str1 consisting entirely
**    of characters from (not from) str2.
**
** ^ALGORITHM:
**    - length = 0
**    - for each char in str1
**       - if char is in str2 (for strcspn) or not in str2 (for strcspn)
**            then return length
**       - else
**            add 1 to length
**         end-if
**      end-for
**    - if end-of-string then return length
**
***^^**********************************************************************/
#ifdef __ANSI_C__
   int strspn( const char *str1, const char *str2 )
#else
   int strspn( str1, str2 )  char *str1, *str2;
#endif
{
   register CONST char  *s1 = str1, *s2 = str2;
   int len = 0;

   if ( !s1 || !*s1 || !s2 || !*s2 )  return  0;
   while ( *s1  &&  strchr(s2, *s1++) )  ++len;
   return  len;
}


#ifdef __ANSI_C__
   int strcspn( const char *str1, const char *str2 )
#else
   int strcspn( str1, str2 )  char *str1, *str2;
#endif
{
   register CONST char  *s1 = str1, *s2 = str2;
   int len = 0;

   if ( !s1 || !*s1 || !s2 || !*s2 )  return  0;
   while ( *s1  &&  !strchr(s2, *s1++) )  ++len;
   return  len;
}

#endif  /* BSD */


/***************************************************************************
** ^FUNCTION: strltrim, strrtrim, strtrim - trim leading/trailing characters
**
** ^SYNOPSIS:
**    char *strltrim( str, charset )
**    char *strrtrim( str, charset )
**    char *strtrim( str, charset )
**
** ^PARAMETERS:
**    char *str;
**    -- the string to be trimmed
**
**    char *charset;
**    -- the set of characters to be trimmed
**
** ^DESCRIPTION:
**    Strltrim removes from str, all leftmost (leading) characters occurring
**    in charset.
**
**    Strrtrim removes from str, all rightmost (trailing) characters occurring
**    in charset.
**
**    Strtrim removes from str, all leading and trailing characters occurring
**    in charset.
**
**    For each of these functions, if charset is NULL or empty, then the set
**    of whitespace characters (space, tab, newline, carriage-return, form-feed
**    and vertical-tab) is assumed.
**
** ^REQUIREMENTS:
**    str should be non-null and non-empty.
**
** ^SIDE-EFECTS:
**    characters may be removed from the beginning and/or end of str.
**
** ^RETURN-VALUE:
**    Address of str.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
   char *strltrim( char *str, const char *charset )
#else
   char *strltrim( str, charset )  char *str, *charset;
#endif
{
   register   int   i;

   if ( !str  ||  !*str )   return   str;
      /* if delim-string is NULL, whitespace is used */
   if ( !charset )   charset = WhiteSpace;

   i = strspn( str, charset );
   if ( i > 0 )  strcpy( str, &(str[i]) );

   return   str;
}


#ifdef __ANSI_C__
   char *strrtrim( char *str, const char *charset )
#else
   char *strrtrim( str, charset )  char *str, *charset;
#endif
{
   register   int   i;

   if ( !str  ||  !*str )   return   str;
   if ( !charset )   charset = WhiteSpace;
   for ( i = strlen(str) - 1 ;
            ( i >= 0 ) && (strchr( charset, str[i] )) ;
            i--
         ) ;

   str[i+1] = '\0';

   return   str;
}


#ifdef __ANSI_C__
   char *strtrim( char *str, const char *charset )
#else
   char *strtrim( str, charset )  char *str, *charset;
#endif
{
   register   int   i;

   if ( !str  ||  !*str )   return   str;
   if ( !charset )   charset = WhiteSpace;
   i = strspn( str, charset );
   if ( i > 0 )  strcpy( str, &(str[i]) );

   for ( i = strlen(str) - 1 ;
            ( i >= 0 ) && (strchr( charset, str[i] )) ;
            i--
         ) ;

   str[i+1] = '\0';

   return   str;
}


/***************************************************************************
** ^FUNCTION: strsplit - split a string into tokens
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   int  strsplit( vec, token_str, separators )
/*
** ^PARAMETERS:
*/
   char **vec[];
/*    -- pointer to the string vector to be allocated
*/
   char token_str[];
/*    -- the string to be split up
*/
   char separators[];
/*    -- the delimiters that separate tokens
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Strsplit will split token_str up into  a vector of tokens that are
**    separated by one or more characters from <separators>. The number
**    of tokens found is returned and storage is allocated for the given
**    vector (which may later be deallocated using free()).
**
**    If <separators> is NULL or empty, then the set of whitespace characters
**    is used as the token delimiters.
**
** ^REQUIREMENTS:
**    vec must be non-NULL (it must be a valid address).
**    token_str should be non-null and non-empty
**
** ^SIDE-EFECTS:
**    All leading and trailing characters from <separators> are removed
**    from token_str. Furthermore, all remaining sequences in token_str
**    of characters from <separators> are replaced with a single NUL-byte.
**
**    Token_str holds the actual storage for all the strings in the newly
**    created vector.
**
** ^RETURN-VALUE:
**    The number of tokens parsed.
**
** ^ALGORITHM:
**    - count the number of tokens present while at the same time removing
**      all leading and trailing delimiters, and replacing all other sequences
**      of delimiters with the NUL character.
**    - allocate a vector large enough to point to all the token strings.
**    - for i in 0 .. (numtokens - 1) do
**         - vector[i] = token_str
**         - advance token_str to point at the next character past the
**           righmost NUL-byte (which should be the start of the next token).
**      end-for
**    - return the number of tokens parsed.
***^^**********************************************************************/
#ifdef __ANSI_C__
   int strsplit( char **vec[], char token_str[], const char separators[] )
#endif
{
   register   char c, *pread, *pwrite;
   int   i, count = 0;

   if ( !token_str )    return   0;
      /* if delim-string is NULL, whitespace is used */
   if ( !separators )   separators = WhiteSpace;

      /* trim leading separators */
   pread = token_str;
   while ( strchr(separators, *pread) )   ++pread;
   token_str = pwrite = pread;

      /*
      ** make first pass through string, counting # of tokens and
      ** separating all tokens by a single '\0'
      */
   while ( c = *pread++ ) {
      if ( !strchr(separators, c) )   {
         *pwrite++ = c;
      }
      else {
         *pwrite++ = '\0';   /* null terminate this token */
         ++count;                /* update token count */
         while ( strchr(separators, *pread) )   ++pread;
      }
   }/*while*/
   if ( *(pwrite - 1) )  {
      ++count;         /* dont forget last token */
      *pwrite = '\0';   /* null-terminate */
   }

      /* allocate space for the caller's vector (remember NULL at the end) */
   (*vec) = (char **)malloc( (1 + count) * sizeof( char * ) );
   if ( !*vec ) {
      fprintf( stderr, "out of memory in strsplit() - aborting\n" );
      exit( -1 );
   }

      /* now go thru token-string again assigning pointers from vector */
   pread = token_str;
   for ( i = 0 ; i < count ; i++ ) {
      (*vec)[i] = pread;   /* assign pointer */
      pread += strlen( pread ) + 1;
   }/* end-for */

      /* set up the trailing pointer to NULL at the end */
   (*vec)[ count ] = CHARNULL;
   return   count;
}


/***************************************************************************
** ^FUNCTION: strjoin - join a vector of tokens together
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   char  *strjoin( argv, separator )
/*
** ^PARAMETERS:
*/
   char *argv[];
/*    -- pointer to the string vector to join together
*/
   char separator[];
/*    -- the the string to use to separate tokens (if NULL, " " is used)
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Strjoin will make a single string out of the given vector by copying
**    all the tokens from the given vector (in order) toa newly allocated
**    string. Each token will be separated by an occurence of <separator>.
**
**    If <separator> is NULL then a single space is used as the separator.
**    If <separator> is empty, then no separator is used and the tokens are
**    simply concatenated together.
**
** ^REQUIREMENTS:
**    argv must be non-NULL (it must be a valid address), and must be terminated
**    by a pointer to NULL (argv[last+1] == NULL).
**
** ^SIDE-EFECTS:
**    Storage is allocated.
**
** ^RETURN-VALUE:
**    The addres of the newly-joined results (which should be deallocated
**    using free()). Returns NULL if nothing was joined.
**
** ^ALGORITHM:
**    - count the number of characters to place in the joined-result.
**    - allocate a string large-enough to copy the joined-result into.
**    - copy each string into the string (with <separator> between tokens).
**    - 0 return the result.
***^^**********************************************************************/
#ifdef __ANSI_C__
   char *strjoin( const char *argv[], const char separator[] )
#endif
{
   size_t  sz = 0;
   register char *p;
   register CONST char *a, **av;
   register int  seplen;
   char *result;

      /* if argv is NULL, nothing to do */
   if ( !argv )  return  CHARNULL;
   if ( !separator )  separator = " ";
   seplen = strlen( separator );

      /* figure out how much space we need */
   for ( av = argv ; *av ; av++ ) {
      if ( !**av )  continue;
      sz += strlen( *av );
      if ( seplen  &&  *(av + 1) )  sz += seplen;
   }

      /* allocate space */
   result = (char *)malloc( (sz + 1) * sizeof(char) );
   if ( !result )  syserr( "malloc failed in strjoin()" );

      /* join the strings together */
   *result = '\0';
   for ( av = argv, p = result ; (a = *av) ; av++ ) {
      if ( !*a )  continue;
      while ( (*p = *a++) ) ++p;  /* copy token */
      if ( seplen  &&  *(av + 1) ) {
         a = separator;
         while ( (*p = *a++) ) ++p;  /* copy separator */
      }/*end-if*/
   }/*end-for*/

   return  result;
}


/***************************************************************************
** ^FUNCTION: get_name - return the aname (argument-name) of an argument
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   char  *get_name( s, buf )
/*
** ^PARAMETERS:
*/
   char *s;
/*    -- the ad_prompt field of an ARGDESC struct
*/
   char *buf;
/*    -- address to which the aname should be copied
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Get_name will get the full argument name of the given argument
**    (not just the keyword name) and copy it to buf.
**
** ^REQUIREMENTS:
**    Both s and buf must be non-null and non-empty.
**    buf must be large enough to hold the result.
**
** ^SIDE-EFECTS:
**    buf is overwritten.
**
** ^RETURN-VALUE:
**    Address of the buffer containing the name.
**
** ^ALGORITHM:
**    determine the name of an argument from its prompt
**    and copy the result in the given buffer
***^^**********************************************************************/
#ifdef __ANSI_C__
   char *get_name( const char *s, char *buf )
#endif
{
      /* <buf> must be large enough to hold the result! */
   strlcpy(buf, s);
   return   buf;
}


/***************************************************************************
** ^FUNCTION: get_keyword - get the sname (keyword name) of an argument
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   char  *get_keyword( s, buf )
/*
** ^PARAMETERS:
*/
   char *s;
/*    -- the ad_prompt field of an ARGDESC struct
*/
   char *buf;
/*    -- address to which the sname should be copied
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Get_name will get the keyword name of the given argument
**    (not the entire argument name) and copy it to buf.
**
**    The sname (keyword-name) consists only of all uppercase characters
**    from the ad_prompt field (in the order they occur). If the ad_prompt
**    field contains NO uppercase characters, than the aname and the sname
**    are equivalent (the entire string).
**
** ^REQUIREMENTS:
**    Both s and buf must be non-null and non-empty.
**    buf must be large enough to hold the result.
**
** ^SIDE-EFECTS:
**    buf is overwritten.
*
** ^RETURN-VALUE:
**    Address of the buffer containing the keyword.
**
** ^ALGORITHM:
**    determine the keyword of an argument from its prompt
**    and copy the result in the given buffer
***^^**********************************************************************/
#ifdef __ANSI_C__
   char *get_keyword( const char *s, char *buf )
#endif
{
   register char *p1 = (char *)s, *p2;
   register int   i, len = 0;
   char *caps = CHARNULL;

   if ( !p1 )  return  CHARNULL;

      /* find size to copy (use all caps if possible) */
   for ( p1 = (char *)s ; *p1 ; p1++ )   {
     if ( !caps  &&  isupper( *p1 ) )  caps = p1;
         if ( caps   &&   isupper( *p1 ) )   ++len;
   }
   if ( !caps )   len = (int) (p1 - (char *)s);

      /* copy string into buffer and convert it to desired case */
      /* <buf> must be large enough to hold the result! */
   p1 = buf;
   if ( len )   {
     if ( !caps ) {
        for ( p1 = buf, p2 = (char *)s, i = 0 ; i < len ; p1++, p2++, i++ ) {
            *p1 = TO_KWDCASE(*p2);
        }
     }/*if*/

     else {
        for ( p2 = caps, i = 0 ; i < len ; p2++ ) {
           if ( isupper( *p2 ) )   {
              *(p1++) = TO_KWDCASE(*p2);
              ++i;
           }
        }/*for*/
     }/*else*/
   }/*if*/
   *p1 = '\0';

   return   buf;   /* return buffer address */
}
#ifndef amiga_style

/***************************************************************************
** ^FUNCTION: match - match a keyword against a prospective argument
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   int match( candidate, target )
/*
** ^PARAMETERS:
*/
   char *candidate;
/*    -- the possible keyword argument
*/
   char *target;
/*    -- the keyword to be matched
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Match will attempt to see if the candidate string matches the
**    target string (case insensitive). First a match is tried on the
**    sname of the keyword, then on the aname.  Candidate may be only
**    a partial leading portion of the target as long as it is at least
**    two characters long (unless the keyword is 1 character long).
**
**    No "partial" matching is accepted for AmigaDOS command-lines.
**
** ^REQUIREMENTS:
**    Both candidate and target should be non-null and non-empty.
**    target should be the ad_prompt field of an ARGDESC structure.
**
** ^SIDE-EFECTS:
**    None.
**
** ^RETURN-VALUE:
*     < 0  if candidate < target
**    = 0  if candidate matches target
**    > 0  if candidate > target
**
** ^ALGORITHM:
**    - attempt a partial match against the sname and return 0 if we succeed
**    - attempt a partial match against the aname and return 0 if we succeed
**    - if both the above fail return non-zero (no match).
**    
***^^**********************************************************************/

/* rewritten 8/20/90 --BDA */
#define MINLEN  2     /* minimum # of characters to match */

#ifdef __ANSI_C__
   int match ( const char *candidate, const char *target )
#endif
{
   int  i, clen, tlen, too_short=0;
   CONST char *full_targ;
   char *up_targ;


   full_targ = target;

      /* make up_targ the uppercase portion of target */
   up_targ = strdup( full_targ );
   (VOID) get_keyword( full_targ, up_targ );

      /* match at least MINLEN characters if possible */
   tlen = strlen( up_targ );
   clen = strlen( candidate );
   if ( (tlen >= MINLEN)   &&   (clen < MINLEN) ) {
      ++too_short;      /* not long enough -- no match */
   }

#ifdef vms_style
      /* if first two chars are NO then match at least MINLEN+2 chars */
   if ( !strnicmp(up_targ, "NO", 2) ) {
      if ( (tlen >= (MINLEN + 2))   &&   (clen < (MINLEN + 2)) ) {
         ++too_short;      /* not long enough -- no match */
      }
   }
#endif

      /* first try to match prefix of the uppercase portion */
   i = (too_short) ? -1 : strnicmp(up_targ, candidate, clen);

   free( up_targ );

      /* did we match? */
   if ( !i )  return  0;   /* yes! */

   /* no! : compare the whole target
   **       match at least MINLEN characters if possible
   */
   tlen = strlen(full_targ);
   if ( (tlen >= MINLEN)   &&   (clen < MINLEN) ) {
      return   -1;      /* not long enough -- no match */
   }

#ifdef vms_style
   /* if first two chars are NO then match at least MINLEN+2 chars */
   if ( !strnicmp(full_targ, "no", 2) ) {
      if ( (tlen >= (MINLEN + 2))   &&   (clen < (MINLEN + 2)) ) {
         return   -1;      /* not long enough -- no match */
      }
   }
#endif

   return   strnicmp(full_targ, candidate, clen);
}


/* here is the AmigaDOS version of match() */
#else

# ifdef __ANSI_C__
   int match( const char *candidate, const char *target )
# else
   int match( candidate, target) char *candidate, *target;
# endif
{
   int i, j;
   char c;

   i = j = 0;

   while ( isgraph(target[i]) || isgraph(candidate[i]) ) {
      while ( islower(target[i]) ) i++;
      if ( !isgraph(target[i]) ) {
         if ( !isgraph(candidate[j]) )  return 0;
         return  stricmp(target, candidate);
      }
      c = islower( candidate[j] ) ? toupper(candidate[j]) : candidate[j];
      if (target[i] != c)  return  stricmp(target, candidate);
      i++;
      j++;
   }
   return 0;
}

#endif


/***************************************************************************
** ^FUNCTION: basename - return the last component of a pathname
**
** ^SYNOPSIS:
**    char *basename( path )
**
** ^PARAMETERS:
**    path;
**    -- the pathname to be truncated.
**
** ^DESCRIPTION:
**    Basename takes a pathname and strips of all leading components
**    (except for the very last one) which refer to directories or
**    disk-drives.
**
** ^REQUIREMENTS:
**    path should be non-null, non-empty, and should correspond to a valid
**    pathname (absolute or relative).
**
** ^SIDE-EFECTS:
**    None under Unix and AmigaDOS.
**
**    Under VMS, the file version is removed and any .COM or .EXE extension
**    is also removed.
**
**    Under MS-DOS, any .EXE, .COM, or .BAT extension is removed.
**
**    Under OS/2, any .EXE, .COM, or .CMD extension is removed.
**    
** ^RETURN-VALUE:
**    The address of the basename of the path.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
   /* should use '\\' for MS-DOS & OS/2 and use ']' for VMS */
#ifdef vms
#define PATH_SEP ']'

      /* VAX/VMS version of basename */
# ifdef __ANSI_C__
   char *basename( char  path[] )
# else
   char *basename( path ) char  path[];
# endif
   {
      char *base = strrchr( path, PATH_SEP );
      char *vers = strrchr( path, ';' );
      char *ext  = strrchr( path, '.' );

      if ( !base ) {
         if ( !(base = strrchr( path, ':' )) ) {
            base = path;
         }
         else {
            ++base;
         }
      }
      else {
         ++base;
      }

      if ( vers )  *vers ='\0';
      if ( ext  &&  (!stricmp(ext, ".COM") || !stricmp(ext, ".EXE")) ) {
          ext = '\0';
      }

      return  base;
   }

#else
#ifdef AmigaDOS
      /* amiga version of basename() */
# ifdef __ANSI_C__
   char *basename( char  path[] )
# else
   char *basename( path ) char  path[];
# endif
   {
      return   path;
   }
#else
#define PATH_SEP  '/'     /* default path-separator character */

      /* default version of basename() */
# ifdef __ANSI_C__
   char *basename( char  path[] )
# else
   char *basename( path ) char  path[];
# endif
   {
      char *base = strrchr( path, PATH_SEP );

#if ( defined(MSDOS) || defined(OS2) )
      /* remove the extension from .EXE, .COM, .BAT, and .CMD files */
# ifdef OS2
      if ( ext  &&  (!stricmp(ext, ".CMD") )  *ext = '\0';
# else
      if ( ext  &&  (!stricmp(ext, ".BAT") )  *ext = '\0';
# endif

      if ( ext  &&  (!stricmp(ext, ".COM") || !stricmp(ext, ".EXE")) ) {
          ext = '\0';
      }
#endif

      if ( !base ) {
#if ( defined(MSDOS) || defined(OS2) )
         base = strrchr( path, '\\' );
         if ( base )  return  (base + 1);
         if ( path[ 1 ] == ':' )  return  (path + 2);  /* just remove drive */
         return  (base + 1);
#endif
         return  path;
      }
      else {
         return  (base + 1);
      }
   }
#endif
#endif


/***************************************************************************
** ^FUNCTION: indent_para - print a hanging indented paragraph
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
   VOID indent_para(fp, maxcols, margin, title, indent, text)
/*
** ^PARAMETERS:
*/
   FILE *fp;
/*    -- the stream to which output is sent
*/
   int maxcols;
/*    -- the maximum width (in characters) of the output
*/
   int margin;
/*    -- the number of spaces to use as the left margin
*/
   char *title;
/*    -- the paragraph title
*/
   int indent;
/*    -- the distance between the title and the paragraph body
*/
   char *text;
/*    -- the body of the paragraph
*/
#endif  /* !__ANSI_C__ */

/* ^DESCRIPTION:
**    Indent_para will print on fp, a titled, indented paragraph as follows:
**
**    <----------------------- maxcols --------------------------->
**    <--- margin -->     <-- indent -->
**                   title              This is the first sentence
**                                      of the paragraph. Etc ...
**
** ^REQUIREMENTS:
**    maxcols and indent must be positive numbers with maxcols > indent
**
** ^SIDE-EFECTS:
**    Output is printed to fp.
**
** ^RETURN-VALUE:
**    None.
**
** ^ALGORITHM:
**    Print the paragraph title and then print the text.
**    Lines are automatically adjusted so that each one starts in the
**    appropriate column.
***^^**********************************************************************/
#ifdef __ANSI_C__
   void indent_para( FILE *fp, int maxcols, int margin,
                     const char *title, int indent, const char *text )
#endif
{
   register int idx = 0;
   BOOL first_line = TRUE;
   int  text_len = strlen(text);
   char ch;

   /* print the title */
      fprintf( fp, "%*s%-*s", margin, "", indent, title );

   idx = maxcols - margin - indent;

   if ( text_len <= idx )
      fprintf(fp, "%s\n", text);
   else
      do {
               /* backup to end of previous word */
         while (idx  &&  !isspace(text[idx]))  --idx;
         while (idx  &&  isspace(text[idx]))   --idx;

            /* print leading whitespace */
         if (!first_line)
            fprintf(fp, "%*s%-*s", margin, "", indent, "");

         ch = text[ ++idx ];
         *((char *)text + idx) = '\0';
         fprintf(fp, "%s\n", text);
         *((char *)text + idx) = ch;
         first_line = FALSE;
         text = &(text[idx+1]);
         text_len -= (idx+1);

         while (isspace(*text)) {  /* goto next word */
            ++text;
            --text_len;
         }

         idx = maxcols - margin - indent;

         if ( text_len <= idx )  /* print-last line */
            fprintf(fp, "%*s%-*s%s\n", margin, "", indent, "", text);
      } while ( text_len > idx );
}


#ifdef STRTEST

#define WS " \t\n\v\r\f\"'"

static char string2[] =  "	  oh what  	a beautiful -	morning!    	";

static char string[] =  "\n\
\t' ',  ARGREQ,          argStr,   Name,     'Name',\n\
\t'n',  ARGOPT|ARGLIST,  listStr,  Groups,   'newsGROUP (newsgroups test)',\n\
\t'c',  ARGOPT,          argInt,   RepCount, 'REPcount (number of reps)',\n\
\t'd',  ARGOPT,          argStr,   DirName,  'DIRname',\n\
\t'x',  ARGOPT,          argBool,  XFlag,    'Xflag (expand in X direction)',\n\
\t' ',  ARGOPT|ARGLIST,  listStr,  Argv,     'File',\n\
\tENDOFARGS\n\
";

static char word_str[] = "HELP (print help and quit)";

main()
#ifdef __ANSI_C__
#endif
{
   char **vector;
   unsigned i, numtoks;

   printf( "test of strtrim() and strsplit():\n\n" );

   printf( "unparsed string='%s'\n", string2 );
   printf( "ltrimmed string='%s'\n", strltrim( string2, WS ) );
   printf( "rtrimmed string='%s'\n", strrtrim( string2, WS ) );

   numtoks = strsplit( &vector, string, "," );
   printf( "number of tokens=%d\n", numtoks );
   for ( i = 0 ; i < numtoks ; i++ ) {
      printf( "trimmed token[%d] = '%s'\n", i, strtrim( vector[i], WS ) );
   }

   exit( 0 );
}

#endif
