home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
misc
/
volume29
/
parseargs
/
part07
/
strfuncs.c
Wrap
C/C++ Source or Header
|
1992-05-19
|
41KB
|
1,514 lines
/**************************************************************************
** ^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
** stricmp() -- case insensitive comparison of strings
** strnicmp() -- case insensitive length-limited comparison of strings
** strdup() -- return a (newly allocated) copy of a string
** strndup() -- return a (newly allocated) copy of a prefix 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 tokens into a single string
** get_argpfx() -- return the length of the first part of the string
** get_argdesc() -- return the description (second part) of the string
** get_argname() -- return the aname (argument-name) of an argument
** get_kwdname() -- return the sname (keyword-name) of an argument
** match() -- match two keywords (case insensitive) upto a unique prefix
** basename() -- remove the leading directories (and disks) from a path
** indent_para() -- print an indented hanging paragraph
**
** ^HISTORY:
**
** 12/16/91 Brad Appleton <brad@ssd.csd.harris.com>
** - add strndup()
**
** 11/26/91 Brad Appleton <brad@ssd.csd.harris.com>
** - added the following to indent_para(). If last arg is 0,
** then the whole length is used.
**
** 08/27/91 Earl Chew <cechew@bruce.cs.monash.edu.au>
** - add extra length argument to indent_para().
** - add FORCE_KWDCASE() macro
** - add non-writable strings support to get_argname() and
** get_kwdname()
** - add get_argpfx() and get_argdesc() for non-writable strings
** support
** - allow zero length string for strsplit()
**
** 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>
#ifdef vms
# include <ssdef.h>
#endif
#include "strfuncs.h"
EXTERN VOID syserr ARGS((const char *, ...));
static CONST char WhiteSpace[] = " \t\n\r\v\f";
#define c_ARG_SEP '='
#if ( defined(unix_style) || defined(ibm_style) )
# define FORCE_KWDCASE(s) strlwr(s)
# define TO_KWDCASE(c) TOLOWER(c)
# define KWDCASECOPY(dest,src) strlcpy(dest,src)
#else
# define FORCE_KWDCASE(s) strupr(s)
# define TO_KWDCASE(c) TOUPPER(c)
# define KWDCASECOPY(dest,src) strucpy(dest,src)
#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-EFFECTS:
** 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-EFFECTS:
** 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-EFFECTS:
** 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 (!len || (*s1 == *s2)) ? 0 : (int)(*s1 - *s2);
}
/***************************************************************************
** ^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 = (char *)malloc( len * sizeof(char) );
if ( !p ) syserr( "malloc failed in strdup()" );
strcpy(p, str);
return p;
}
/***************************************************************************
** ^FUNCTION: strndup - copy a prefix of a string
**
** ^SYNOPSIS:
*/
# ifndef __ANSI_C__
char *strndup( str, len )
/*
** ^PARAMETERS:
*/
char *str;
/* -- the string to replicate
*/
unsigned len;
/* -- the number of characters to be replicated
*/
# endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Strndup allocates storrage and copies the the first "len" characters
** of 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 *strndup( const char *str, unsigned len )
# endif
{
char *p = (char *)malloc( (len + 1) * sizeof(char) );
if ( !p ) syserr( "malloc failed in strndup()" );
strncpy(p, str, len);
return p;
}
#ifdef BSD
/***************************************************************************
** ^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-EFFECTS:
** 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-EFFECTS:
** 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-EFFECTS:
** 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
**
** ^SIDE-EFFECTS:
** 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
** rightmost NUL-byte (which should be the start of the next token).
** end-for
** - vector[numtokens] = NUL
** - 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 ) token_str = "";
/* if delim-string is NULL, whitespace is used */
if ( !separators ) separators = WhiteSpace;
/* trim leading separators */
pread = token_str;
while ( strchr(separators, *pread) ) ++pread;
if ( ! *pread ) return 0;
token_str = pwrite = pread;
/*
** make first pass through string, counting # of tokens and
** separating all tokens by a single '\0'
*/
for ( c = *pread++ ; c ; ) {
if ( !strchr(separators, c) ) {
do {
*pwrite++ = c;
} while ( (c = *pread++) && !strchr(separators, c) );
*pwrite++ = '\0';
++count;
}/*if*/
while ( c && strchr(separators, c) ) c = *pread++;
}/*for*/
/* allocate space for the caller's vector (remember NULL at the end) */
(*vec) = (char **)malloc( (1 + count) * sizeof( char * ) );
if ( !*vec ) syserr( "malloc failed in strsplit()" );
/* 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) to a newly allocated
** string. Tokens will be separated by a single 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-EFFECTS:
** Storage is allocated.
**
** ^RETURN-VALUE:
** The address of the newly-joined result (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_argpfx - get the prefix portion of a string
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
int get_argpfx( str )
/*
** ^PARAMETERS:
*/
char *str;
/* -- the string to parse for a description
*/
#endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Get_argdesc returns the length of the first portion of the string.
**
** Two "portions" must be either separated by whitespace or the second
** portion may be within "(),{},[], or <>" delimiters. The second
** portion is assumed to begin with the first alphabetic following
** separator.
**
** ^REQUIREMENTS:
** str should be non-null and non-empty
**
** ^SIDE-EFFECTS:
** None.
**
** ^RETURN-VALUE:
** The length of the first portion.
**
** ^ALGORITHM:
** - locate the end of the first portion by scanning for whitespace or
** balanced delimiters.
***^^**********************************************************************/
#ifdef __ANSI_C__
int get_argpfx( const char *str )
#endif
{
register char *description;
static CONST char whitespace[] = " \t\n\r\f\v";
static CONST char beg_portion[] = "(<{[";
description = strpbrk( str, whitespace );
if ( !description ) {
description = strpbrk( str, beg_portion );
}
return description ? description - str : strlen(str);
}
/***************************************************************************
** ^FUNCTION: get_argdesc - get the description portion of a string
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
char *get_argdesc( str, len )
/*
** ^PARAMETERS:
*/
char *str;
/* -- the string to parse for a description
*/
int *len;
/* -- the pointer to the length
*/
#endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Get_argdesc returns a pointer to the second portion of the string
** and also indicates how long it is.
**
** Two "portions" must be either separated by whitespace or the second
** portion may be within "(),{},[], or <>" delimiters. The second
** portion is assumed to begin with the first alphabetic following
** separator.
**
** ^REQUIREMENTS:
** str should be non-null and non-empty
**
** ^SIDE-EFFECTS:
** The length of the description is written to *len.
**
** ^RETURN-VALUE:
** Address of the description (or NULL if the string has no description).
**
** ^ALGORITHM:
** - locate the end of the first portion by scanning for whitespace or
** balanced delimiters.
** - locate the beginning of the second portion by scanning for the first
** alpha-numeric following the end of the first portion.
** - return the address of the description.
***^^**********************************************************************/
#ifdef __ANSI_C__
char *get_argdesc( const char *str, int *len )
#endif
{
register char *description = CHARNULL;
BOOL is_end = FALSE, is_balanced = FALSE;
char *p;
static CONST char whitespace[] = " \t\n\r\f\v";
static CONST char beg_portion[] = "(<{[";
static CONST char end_portion[] = ")>}]";
description = strpbrk( str, whitespace );
if ( description ) {
is_end = TRUE;
while ( isspace(*++description) ) continue; /* trim leading ' ' */
if ( strchr(beg_portion, *description) ) {
is_balanced = TRUE;
++description;
}
}
else {
description = strpbrk( str, beg_portion );
if ( description ) { /* skip leading '(' */
is_end = is_balanced = TRUE;
++description;
}
}
if ( !description ) {
*len = 0;
}
else {
if ( is_balanced ) { /* remove trailing ')' */
p = description + (strlen( description ) - 1);
if ( !strchr(end_portion, *p) ) ++p;
*len = p - description;
}
else {
while ( !isalnum(*description) ) ++description;
*len = strlen( description );
}
}
return description;
}
/***************************************************************************
** ^FUNCTION: get_argname - return the aname (argument-name) of an argument
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
char *get_argname( 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_argname 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-EFFECTS:
** 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_argname( const char *s, char *buf )
#endif
{
register CONST char *p2;
int len;
/* see if sname and aname are separated by c_ARG_SEP
** <buf> must be large enough to hold the result!
*/
len = get_argpfx(s);
p2 = strchr( s, c_ARG_SEP );
if ( p2 && p2 < &s[len]) {
++p2;
strncpy( buf, p2, len-(p2-s) );
buf[len-(p2-s)] = 0;
strlwr(buf);
}
else {
strncpy( buf, s, len );
buf[len] = 0;
strlwr(buf);
}
return buf;
}
/***************************************************************************
** ^FUNCTION: get_kwdname - get the sname (keyword name) of an argument
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
char *get_kwdname( 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_kwdname 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-EFFECTS:
** 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_kwdname( const char *s, char *buf )
#endif
{
register char *p1 = (char *)s, *p2, ch;
int len;
BOOL caps = FALSE;
if ( !p1 ) return CHARNULL;
/* see if sname and aname are separated by c_ARG_SEP */
len = get_argpfx( p1 );
p2 = strchr( p1, c_ARG_SEP );
if ( p2 && p2 < &p1[len]) {
strncpy( buf, p1, p2-p1);
buf[p2-p1] = 0;
FORCE_KWDCASE(buf);
return buf;
}
/* copy string into buffer and convert it to desired case */
/* <buf> must be large enough to hold the result! */
for ( p2 = buf; *p1 && len != 0; p1++, len-- ) {
if ( isupper(*p1) ) {
if ( !caps ) {
caps = TRUE;
p2 = buf;
}
*p2++ = TO_KWDCASE(*p1);
}
else if ( !caps ) {
*p2++ = TO_KWDCASE(*p1);
}
}
*p2 = '\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-EFFECTS:
** 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;
char arg_targ[ 256 ], kwd_targ[ 256 ];
/* make kwd_targ the keyword portion of target */
(VOID) get_kwdname( target, kwd_targ );
/* match at least MINLEN characters if possible */
tlen = strlen( kwd_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(kwd_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 keyword portion */
i = (too_short) ? -1 : strnicmp(kwd_targ, candidate, clen);
/* did we match? */
if ( !i ) return 0; /* yes! */
/* no! : compare the argument portion
** match at least MINLEN characters if possible
*/
/* make arg_targ the argument portion of target */
(VOID) get_argname( target, arg_targ );
tlen = strlen(arg_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(arg_targ, "no", 2) ) {
if ( (tlen >= (MINLEN + 2)) && (clen < (MINLEN + 2)) ) {
return -1; /* not long enough -- no match */
}
}
#endif
return strnicmp(arg_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
{
char kwd_targ[ 256 ], arg_targ[ 256 ];
int rc;
(VOID) get_kwdname( target, kwd_targ );
rc = stricmp( kwd_targ, candidate );
if ( rc == 0 ) return 0;
(VOID) get_argname( target, arg_targ );
rc = stricmp( arg_targ, candidate );
return rc;
}
#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-EFFECTS:
** 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, textlen)
/*
** ^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
*/
int textlen;
/* -- the length of 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-EFFECTS:
** 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,
int textlen )
#endif
{
register int idx = 0;
BOOL first_line = TRUE;
char ch;
if ( ! textlen ) textlen = strlen( text );
/* print the title */
fprintf( fp, "%*s%-*s", margin, "", indent, title );
idx = maxcols - margin - indent;
if ( textlen <= idx )
fprintf(fp, "%.*s\n", textlen, text);
else
do {
/* backup to end of previous word */
while (idx && !isspace(text[idx])) --idx;
while (idx && isspace(text[idx])) --idx;
idx++;
/* print leading whitespace */
if (!first_line)
fprintf(fp, "%*s%-*s", margin, "", indent, "");
fprintf(fp, "%.*s\n", idx, text);
first_line = FALSE;
text = &(text[idx+1]);
textlen -= (idx+1);
while (isspace(*text)) { /* goto next word */
++text;
--textlen;
}
idx = maxcols - margin - indent;
if ( textlen <= idx ) /* print-last line */
fprintf(fp, "%*s%-*s%.*s\n", margin, "", indent, "", textlen, text);
} while ( textlen > 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 ) );
}
#ifdef vms
exit( SS$_NORMAL );
#else
exit( 0 );
#endif
}
#endif