The abominable strtok()
zefonseca.com
zefonseca.com
I agree strongly with the other comments on this thread that strsep(3) is just peachy, even though it "alters its inputs". Unlike in functional programs, reasonable destructive functions are to be preferred in C programs; it's easier to work around destructiveness than it is to track state (and/or to go through contortions to pretend that you aren't tracking state).
Secondly, this is C code--it's pretty low-level. Yes, strsep() messes with your input. But that is very well documented. If you don't want it to, strdup() beforehand (or strndup() if you don't trust the input).
His whole example of strtok() dying on a character constant is stupid--why on earth would you do that? If you've got a string constant you may as well just have the constant array and save yourself the parsing headache.
This isn't rocket science.
The people who designed the C string functions should have been made to pee in a cup before they were allowed into the building. So much trouble could have been avoided if they had only been consistent about using memory block sizes as parameters rather than "string" (sic) lengths.
I agree with the message: don't use strtok, it has unpleasant side effects that you probably don't want. I do not feel this article does a good job of presenting that message. It spends too much time on pathologically bad examples of strtok usage while only briefly mentioning (and providing no example code for) any of the alternatives.
I've always, in both my dev career and in security research, felt that manual checking is dumb. In 99.999% of cases, the only option given to code when allocations fail is to start a chain of events that ends the program. Inflating each and every allocation (or, more likely, missing the little things that allocate silently, like strdup) into 4-5 lines of code seems wasteful.
What I advocate is what my friend Danny told me he did at Juno in the mid '90s: preload a wrapper on malloc (don't use silly wrappers like "xmalloc") that catches faults and does something intelligent; centralize your handling of allocation failures, instead of trying to graft it onto each and every allocation point.
The nice thing about my philosophy: it means that when you write normal, non-runtime code, you just pretend malloc never fails. Your code is cleaner, and it's probably safer.
Similarly an embedded program might not want to reboot itself if there aren't enough memory resources to carry out a command (due to other commands being processed at the same time).
But you are right, a lot of the time (perhaps even the majority of the time) it really is perfectly acceptable to just die if you can't malloc().
And it really does take a lot of effort to manage memory like that, but if it is done right it can really make things work robustly.
It's actually easier to do things like this if you aren't hand-crafting individual little alloc-check-handle routines.
Though if it's linux it'll just kill you anyway :-). (Yeah, I know you can turn the OOM killer off).
char *tok = (char *)malloc(SOMEMEMORY);
tok = strtok(data, ":");
I don't actually care whether the malloc succeeded or whether or not he checked for an error, as the second line blows away the only reference to the potentially allocated memory. Whatever may or may not have been allocated has now officially been leaked.For Win32 developers who don't have the glib g_strsplit function, you can use strtok_s which is detailed on MSDN here: http://msdn.microsoft.com/en-us/library/ftsafwz3(VS.80).aspx
strtok_s is re-entrant, thread-safe, and uses no global data, but keep in mind it still modifies your input string.
I think I that I have used it in shipping code two or three times. These days I don't do much in C so it's unlikely I'll ever use it again.
This post actually gave me an impetus to go back and add tokenization to my string library that's built around this idea.
Just as map and reduce in Python is "unpythonic", interfaces that mint new temp strings to process what should be a simple charstar are un-c-like, and should be avoided.
That's not what I'm doing at all, though. I'm just giving char *'s and size_t's for portions of the original character array, not duplicating subsections of the array at all.
It's also part of a larger library that consistently uses pointer/length pairs to designate strings as opposed to null termination, which admittedly is quite non-C. No clue whether it's actually a good idea.
EDIT: If anyone's actually curious, I have it on github: http://github.com/philwelch/string