From 7adfbfd2cf1d46144c41a75aa501938607a316b5 Mon Sep 17 00:00:00 2001
From: Simon Ruderich <simon@ruderich.org>
Date: Thu, 13 Jun 2013 12:59:12 +0200
Subject: [PATCH] Fix hook for putc_unlocked() with glibc.

putc_unlocked() is a macro which expands to __overflow() in some cases.
---
 configure.ac           | 5 +++++
 src/coloredstderr.c    | 7 +++++++
 src/hookmacros.h       | 7 +++++++
 tests/example.c        | 4 ++++
 tests/example.expected | 1 +
 5 files changed, 24 insertions(+)

diff --git a/configure.ac b/configure.ac
index 7ef71f9..09d90a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -47,6 +47,11 @@ AC_TYPE_SSIZE_T
 AC_C_INLINE
 AX_C___ATTRIBUTE__
 
+AC_CHECK_MEMBER([struct _IO_FILE._fileno],
+                [AC_DEFINE([HAVE_STRUCT__IO_FILE__FILENO], 1,
+                           [Define to 1 if `_fileno' is a member of `struct _IO_FILE'.])],
+                [],[[#include <libio.h>]])
+
 AC_FUNC_FORK
 AC_CHECK_FUNCS([memmove setenv],
                [],[AC_MSG_ERROR([function is required])])
diff --git a/src/coloredstderr.c b/src/coloredstderr.c
index cc58883..db6cb27 100644
--- a/src/coloredstderr.c
+++ b/src/coloredstderr.c
@@ -35,6 +35,9 @@
 #ifdef HAVE_ERROR_H
 # include <error.h>
 #endif
+#ifdef HAVE_STRUCT__IO_FILE__FILENO
+# include <libio.h>
+#endif
 
 /* Conflicting declaration in glibc. */
 #undef fwrite_unlocked
@@ -241,6 +244,10 @@ HOOK_FILE2(int, putc_unlocked, stream,
            int, c, FILE *, stream)
 HOOK_FILE1(int, putchar_unlocked, stdout,
            int, c)
+/* glibc defines (_IO_)putc_unlocked() to __overflow() in some cases. */
+#ifdef HAVE_STRUCT__IO_FILE__FILENO
+HOOK_FD2(int, __overflow, f->_fileno, _IO_FILE *, f, int, ch)
+#endif
 
 /* perror(3) */
 HOOK_VOID1(void, perror, STDERR_FILENO,
diff --git a/src/hookmacros.h b/src/hookmacros.h
index b5e2cb0..b8b70ec 100644
--- a/src/hookmacros.h
+++ b/src/hookmacros.h
@@ -117,6 +117,13 @@
         _HOOK_POST_FD_(fd) \
     }
 
+#define HOOK_FD2(type, name, fd, type1, arg1, type2, arg2) \
+    static type (*real_ ## name)(type1, type2); \
+    type name(type1 arg1, type2 arg2) { \
+        _HOOK_PRE_FD(type, name, fd) \
+        result = real_ ## name(arg1, arg2); \
+        _HOOK_POST_FD(fd) \
+    }
 #define HOOK_FD3(type, name, fd, type1, arg1, type2, arg2, type3, arg3) \
     static type (*real_ ## name)(type1, type2, type3); \
     type name(type1 arg1, type2 arg2, type3 arg3) { \
diff --git a/tests/example.c b/tests/example.c
index 7f7d2ad..0fee973 100644
--- a/tests/example.c
+++ b/tests/example.c
@@ -52,5 +52,9 @@ int main(int argc, char **argv unused) {
     write(42, "stderr ...\n", 11);
     write(471, "more on stdout\n", 15);
 
+    /* Glibc uses __overflow() for this ... */
+    putc_unlocked('x', stderr);
+    putc_unlocked('\n', stdout);
+
     return EXIT_SUCCESS;
 }
diff --git a/tests/example.expected b/tests/example.expected
index edb9c58..ecd2461 100644
--- a/tests/example.expected
+++ b/tests/example.expected
@@ -6,3 +6,4 @@
 >STDERR>more on stderr
 <STDERR<>STDERR>stderr ...
 <STDERR<more on stdout
+>STDERR>x<STDERR<
-- 
2.49.0