The modus operandi for this is similar to that taken by PHP's implementation of such functions. It's comparitively memory-intensive, but is much faster than running a whole bunch of tests.
Basically, you set a mask -- an array of 256 null bytes -- and set those that correspond to characters you wish to trim. Then, rather than having to test if a character is in the set of characters to trim(O(n), or linear time on *ws), you just test once (O(1), or unit time) to see if the byte in question is set.
And of course, to trim(), you just wrap trim() around both ltrim() and rtrim().
One point of caution: these functions trim in place, so copy strings before trimming them. (Of course, if you usually want access to both pre- and post-trimmed strings, you could always make these malloc() a new string and return a pointer to it . . . )