Changeset View
Changeset View
Standalone View
Standalone View
lib/libc/string/strtok.c
Show All 40 Lines | |||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <stddef.h> | #include <stddef.h> | ||||
#ifdef DEBUG_STRTOK | #ifdef DEBUG_STRTOK | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#endif | #endif | ||||
#include <string.h> | #include <string.h> | ||||
char *__strtok_r(char *, const char *, char **); | char *__strtok_r(char * __restrict, const char * __restrict, char ** __restrict); | ||||
__weak_reference(__strtok_r, strtok_r); | __weak_reference(__strtok_r, strtok_r); | ||||
char * | char * | ||||
__strtok_r(char *s, const char *delim, char **last) | __strtok_r(char * __restrict s, const char * __restrict delim, char ** __restrict last) | ||||
{ | { | ||||
char *spanp, *tok; | const char *spanp; | ||||
int c, sc; | char *tok; | ||||
char c, sc; | |||||
if (s == NULL && (s = *last) == NULL) | if (__predict_false(s == NULL && (s = *last) == NULL)) | ||||
jrtc27: You've lost the return NULL for if s is still NULL | |||||
Done Inline ActionsI mean it is undefined to have s and last be null according to the c standard gfunni234_gmail.com: I mean it is undefined to have s and last be null according to the c standard | |||||
Done Inline ActionsNot true; *last is entirely unspecified and up to the implementation. In particular, when our strtok_r returns the final token, it sets *last to NULL, but the application needs to call strtok_r one more time to see the NULL *return* value. This means the final call to strtok_r for an application iterating over the tokens is going to dereference a null pointer. See https://godbolt.org/z/9qEqb34z4 for an example proving the old code works but your new code renders the standard use broken. Also, strtok_r is POSIX not C. jrtc27: Not true; `*last` is entirely unspecified and up to the implementation. In particular, when our… | |||||
return (NULL); | return (NULL); | ||||
/* | /* | ||||
* Skip (span) leading delimiters (s += strspn(s, delim), sort of). | * Skip (span) leading delimiters (s += strspn(s, delim), sort of). | ||||
*/ | */ | ||||
cont: | cont: | ||||
c = *s++; | c = *s++; | ||||
for (spanp = (char *)delim; (sc = *spanp++) != 0;) { | for (spanp = delim; (sc = *spanp++) != '\0';) { | ||||
if (c == sc) | if (c == sc) | ||||
goto cont; | goto cont; | ||||
} | } | ||||
if (c == 0) { /* no non-delimiter characters */ | if (c == '\0') { /* no non-delimiter characters */ | ||||
*last = NULL; | *last = NULL; | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
tok = s - 1; | tok = s - 1; | ||||
/* | /* | ||||
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of). | * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). | ||||
* Note that delim must have one NUL; we stop if we see that, too. | * Note that delim must have one NUL; we stop if we see that, too. | ||||
*/ | */ | ||||
for (;;) { | for (;;) { | ||||
c = *s++; | c = *s++; | ||||
spanp = (char *)delim; | spanp = delim; | ||||
do { | do { | ||||
if ((sc = *spanp++) == c) { | if ((sc = *spanp++) == c) { | ||||
if (c == 0) | if (c == '\0') | ||||
s = NULL; | s = NULL; | ||||
else | else | ||||
s[-1] = '\0'; | s[-1] = '\0'; | ||||
*last = s; | *last = s; | ||||
return (tok); | return (tok); | ||||
} | } | ||||
} while (sc != 0); | } while (sc != '\0'); | ||||
} | } | ||||
/* NOTREACHED */ | /* NOTREACHED */ | ||||
} | } | ||||
char * | char * | ||||
strtok(char *s, const char *delim) | strtok(char * __restrict s, const char * __restrict delim) | ||||
{ | { | ||||
static char *last; | static char *last; | ||||
return (__strtok_r(s, delim, &last)); | return (__strtok_r(s, delim, &last)); | ||||
} | } | ||||
#ifdef DEBUG_STRTOK | #ifdef DEBUG_STRTOK | ||||
/* | /* | ||||
Show All 30 Lines |
You've lost the return NULL for if s is still NULL