Skip to content

Instantly share code, notes, and snippets.

@hippietrail
Last active March 23, 2024 19:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hippietrail/aa5e6d9a9db377b2c0c50c7ab8a35533 to your computer and use it in GitHub Desktop.
Save hippietrail/aa5e6d9a9db377b2c0c50c7ab8a35533 to your computer and use it in GitHub Desktop.
Example of how to wrap C printf type functions to add a new format specifier
// adds the %hb specifer to print a number of bytes in human-readable number of bytes/kb/mb
// there's no way to modify or build a va_list after handling some of them ourselves
// so we get the standard function to ignore them by treating them as zero-length strings
// this could be a problem for compilers that evaluate a number as a string pointer before truncating
// but it works with clang on Xcode 15.3
// there's no buffer overflow handling to make the code easy to follow - don't deploy as-is in real code!
void custom_fprintf(FILE* stream, const char* format_orig, ...) {
size_t format_orig_len = strlen(format_orig);
va_list args_orig, args_copy;
va_start(args_orig, format_orig);
va_copy(args_copy, args_orig);
char format_new[1024] = { 0 };
size_t outlen = 0;
const char *p = format_orig;
while (p - format_orig < format_orig_len) {
if (*p == '%') {
int num = va_arg(args_orig, int);
if (p + 2 < format_orig + format_orig_len && *(p + 1) == 'h' && *(p + 2) == 'b') {
// the %%.0s specifier makes the standard printf function effectively ignore an arg!
typedef struct { const char *u; int d; } ud;
ud unidiv = { .u = NULL, .d = 0 };
if (num < 1024)
unidiv = (ud) { .u = "bytes", .d = 1 };
else if (num < 1024 * 1024)
unidiv = (ud) { .u = "KB", .d = 1024 };
else if (num < 1024 * 1024 * 1024)
unidiv = (ud) { .u = "MB", .d = 1024 * 1024 };
else
unidiv = (ud) { .u = "GB", .d = 1024 * 1024 * 1024 };
outlen += snprintf(format_new + outlen, sizeof(format_new) - outlen, "%.2f %s%%.0s", (float)num / unidiv.d, unidiv.u);
p += 3;
continue;
}
}
format_new[outlen++] = *p;
p++;
}
format_new[outlen] = '\0';
va_end(args_orig);
vfprintf(stream, format_new, args_copy);
va_end(args_copy);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment