/*	$Id$
 *	Author:	Roger A. Cole
 *	Date:	10-10-90
 *
 *	Experimental Physics and Industrial Control System (EPICS)
 *
 *	Copyright 1991-92, the Regents of the University of California,
 *	and the University of Chicago Board of Governors.
 *
 *	This software was produced under  U.S. Government contracts:
 *	(W-7405-ENG-36) at the Los Alamos National Laboratory,
 *	and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *	Initial development by:
 *		The Controls and Automation Group (AT-8)
 *		Ground Test Accelerator
 *		Accelerator Technology Division
 *		Los Alamos National Laboratory
 *
 *	Co-developed with
 *		The Controls and Computing Group
 *		Accelerator Systems Division
 *		Advanced Photon Source
 *		Argonne National Laboratory
 *
 * Modification Log:
 * -----------------
 * .00	10-10-90	rac	initial version
 * .01	06-18-91	rac	installed in SCCS
 * .02  06-25-91	rac	changed nextNonSpaceField to handle quotes
 * .03	03-04-92	rac	fixed nextNonSpaceField to honor '\0'
 *
 * make options
 *	-DvxWorks	makes a version for VxWorks
 *	-DNDEBUG	don't compile assert() checking
 *      -DDEBUG         compile various debug code, including checks on
 *                      malloc'd memory
 */
/*+/mod***********************************************************************
* TITLE	nextFieldSubr.c - text field scanning routines
*
* GENERAL DESCRIPTION
*	The routines in this module provide for scanning fields in text
*	strings.  They can be used as the basis for parsing text input
*	to a program.
*
* QUICK REFERENCE
* char	(*pText)[];
* char	(*pField)[];
* char	*pDelim;
*
*   int  nextAlphField(		<>pText,   >pField,   >pDelim		)
*   int  nextAlph1UCField(	<>pText,   >pField,   >pDelim		)
*   int  nextANField(		<>pText,   >pField,   >pDelim		)
*   int  nextChanNameField(	<>pText,   >pField,   >pDelim		)
*   int  nextFltField(		<>pText,   >pField,   >pDelim		)
*   int  nextFltFieldAsDbl(	<>pText,   >pDblVal,  >pDelim		)
*   int  nextIntField(		<>pText,   >pField,   >pDelim		)
*   int  nextIntFieldAsInt(	<>pText,   >pIntVal,  >pDelim		)
*   int  nextIntFieldAsLong(	<>pText,   >pLongVal, >pDelim		)
*   int  nextNonSpace(	        <>pText					)
*   int  nextNonSpaceField(	<>pText,   >pField,   >pDelim		)
*
* DESCRIPTION
*	The input text string is scanned to identify the beginning and
*	end of a field.  At return, the input pointer points to the character
*	following the delimiter and the delimiter has been returned through
*	its pointer; the field contents are `returned' either as a pointer
*	to the first character of the field or as a value returned through
*	a pointer.
*
*	For nextNonSpaceField, the field is defined as either:
*	1)  from the next non-white-space character up to the following
*	    white-space character, or
*	2)  from a " through a matching ", with the "s not being included
*	    in the characters returned to the caller.  The delimeter will
*	    be the character following the closing ".
*
*	In the input string, a '\0' is stored in place of the delimiter,
*	so that standard string handling tools can be used for text fields.
*
*	nextAlphField		scans the next alphabetic field
*	nextAlph1UCField	scans the next alphabetic field, changes
*				the first character to upper case, and
*				changes the rest to lower case
*	nextANField		scans the next alpha-numeric field
*	nextChanNameField	scans the next field as a channel name,
*				delimited by white space or a comma
*	nextFltField		scans the next float field
*	nextFltFieldAsDbl	scans the next float field as a double
*	nextIntField		scans the next integer field
*	nextIntFieldAsInt	scans the next integer field as an int
*	nextIntFieldAsLong	scans the next integer field as a long
*	nextNonSpace		scans to the next non-space character
*	nextNonSpaceField	scans the next field, delimited by white
*				space
*
* RETURNS
*	count of characters in field, including the delimiter.  A special
*	case exists when only '\0' is encountered; in this case 0 is returned.
*	(For quoted alpha strings, the count will include the " characters.)
*
* BUGS
* o	use of type checking macros isn't protected by isascii()
* o	for nextANField, with string in quotes: embedded " isn't handled;
*	if " isn't encountered before end of line, no error message is
*	generated.
*
* SEE ALSO
*	tsTextToStamp()
*
* NOTES
* 1.	fields involving alpha types consider underscore ('_') to be
*	alphabetic.
*
* EXAMPLE
*	char text[]="process 30 samples"
*	char *pText;	pointer into text string
*	char *pCmd;	pointer to first field, to use as a command
*	int  count;	value of second field, number of items to process
*	char *pUnits;	pointer to third field, needed for command processing
*	int  length;	length of field
*	char delim;	delimiter for field
*
*	pText = text;
*	if (nextAlphField(&pText, &pCmd, &delim) <= 1)
*	    error action if empty field
*	if (nextIntFieldAsInt(&pText, &count, &delim) <= 1)
*	    error action if empty field
*	if (nextAlphField(&pText, &pUnits, &delim) <= 1)
*	    error action if empty field
*	printf("command=%s, count=%d, units=%s\n", pCmd, count, pUnits);
*
*-***************************************************************************/
#include <genDefs.h>
#ifdef vxWorks
#   include <ctype.h>
#else
#   include <ctype.h>
#endif

/*-----------------------------------------------------------------------------
*    the preamble skips over leading white space, stopping either at
*    end of string or at a non-white-space character.  If EOS is encountered,
*    then the appropriate return conditions are set up and a return 0 is done.
*----------------------------------------------------------------------------*/
#define NEXT_PREAMBLE \
    char	*pDlm;		/* pointer to field delimiter */ \
    int		count;		/* count of characters (plus delim) */ \
 \
    assert(ppText != NULL); \
    assert(*ppText != NULL); \
    assert(ppField != NULL); \
    assert(pDelim != NULL); \
 \
    if (**ppText == '\0') { \
	*ppField = *ppText; \
	*pDelim = **ppText; \
	return 0; \
    } \
    while (**ppText != '\0' && isspace(**ppText)) \
	(*ppText)++;		/* skip leading white space */ \
    pDlm = *ppField = *ppText; \
    if (*pDlm == '\0') { \
	*pDelim = **ppText; \
	return 0; \
    } \
    count = 1;			/* include delimiter in count */
/*-----------------------------------------------------------------------------
*    the postamble is called for each character in the field.  It moves
*    the pointers and increments the count.  If the loop terminates, then
*    the caller is informed of the delimiter and the source character
*    string has '\0' inserted in place of the delimiter, so that the field
*    is now a proper '\0' terminated string.
*----------------------------------------------------------------------------*/
#define NEXT_POSTAMBLE \
	pDlm++; \
	count++; \
    } \
    *pDelim = *pDlm; \
    *ppText = pDlm; \
    if (*pDlm != '\0') { \
	(*ppText)++;		/* point to next available character */ \
	*pDlm = '\0'; \
    }

int
nextAlphField(ppText, ppField, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
char	**ppField;	/* O pointer to pointer to field */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    NEXT_PREAMBLE
	while (isalpha(*pDlm) || *pDlm == '_') {
	    NEXT_POSTAMBLE
    return count;
}
int
nextAlph1UCField(ppText, ppField, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
char	**ppField;	/* O pointer to pointer to field */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    NEXT_PREAMBLE
    while (isalpha(*pDlm) || *pDlm == '_') {
	if (count == 1) {
	    if (islower(*pDlm))
		*pDlm = toupper(*pDlm);
	}
	else {
	    if (isupper(*pDlm))
		*pDlm = tolower(*pDlm);
	}
	NEXT_POSTAMBLE
    return count;
}
int
nextANField(ppText, ppField, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
char	**ppField;	/* O pointer to pointer to field */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    NEXT_PREAMBLE
    while (isalnum(*pDlm) || *pDlm == '_') {
	NEXT_POSTAMBLE
    return count;
}
int
nextChanNameField(ppText, ppField, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
char	**ppField;	/* O pointer to pointer to field */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    NEXT_PREAMBLE
    while (!isspace(*pDlm)) {
	if (*pDlm == '\0')
	    break;
	else if (*pDlm == ',')
	    break;
	NEXT_POSTAMBLE
    return count;
}
int
nextFltField(ppText, ppField, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
char	**ppField;	/* O pointer to pointer to field */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    NEXT_PREAMBLE
    while (isdigit(*pDlm) || *pDlm=='-' || *pDlm=='+' || *pDlm=='.') {
	NEXT_POSTAMBLE
    return count;
}
int
nextFltFieldAsDbl(ppText, pDblVal, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
double	*pDblVal;	/* O pointer to return field's value */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    char	*pField;	/* pointer to field */
    int		count;		/* count of char in field, including delim */

    assert(pDblVal != NULL);

    count = nextFltField(ppText, &pField, pDelim);
    if (count > 1) {
	if (sscanf(pField, "%lf", pDblVal) != 1)
	    assert(0);
    }

    return count;
}
int
nextIntField(ppText, ppField, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
char	**ppField;	/* O pointer to pointer to field */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    NEXT_PREAMBLE
    while (isdigit(*pDlm) || ((*pDlm=='-' || *pDlm=='+') && count==1)) {
	NEXT_POSTAMBLE
    return count;
}
int
nextIntFieldAsInt(ppText, pIntVal, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
int	*pIntVal;	/* O pointer to return field's value */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    char	*pField;	/* pointer to field */
    int		count;		/* count of char in field, including delim */

    assert(pIntVal != NULL);

    count = nextIntField(ppText, &pField, pDelim);
    if (count > 1) {
	if (sscanf(pField, "%d", pIntVal) != 1)
	    assert(0);
    }

    return count;
}
int
nextIntFieldAsLong(ppText, pLongVal, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
long	*pLongVal;	/* O pointer to return field's value */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    char	*pField;	/* pointer to field */
    int		count;		/* count of char in field, including delim */

    assert(pLongVal != NULL);

    count = nextIntField(ppText, &pField, pDelim);
    if (count > 1) {
	if (sscanf(pField, "%ld", pLongVal) != 1)
	    assert(0);
    }

    return count;
}
int
nextNonSpace(ppText)
char	**ppText;	/* I/O pointer to pointer to text to scan */
{
    while (isspace(**ppText))
	(*ppText)++;
    return 0;
}
int
nextNonSpaceField(ppText, ppField, pDelim)
char	**ppText;	/* I/O pointer to pointer to text to scan */
char	**ppField;	/* O pointer to pointer to field */
char	*pDelim;	/* O pointer to return field's delimiter */
{
    NEXT_PREAMBLE
    if (**ppField == '"') {
	(*ppField)++;		/* skip over leading double quote */
	count++;
	pDlm++;
	while (*pDlm != '"') {	/* scan until find another " */
	    NEXT_POSTAMBLE
	if (*pDelim == '"') {
	    pDlm++;
	    while (isspace(*pDlm))
		pDlm++;		/* skip trailing white space */
	    if (*pDlm == '"')
		pDlm--;
	    *pDelim = *pDlm;	/* give caller the delim and set */
	    *ppText = pDlm;	/*     for scanning the next field */
	    if (*pDlm != '\0')
		(*ppText)++;
	}
    }
    else {
	while (!isspace(*pDlm)) {
	    if (*pDlm == '\0')
		break;
	    NEXT_POSTAMBLE
    }
    return count;
}
