int str_to(char* dest, char const* src, size_t bufsiz, size_t *off);
It returns zero if the copy succeeds without truncating. If off is non-NULL, then it starts copying at dest + *off, and writes the lesser of (dest + *off + strlen(src)) or (bufsiz-1) into *off. If it's obliged to truncate, it returns the number of bytes it abandoned.
If you are calling it just once, you pass NULL to off. But if you're appending a series of strings, you initialize "size_t end = 0;" first, and pass &end to each subsequent call. All the calls get the same dest, the same bufsiz, and the same off.
It's not perfect. Forgetting to (re-)initialize end is easy. It would be better if checking only the final result of a sequence of calls sufficed; as it is, if *src is empty in the final call, you won't know whether one of the previous calls was obliged to truncate. But C is limited.