Last active
March 23, 2024 19:38
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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