#include "shared.h"










int main(int argc, char *argv[]) {



  sink(_strdup(getenv("VAR")));
  sink(strdup(getenv("VAR")));
  sink(unmodeled_function(getenv("VAR")));

  char untainted_buf[100] = "";
  char buf[100] = "VAR = ";
  sink(strcat(buf, getenv("VAR")));

  sink(buf);
  sink(untainted_buf); // the two buffers would be conflated if we added flow through all partial chi inputs

  return 0;
}

typedef unsigned int inet_addr_retval;
inet_addr_retval inet_addr(const char *dotted_address);
void sink(inet_addr_retval);

void test_indirect_arg_to_model() {
    // This test is non-sensical but carefully arranged so we get data flow into
    // inet_addr not through the function argument but through its associated
    // read side effect.
    void *env_pointer = getenv("VAR"); // env_pointer is tainted, not its data.
    inet_addr_retval a = inet_addr((const char *)&env_pointer);
    sink(a);
}

class B {
    public:
    virtual void f(const char*) = 0;
};

class D1 : public B {};

class D2 : public D1 {
    public:
    void f(const char* p) override {}
};

class D3 : public D2 {
    public:
    void f(const char* p) override {
        sink(p);
    }
};

void test_dynamic_cast() {
    B* b = new D3();
    b->f(getenv("VAR")); // tainted

    ((D2*)b)->f(getenv("VAR")); // tainted
    static_cast<D2*>(b)->f(getenv("VAR")); // tainted
    dynamic_cast<D2*>(b)->f(getenv("VAR")); // tainted
    reinterpret_cast<D2*>(b)->f(getenv("VAR")); // tainted

    B* b2 = new D2();
    b2->f(getenv("VAR"));

    ((D2*)b2)->f(getenv("VAR"));
    static_cast<D2*>(b2)->f(getenv("VAR"));
    dynamic_cast<D2*>(b2)->f(getenv("VAR"));
    reinterpret_cast<D2*>(b2)->f(getenv("VAR"));

    dynamic_cast<D3*>(b2)->f(getenv("VAR")); // tainted [FALSE POSITIVE]
}

namespace std {
  template< class T >
  T&& move( T&& t ) noexcept;
}

void test_std_move() {
  sink(std::move(getenv("VAR")));
}

void flow_to_outparam(char ** ret, char *arg) {
    *ret = arg; 
}

void test_outparams() {
    char *p2 = nullptr;
    flow_to_outparam(&p2, getenv("VAR"));
    sink(p2); // tainted
}




struct XY {
  int x;
  int y;
};

void taint_y(XY *xyp) {
  int tainted = getenv("VAR")[0];
  xyp->y = tainted;
}

void test_conflated_fields3() {
  XY xy;
  xy.x = 0;
  taint_y(&xy);
  sink(xy.x); // not tainted
}

struct Point {
  int x;
  int y;

  void callSink() {
    sink(this->x); // tainted
    sink(this->y); // not tainted
  }
};

void test_conflated_fields1() {
  Point p;
  p.x = getenv("VAR")[0];
  sink(p.x); // tainted
  sink(p.y); // not tainted
  p.callSink();
}

void taint_x(Point *pp) {
  pp->x = getenv("VAR")[0];
}

void y_to_sink(Point *pp) {
  sink(pp->y); // not tainted
}

void test_conflated_fields2() {
  Point p;
  taint_x(&p);
  y_to_sink(&p);
}
