C error handling ergonomics are non-existent which means that everybody bakes ad-hoc library-specific conventions that are extremely error-prone.
You could argue that they're doing it wrong and you might have a point but if almost everybody gets it wrong maybe it's fair to blame the language itself a little bit.
BTW curl does seem to do what i wrote above, for example `curl_easy_init` returns a `CURL` object on success or NULL if there was an error [1] and `curl_easy_perform` returns a `CURLcode` value [2] that looks like it is used across the API to indicate errors.
If you have a kernel function returning a pointer and you think that you're supposed to check for NULL when it actually returns a ERR_PTR in case of errors you will not only fail to do the check but on top of that end up with a garbage pointer somewhere in your program. If you have a MMU and you try to de-reference the pointer you'll have a violent crash, which at least shouldn't be too hard to debug. If you feed the pointer to some hardware module or if you're working on an MMU-less system then Good Luck; Have Fun.
C doesn't have your back here. It doesn't let you signal how a function reports errors, it doesn't even let you tag nullable pointers.
bool parse(inp_type *a, out_type **b, out_error **c);
where the return value false indicates an error. In C++, you'd just have written something like: out_type parse(const inp_type& a);
and thrown an exception on error. ctx_t* ctx = ctx_new();
if (!ctx) ... fail ...
if (!ctx_parse(ctx, code)) {
show_error_message(ctx_erline(ctx), ctx_ercol(ctx));
... more fail ...
ctx_free(ctx); /* often done in a goto'd section to avoid missing frees*/
}
This also allows you to extend the APIs functionality, error information, etc in the future while remaining backwards compatible.You also have to worry about all of the ctx objects you've created along the way, to free them up as you recover from the low memory error.