/* * 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 #include #include /* 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); }