#include <stdarg.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>

#define weak_alias(name, aliasname) \
  extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));

typedef _IO_FILE* (*real__IO_fopen64_type) (char const* pathname, char const* mode);
typedef int (*fclose_type) (FILE* fp);
typedef ssize_t (*read_type) (int fd, void* buf, size_t count);

real__IO_fopen64_type real__IO_fopen64;
fclose_type real_fclose;
read_type real_read;

static FILE* cpuinfo;
static int initialized;

void initialize_overridelib(void)
{
  real__IO_fopen64 = (real__IO_fopen64_type)dlsym(RTLD_NEXT, "fopen64");
  real_fclose = (fclose_type)dlsym(RTLD_NEXT, "fclose");
  real_read = (read_type)dlsym(RTLD_NEXT, "read");
  initialized = 1;
}

// This is the actual first function in libc called from stdc++ (std::fstream::open).
_IO_FILE* _IO_fopen64(char const* filename, char const* mode)
{
  if (!initialized)
    initialize_overridelib();
  printf("Calling _IO_fopen64(\"%s\", \"%s\") = ", filename, mode);
  _IO_FILE* res = real__IO_fopen64(filename, mode);
  printf("{%d}\n", res->_fileno);
  if (!strcmp(filename, "/proc/cpuinfo"))
    cpuinfo = res;
  return res;
}
weak_alias (_IO_fopen64, fopen64);

int fclose(FILE* fp)
{
  if (!initialized)
    initialize_overridelib();
  if (cpuinfo && cpuinfo->_fileno == fp->_fileno)
    cpuinfo = NULL;
  printf("Calling fclose({%d}) = ", fp->_fileno);
  int res = real_fclose(fp);
  printf("%d\n", res);
  return res;
}

ssize_t read(int fd, void* buf, size_t count)
{
  if (!initialized)
    initialize_overridelib();
  printf("Calling read(%d, %p, %lu) = ", fd, buf, count);
  int res = real_read(fd, buf, count);
  if (cpuinfo && res > 0 && cpuinfo->_fileno == fd)
  {
    char locbuf[8192];
    assert(res <= sizeof(locbuf));
    memcpy(locbuf, buf, res);
    char const* in = locbuf;
    char* out = (char*)buf;
    char const* p = in;
    char const* one_past_last_newline = in;
    int has_colon = 0;
    int non_empty = 0;
    while (p < in + res)
    {
      if (*p == '\n')
      {
	ssize_t line_len = p - one_past_last_newline + 1;
	if (!has_colon || non_empty)
	{
	  memcpy(out, one_past_last_newline, line_len);
	  out += line_len;
	}
	one_past_last_newline = p + 1;
	has_colon = 0;
	non_empty = 0;
      }
      else if (has_colon && *p != ' ')
	non_empty = 1;
      if (*p == ':')
	has_colon = 1;
      ++p;
    }
    res = out - (char*)buf;
  }
  printf("%d\n", res);
  return res;
}

