Skip to content

Instantly share code, notes, and snippets.

@andrewjbennett
Created April 28, 2019 12:43
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 andrewjbennett/1cc81fa6854bd84ec6155a92efb3ddda to your computer and use it in GitHub Desktop.
Save andrewjbennett/1cc81fa6854bd84ec6155a92efb3ddda to your computer and use it in GitHub Desktop.
A hack to print arbitrary text before any printf'd text.
#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");
}
// 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