/*
* xstrtok.c
* Written by D'Arcy J.M. Cain
* based on code by Henry Spencer
*
* This routine may be freely distributed as long as credit is given to D'Arcy
* J.M. Cain, the source is included and this notice remains intact. There is
* specifically no restrictions on use of the program including personal or
* commercial. You may even charge others for this routine as long as the above
* conditions are met.
*
* This is not shareware and no registration fee is expected. If you like the
* program and want to support this method of distribution, write a program or
* routine and distribute it the same way and I will feel I have been paid.
*
* As for documentation, you're looking at it.
*
* Because strtok is a little brain dead about multiple calls
* this is a modified version which carries the pertinent information
* in a structure. This allows more than one string to be parsed
* at the same time (the code is re-entrant.)
*
* Get next token from string x->s (NULL on 2nd, 3rd, etc. calls),
* where tokens are strings separated by runs of chars from delim.
* Writes NULs into s to end tokens. delim need not remain constant
* from call to call. If quote is set then quotes (single * or double)
* are respected.
*
* Note that unlike strtok(), this function returns a field for every
* token even if the field is empty.
*
*/
#include <ctype.h>
#include <string.h>
#include <stdio.h>
/* copy this definition to file that calls this (or a header file) */
typedef struct {
char *scanpoint; /* filled in by xstrtok */
char *str2parse; /* string to parse - set for first call */
const char *delim; /* string of delimiters */
int quote; /* respect quoting if set */
} XSTRTOK;
extern char *xgetline(FILE *fp, char *buf, size_t *linenum);
char *xstrtok(XSTRTOK *xinfo);
char *
xstrtok(XSTRTOK *xinfo)
{
char *scan;
char *tok;
char qchar = 0;
const char *dscan;
/* figure out what stage we are at */
if (xinfo->str2parse == NULL && xinfo->scanpoint == NULL)
return(NULL); /* last one went last time */
if (xinfo->str2parse != NULL)
{
scan = xinfo->str2parse; /* this is the first time */
xinfo->str2parse = NULL;
}
else
scan = xinfo->scanpoint; /* we have already started */
/* special case for space delimiter */
if (*xinfo->delim == ' ')
while (isspace((int) *scan))
scan++;
/* are we finished with the line? */
if (*scan == '\0')
{
xinfo->scanpoint = NULL;
return(*xinfo->delim == ' ' ? NULL : scan);
}
/* otherwise we now point to the next token */
tok = scan;
/* find the end of the token */
/* three versions for speedup rather than testing within loop */
/* first find empty string such as "" */
if (xinfo->quote && (*tok == '\"' || *tok == '\'') && tok[0] == tok[1])
{
*tok = 0;
xinfo->scanpoint = tok + 2;
return(tok);
}
/* non-empty string respecting quotes */
if (xinfo->quote) for (; *scan != '\0'; scan++)
{
for (dscan = xinfo->delim; *dscan != '\0';) /* increment is in body */
{
/* have we found a delimiter? */
if ((*xinfo->delim == ' ' && isspace((int) *scan)) || *scan == *dscan++)
{
xinfo->scanpoint = scan + 1; /* point to next character */
*scan = '\0'; /* terminate the token */
return(tok); /* and return it */
}
/* is it a quote character */
if (*scan == '\'' || *scan == '\"')
{
qchar = *scan; /* search for same close quote */
strcpy(scan, scan + 1); /* don't return quotes */
while (*scan && *scan != qchar)
scan++;
strcpy(scan, scan + 1); /* don't return quotes */
scan--;
}
}
}
/* normal strtok semantics */
else for (; *scan != '\0'; scan++)
{
for (dscan = xinfo->delim; *dscan != '\0';) /* increment is in body */
{
/* have we found a delimiter? */
if ((*xinfo->delim == ' ' && isspace((int) *scan)) || *scan == *dscan++)
{
xinfo->scanpoint = scan + 1; /* point to next character */
*scan = '\0'; /* terminate the token */
return(tok); /* and return it */
}
}
}
/* Reached end of string. */
xinfo->scanpoint = NULL;
return(tok);
}
syntax highlighted by Code2HTML, v. 0.9.1