Exactly. A wrapper that handles all of the edge cases properly and gives proper reporting just gets added to your own library of functions and the devs get used to using it. Much like the code for abstract data types like lists/hashmaps/etc which neither C nor the standard libraries provide.
Bonus points for having bespoke linting rules to point out the use of known “bad” functions.
In one old project we went through and replaced all instances of sprintf() with snprintf() or equivalent. Once we were happy that we’d got every occurrence we could then add lint rules to flag up any new use of sprintf() so that devs didn’t introduce new possible problems into the code.
(Obviously you can still introduce plenty of problems with snprintf() but we learned to give that more scrutiny.)
While snprintf() is better than sprintf(), I find that it's easy for people to not check if the return value is bigger than the provided size. Sure, it prevents a buffer overflow, but there could still be a string truncation problem.
Similar to how strlcpy() is not a slam dunk fix to the strcpy() problem.
> like lists/hashmaps/etc which neither C nor the standard libraries provide
There is a hashmap implementation though: https://man7.org/linux/man-pages/man3/hsearch.3.html