/*	PIRL_strings

A collection of string manipulation functions.


Use:

	#include	"PIRL_strings.h"

	char *
	strcombine
		(
		char	*leading_string,
		char	*trailing_string
		);

	char *
	strcompress
		(
		char	*string
		);

	char *
	strdir
		(
		char	*directory,
		char	*pathname
		);

	char *
	strfile
		(
		char	*filename,
		char	*pathname
		);

	char *
	strndup
		(
		char			*string,
		unsigned int	amount
		);

	char *
	skip_back_until
		(
		char	*string_start,
		char	*string_end,
		char	*skip_characters
		);

	char *
	skip_back_over
		(
		char	*string_start,
		char	*string_end,
		char	*skip_characters
		);

	char *
	strtruncate
		(
		char	*string;
		);

	char *
	next_word
		(
		char	*string,
		char	*delimiters
		);

	char *
	skip_delimiters
		(
		char	*string,
		char	*delimiters
		);

	char *
	skip_word
		(
		char	*string,
		char	*delimiters
		);

	char *
	ul[l]tostring
		(
		unsigned long [long]	value,
		int						base,
		char					*string
		);


Description:

- strcombine

Appends a string to a string returning a new combined string. The trailing
string is appended to the end of the leading string in newly allocated memory
the size of the combined result. If either leading or trailing is NULL, it will
be ignored. If both are NULL, and empty string will be produced.

Returns: A pointer to the newly allocated memory containing the combined
string. If an error occurs, NULL is returned.


- strcompress

Compresses white space out of a string. The string is compressed such that all
white space characters are removed, non-white space characters being shifted
"left" to fill the white space. White space includes all non-printing
characters (values less than or equal to the space character), except the null
character which marks the end of the string. The compression is done in place
as the output string is certain to be less than or equal to the size of the
input string.

Return value: The address of the null at the end of the compressed string.


- strdir/strfile

Obtains the directory/filename portion of a pathname string. strdir copies all
characters up to, and including, the final "/" character of pathname into the
directory string. There must be sufficient memory for the directory string (at
least as much as the pathname string). If the pathname does not have a
directory section (there is no "/") then the directory string will be zero
length. strfile works like strdir, except that the trailing filename section of
the pathname is copied: all characters after the directory section, or the
entire pathname if it has no directory section. Note that the concatenation
(strcat) of a directory and filename from the same pathname will reproduce the
original pathname.

Returns: A pointer to the directory/filename string, or NULL if either pathname
or directory/filename are NULL.


- strndup

Makes a duplicate copy of a specified amount of string. This function acts like
strdup except, instead of looking for an end-of-string character, the specified
amount is duplicated and an EOS is appended. This allows sections to be
extracted from larger strings.

Returns: A pointer to the new string, or NULL if memory for the new string
could not be allocated or the string argument is NULL.


skip_back_until -

Searches backwards from string_end until a character that is one of the
skip_characters is found. If string_end is NULL then the last character of
string_start is used as string_end (i.e. the character immediately preceeding
the null character marking the end of string).

Returns: A pointer to the last occurrence of one of the skip_characters before
or at string_end, or the next character after string_end if skip_characters is
NULL (this will be the null marking the end of the string when string_end is
NULL), or NULL if no skip_character was found between string_string and
string_end (or string_start is NULL).


skip_back_over -

The converse of skip_back_until: search backwards until a character that is NOT
one of the skip_characters is seen.

Returns: A pointer to the last occurrence of a character that is not one of the
skip_characters, or string_end if skip_characters is NULL, or NULL if all
characters between string_start and string_end are members of the
skip_characters set (or string_start is NULL).


strtruncate -

The string is truncated such that all white space characters are removed from
the end by putting an EOS after the last non-white space character in the
string (other embedded white space characters are part of the remaining string
as the check for white space starts at the end of the string and proceeds
towards the front). White space includes all non-printing characters (values
less than or equal to the space character). The truncation is done in place.

Returns: A pointer to the end of the truncated string.


next_word -

Starting at the string location skip any word and then skip the delimiters. A
"word" is defined here as a continguous string of non-delimiter characters.

Returns: A pointer to the beginning of the next word of the string, or, if no
next word is found before the end of the string, a pointer to the end-of-string
null.


skip_delimiters -

Returns: A pointer to the next non-delimiter character (not in the delimiters
string) in the string, or the end-of-string null.


skip_word -

Returns: A pointer to the next delimiter character (any character in the
delimiters string) in the string, or the end-of-string null.


- ul[l]tostring

Converts an unsigned long [long] value to an ASCII string representation. The
inverse of strtoul[l], the value is converted to its string representation in
the specified base notation. The base must be between 2 and 36, inclusive. If
the base is negative, the value is treated as signed. If a string buffer is
provided it receives the resultant string, otherwise string storage is
dynamically allocated.

Returns: A pointer to the string, or NULL (and errno is set) if the conversion
fails.


Author:

	Bradford Castalia, UA/PIRL, 4/99

SCCS ID: @(#)PIRL_strings.c	1.2 01/11/00
*/

/*******************************************************************************

Copyright (C) 2000  Bradford Castalia, University of Arizona

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.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


Bradford Castalia                       Castalia@Arizona.edu
Senior Systems Analyst                  http://PIRL.LPL.Arizona.edu/~castalia
Planetary Image Research Laboratory     520-621-4824
Department of Planetary Sciences        FAX: 520-621-9628
University of Arizona                   Space Sciences Bldg.
1629 E. University Blvd.                Room 429
Tucson, Arizona  85721-0092

"Build an image in your mind, fit yourself into it."
    The Log of Cyradis, Seeress of Kell.

*******************************************************************************/

#include	<string.h>
#include	<errno.h>


#ifndef NULL
#define NULL	0
#endif


char *
strcombine
	(
	char	*leading,
	char	*trailing
	)
{
char	*string;

if (! (string = (char *)calloc
		(1,
		(leading  ? strlen (leading)  : 0) +
		(trailing ? strlen (trailing) : 0) +
		1
		)
	))
	return (NULL);

if (leading)
	strcpy (string, leading);
if (trailing)
	strcat (string, trailing);
return (string);
}


char *
strcompress
	(
	char	*string
	)
{
char	*compressed;

for (compressed = string; *string; string++)
	if (*string > ' ')
		*compressed++ = *string;
*compressed = '\0';
return (compressed);
}


char *
strdir
	(
	char	*directory,
	char	*pathname
	)
{
if (directory && pathname)
	{
	strcpy (directory, pathname);
	if (pathname = strrchr (directory, '/'))
		*(++pathname) = '\0';
	else
		*directory = '\0';
	}
else
	return (NULL);
return (directory);
}


char		*strfile
	(
	char	*filename,
	char	*pathname
	)
{
char		*string;

if (filename && pathname)
	{
	if (string = strrchr (pathname, '/'))
		pathname = ++string;
	strcpy (filename, pathname);
	}
else
	return (NULL);
return (filename);
}


char *
strndup
	(
	char			*string,
	unsigned int	amount
	)
{
char	*new_string;

if (string &&
	(new_string = (char *)malloc (amount + 1)))
	{
	memcpy (new_string, string, amount);
	new_string[amount] = 0;
	}
else
	return (NULL);
return (new_string);
}


char *
skip_back_over
	(
	char	*string_start,
	char	*string_end,
	char	*skip_characters
	)
{
char
	*character;

if (string_start)
	{
	if (! string_end)
		string_end = &string_start[strlen (string_start) - 1];
	if (! skip_characters)
		return (string_end);

	/*	Backup over the skip_characters.
	*/
	while (string_start <= string_end)
		{
		for (character = skip_characters;
			*character;
			 character++)
			if (*string_end == *character)
				goto next_character;

		return (string_end);	/* Not a skip_character */

		next_character:
		string_end--;
		}
	}
return (NULL);
}


char *
skip_back_until
	(
	char	*string_start,
	char	*string_end,
	char	*skip_characters
	)
{
char
	*character;

if (string_start)
	{
	if (! string_end)
		string_end = &string_start[strlen (string_start)];
	else
		string_end++;
	if (! skip_characters)
		return (string_end);

	/*	Backup until one of the skip_characters is seen.
	*/
	while (string_start <= --string_end)
		for (character = skip_characters;
			*character;
			 character++)
			if (*string_end == *character)
				return (string_end);	/* A skip_character */
	}
return (NULL);
}



char *
strtruncate
	(
	char	*string
	)
{
char	*the_end;

for (the_end = &string[strlen (string) - 1];
	 the_end >= string;
	 the_end--)
	if (*the_end > ' ')
		break;
*(++the_end) = '\0';
return (the_end);
}


char *
skip_delimiters
	(
	char	*string,
	char	*delimiters
	)
{
char	*delimiter;

while (*string)
	{
	for (delimiter = delimiters;
		*delimiter;
		 delimiter++)
		if (*string == *delimiter)
			goto next_character;
	return (string);

	next_character:
	string++;
	}
return (string);
}


char *
skip_word
	(
	char	*string,
	char	*delimiters
	)
{
char	*delimiter;

while (*string)
	{
	for (delimiter = delimiters;
		*delimiter;
		 delimiter++)
		if (*string == *delimiter)
			return (string);
	string++;
	}
return (string);
}


char *
next_word
	(
	char	*string,
	char	*delimiters
	)
{
return (skip_delimiters (skip_word (string, delimiters), delimiters));
}


static char		digit[] =
	{
	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
	'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
	'U', 'V', 'W', 'X', 'Y', 'Z'
	};

char *
ultostring
	(
	unsigned long	value,
	int				base,
	char			*buffer
	)
{
unsigned long
	number,
	remainder;
int
	sign = 0,
	count;
char
	*string;


if (base < 0)
	{
	base = -base;
	if (value)
		{
		sign = 1;
		value = -((long)value);
		}
	}

if (base < 2 || base > 36)
	{
	errno = EINVAL;
	return (NULL);
	}

/*	Determine the size of the resultant string.
*/
number = value;
count = sign + 1;
do
	{
	number /= (long)base;
	count++;
	}
	while (number);


/*	If no buffer was provided, allocate one.
*/
if (! buffer &&
	! (buffer = (char *)malloc (count)))
	return (NULL);

	
/*	Start at the end of the string (least significant digit)
	and work back to the beginning (most significant digit).
*/
string = buffer + count - 1;
*string-- = '\0';

number = value;
do
	{
	*string-- = digit[number % (long)base];
	number /= (long)base;
	}
	while (number);

if (sign)
	*string = '-';

return (buffer);
}


char *
ulltostring
	(
	unsigned long long	value,
	int					base,
	char				*buffer
	)
{
unsigned long long
	number,
	remainder;
int
	sign = 0,
	count;
char
	*string;


if (base < 0)
	{
	base = -base;
	if (value)
		{
		sign = 1;
		value = -((long long)value);
		}
	}

if (base < 2 || base > 36)
	{
	errno = EINVAL;
	return (NULL);
	}

/*	Determine the size of the resultant string.
*/
number = value;
count = sign + 1;
do
	{
	number /= (long long)base;
	count++;
	}
	while (number);


/*	If no buffer was provided, allocate one.
*/
if (! buffer &&
	! (buffer = (char *)malloc (count)))
	return (NULL);

	
/*	Start at the end of the string (least significant digit)
	and work back to the beginning (most significant digit).
*/
string = buffer + count - 1;
*string-- = '\0';

number = value;
do
	{
	*string-- = digit[number % (long long)base];
	number /= (long long)base;
	}
	while (number);

if (sign)
	*string = '-';

return (buffer);
}
