Compilation problems
NULL
isn't defined - either use the constant 0
or include <stddef.h>
before skip()
. Alternatively, just initialise p = src
instead of this dead store.
Type mismatch assigning src
to p
- we should make them point to the same kind of object, either both const char
or both char
. (In C++, we would overload the function with two variants, each accepting and returning the same type; in C we need to give the two functions different names).
In the test program, printf
is undefined - include <stdio.h>
to fix this.
We can't pass ishspace()
to skip()
as it has type int(*)(char)
but we need an int(*)(int)
. That's easily fixed by making it accept int
instead.
ishspace
is a reserved identifier that might conflict with future library versions of <ctype.h>
. Choose a name that's allowed for user functions (simply inserting _
after is
is sufficient).
Design issues
The comment advertises that the is…()
functions from <ctype.h>
can be used, but that's dangerous with the present implementation because these functions accept only positive characters (or EOF
). We need to cast to unsigned char
before widening to int
when calling these.
When func()
returns true for '\\'
, the backslash-newline concatenation doesn't occur. I think that's the wrong choice, as conceptually, joining continuation lines is usually the first step in parsing. In either case, we should document this in comments.
When we see a backslash not followed by newline, we return a pointer to the character immediately after the backslash (or after the carriage-return immediately following). That seems wrong to me; I would expect the return value to point at the backslash itself.
A single test is nowhere near enough for this function. I'd like to see a variety of input strings and predicates that together test the entire specification of the function. Some things that are not tested:
- the empty string
- input strings with backslash not followed by newline (including some with the backslash at the end of the string).
- input strings containing consecutive escaped newlines (
"\\\n\\\n"
)
- input strings containing carriage returns
- input strings with escaped and non-escaped CRLF
- predicate function that always returns true
- predicate functions that return true for
'\\'
- predicate functions that return true for newline or carriage-return
We should make the tests self-checking - return a non-zero value from main()
if any test returns the wrong result.
Modified code
I renamed src
to s
as we don't need the separate p
variable.
/** Skip characters from @src until @func returns false
* This function skips over escaped newlines before applying @func
* @func: isspace, isdigit, isalpha, etc can be used
* @return: a pointer into @src or to its terminating null character
*/
const char *skip (const char *s, int(*func)(int))
{
for (;;)
{
/* first skip escaped newlines */
if (*s == '\\')
{
if (s[1] == '\n')
{
s += 2;
continue;
}
if (s[1] == '\r' && s[2] == '\n')
{
s += 3;
continue;
}
}
/* then test whether we're looking at a skippable character */
if (!*s || !func((unsigned char)*s))
{
return s;
}
/* next character */
++s;
}
}
/* skip() for mutable strings */
char *skip_m (char *src, int(*func)(int))
{
return (char*)skip(src, func);
}
I've made only minor changes to the test. You should really replace this with a full unit-test suite.
#include <stdio.h>
int is_hspace(int ch) { return ch == ' ' || ch == '\t'; }
int main(void)
{
const char* p = skip("\\ \t \\\n \t Hello World", is_hspace);
printf("%s\n", p);
}
ispunct
? \$\endgroup\$strcspn
function. It returns the length of a string until the point where one out of several characters has been found.size_t i = strcspn(str, " \t\r\n"); /* whatever characters you like to stop at */ char* next = &str[i+1];
Seems it would be useful for your specific scenario. \$\endgroup\$