Created
April 28, 2019 12:43
-
-
Save andrewjbennett/1cc81fa6854bd84ec6155a92efb3ddda to your computer and use it in GitHub Desktop.
A hack to print arbitrary text before any printf'd text.
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
#include <stdio.h> | |
//////////////////////////////////////////////////////////////////////// | |
// Fake version of pokedex.c // | |
//////////////////////////////////////////////////////////////////////// | |
// Pretend this takes in a pokedex... | |
void detail_pokemon(void) { | |
printf("Id: #001\n"); | |
printf("Name: Bulbasaur\n"); | |
printf("Weight: 6.9kg\n"); | |
printf("Height: 0.7m\n"); | |
printf("Type: Poison Grass\n"); | |
} | |
//////////////////////////////////////////////////////////////////////// | |
// Fake version of test_pokedex.c // | |
//////////////////////////////////////////////////////////////////////// | |
// Prototypes for what the "real" printf-y functions look like. | |
int __real_printf(const char *format, ...); | |
// This macro is just to make printing be nicer than having to type | |
// __real_printf each time... | |
// It's "r" for "real printf" ;) | |
#define rprintf __real_printf | |
int main(int argc, char *argv[]) { | |
rprintf("Welcome to the COMP1511 Pokedex Tests!\n"); | |
rprintf("\n=================== Pokedex Tests ===================\n"); | |
rprintf(">> add_pokemon test 1:\n"); | |
rprintf(" ... Creating a new Pokedex\n"); | |
rprintf(" ... Creating Bulbasaur\n"); | |
rprintf(" ... Adding Bulbasaur to the Pokedex\n"); | |
rprintf(" ... Creating Ivysaur\n"); | |
rprintf(" ... Adding Ivysaur to the Pokedex\n"); | |
rprintf(" ...Printing the details of the current Pokemon\n"); | |
detail_pokemon(); | |
rprintf("\nAll Pokedex tests passed, you are Awesome!\n"); | |
} |
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
// Some horrible hacky code that I almost feel ashamed to put my name on. | |
// By Andrew Bennett <andrew.bennett@unsw.edu.au>, 2019. | |
// | |
// Extremely hacky code to wrap all prints in a given piece of code, and | |
// to print "[MAGIC] " at the start of each line that's printed out. | |
// | |
// The hard part is dealing with prints that have newlines in the middle | |
// of them -- e.g. "printf("hello\nworld\n")` should be printed out as | |
// [MAGIC] hello | |
// [MAGIC] world | |
// -- despite coming from a single printf call. | |
// | |
// So, it's not *quite* as simple as "just" wrapping printf -- instead | |
// we do some hacks with strchr to split the printed string up by | |
// newlines, and print the "[MAGIC]" as appropriate. | |
// | |
// Note: compile with "-Wl,-wrap=printf". | |
#include <stdarg.h> | |
#include <string.h> | |
#include <stdio.h> | |
// Prototypes for what the "real" printf-y functions look like. | |
int __real_printf(const char *format, ...); | |
#define MAX 65536 | |
// Global variables -- the true sign of hacky code. | |
static int PRINTED_NEWLINE = 1; | |
static char buf[MAX]; | |
// Macros, to simplify the process of doing the same thing for multiple | |
// print functions (heavily cut down for this example since I only | |
// support printf) | |
#define MAGIC_PRINT \ | |
if (PRINTED_NEWLINE) {\ | |
__real_printf("[MAGIC!] "); \ | |
PRINTED_NEWLINE = 0; \ | |
} | |
#define NORMAL_PRINT \ | |
snprintf(buf, MAX, "%s", format); \ | |
PRINT(buf) | |
// Approach: | |
// | |
// 1. Only want to print [MAGIC] if we have previously printed a | |
// newline so maybe have a global variable flag for if we've just | |
// printed a newline | |
// | |
// 2. OTOH, *always* want to print [MAGIC] after a newline. | |
// So if we get a newline in the middle of the string, we want to | |
// re-print [MAGIC] | |
// char *strchr(const char *s, int c); | |
// Return a pointer to the first occurrence of the character c in the string s. | |
static inline void print(char *buf) { | |
// we want to print everything from the start of the string until | |
// we find a newline. | |
// | |
// if we find a newline, print it out, then set our flag to 1 | |
// if our flag is 1, print [MAGIC] first | |
// approach: use strchr to go through the string as chunks separated | |
// by newlines. | |
// if x is NULL, there's no newline after us, so just print the | |
// string and stop. | |
// if x isn't NULL, we want to print: | |
// - everything from the start until that newline | |
// - a newline | |
// - and set the flag to 1 | |
char *x = strchr(buf, '\n'); | |
while ((x = strchr(buf, '\n')) != NULL) { | |
MAGIC_PRINT; // maybe print [MAGIC] | |
// x points to a newline | |
// turn that x into a NULL | |
// print out everything up until that point | |
// then restart the buffer to be one past that ex-newline NULL | |
*x = '\0'; | |
__real_printf("%s\n", buf); | |
PRINTED_NEWLINE = 1; | |
buf = x+1; | |
} | |
if (x == NULL && buf[0] != '\0') { | |
MAGIC_PRINT; // maybe print [MAGIC] | |
__real_printf("%s", buf); | |
// and we're done | |
} | |
} | |
// Wrap printf. This function gets called whenever printf is called in | |
// normal code | |
int __wrap_printf(const char *format, ...) { | |
va_list argptr; | |
va_start(argptr, format); | |
vsnprintf(buf, MAX, format, argptr); | |
print(buf); | |
va_end(argptr); | |
fflush(stdout); | |
// This will break any code that relies on checking the return value | |
// from printf, but we're not aiming for portable or robust here, | |
// just "it works" >_> | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment