// RUN: %clang_analyze_cc1 -verify %s \
// RUN:   -analyzer-checker=core \
// RUN:   -analyzer-checker=unix.Errno \
// RUN:   -analyzer-checker=unix.Stream \
// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
// RUN:   -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true \
// RUN:   -analyzer-checker=debug.ExprInspection

// enable only StdCLibraryFunctions checker
// RUN: %clang_analyze_cc1 -verify %s \
// RUN:   -analyzer-checker=core \
// RUN:   -analyzer-checker=unix.Errno \
// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
// RUN:   -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true \
// RUN:   -analyzer-checker=debug.ExprInspection

#include "Inputs/system-header-simulator.h"
#include "Inputs/errno_var.h"

void clang_analyzer_eval(int);

const char *WBuf = "123456789";
char RBuf[10];

void test_freopen(FILE *F) {
  F = freopen("xxx", "w", F);
  if (F) {
    if (errno) {} // expected-warning{{undefined}}
  } else {
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  }
}

void test_fread(FILE *F) {
  size_t Ret = fread(RBuf, 1, 10, F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret == 10) {
    if (errno) {} // expected-warning{{undefined}}
  } else {
    clang_analyzer_eval(Ret < 10); // expected-warning {{TRUE}}
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void test_fwrite(FILE *F) {
  size_t Ret = fwrite(WBuf, 1, 10, F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret == 10) {
    if (errno) {} // expected-warning{{undefined}}
  } else {
    clang_analyzer_eval(Ret < 10); // expected-warning {{TRUE}}
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void test_fgetc(FILE *F) {
  int Ret = fgetc(F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret != EOF) {
    if (errno) {} // expected-warning {{undefined}}
  } else {
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
                                     // expected-warning@-1 {{FALSE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void test_fputc(FILE *F) {
  int Ret = fputc('a', F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret != EOF) {
    clang_analyzer_eval(Ret == 'a'); // expected-warning {{TRUE}}
    if (errno) {} // expected-warning {{undefined}}
  } else {
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void test_fgets(char *Buf, int N, FILE *F) {
  char *Ret = fgets(Buf, N, F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  clang_analyzer_eval(Buf != NULL); // expected-warning {{TRUE}}
  clang_analyzer_eval(N >= 0); // expected-warning {{TRUE}}
  if (Ret == Buf) {
    if (errno) {} // expected-warning {{undefined}}
  } else {
    clang_analyzer_eval(Ret == 0); // expected-warning {{TRUE}}
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
                                     // expected-warning@-1 {{FALSE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}

  char Buf1[10];
  Ret = fgets(Buf1, 11, F); // expected-warning {{The 1st argument to 'fgets' is a buffer with size 10}}
}

void test_fgets_bufsize(FILE *F) {
  char Buf[10];
  fgets(Buf, 11, F); // expected-warning {{The 1st argument to 'fgets' is a buffer with size 10}}
}

void test_fputs(char *Buf, FILE *F) {
  int Ret = fputs(Buf, F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  clang_analyzer_eval(Buf != NULL); // expected-warning {{TRUE}}
  if (Ret >= 0) {
    if (errno) {} // expected-warning {{undefined}}
  } else {
    clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void test_ungetc(FILE *F) {
  int Ret = ungetc('X', F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret == 'X') {
    if (errno) {} // expected-warning {{undefined}}
  } else {
    clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void test_ungetc_EOF(FILE *F, int C) {
  int Ret = ungetc(EOF, F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
  clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  Ret = ungetc(C, F);
  if (Ret == EOF) {
    clang_analyzer_eval(C == EOF); // expected-warning {{TRUE}}
                                   // expected-warning@-1{{FALSE}}
  }
}

void test_fclose(FILE *F) {
  int Ret = fclose(F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret == 0) {
    if (errno) {} // expected-warning{{undefined}}
  } else {
    clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void test_fseek(FILE *F) {
  int Ret = fseek(F, SEEK_SET, 1);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret == 0) {
    if (errno) {} // expected-warning{{undefined}}
  } else {
    clang_analyzer_eval(Ret == -1); // expected-warning {{TRUE}}
    clang_analyzer_eval(errno != 0); // expected-warning {{TRUE}}
  }
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void check_fgetpos(FILE *F) {
  errno = 0;
  fpos_t Pos;
  int Ret = fgetpos(F, &Pos);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret)
    clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
  else
    clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                     // expected-warning@-1{{FALSE}}
  if (errno) {} // no-warning
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void check_fsetpos(FILE *F) {
  errno = 0;
  fpos_t Pos;
  int Ret = fsetpos(F, &Pos);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret)
    clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
  else
    clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                     // expected-warning@-1{{FALSE}}
  if (errno) {} // no-warning
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void check_ftell(FILE *F) {
  errno = 0;
  long Ret = ftell(F);
  clang_analyzer_eval(F != NULL); // expected-warning {{TRUE}}
  if (Ret == -1) {
    clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
  } else {
    clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                     // expected-warning@-1{{FALSE}}
    clang_analyzer_eval(Ret >= 0); // expected-warning{{TRUE}}
  }
  if (errno) {} // no-warning
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void test_rewind(FILE *F) {
  errno = 0;
  rewind(F);
  clang_analyzer_eval(F != NULL); // expected-warning{{TRUE}}
  clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                   // expected-warning@-1{{FALSE}}
  rewind(F);
}

void test_fflush(FILE *F) {
  errno = 0;
  int Ret = fflush(F);
  clang_analyzer_eval(F != NULL); // expected-warning{{TRUE}}
                                  // expected-warning@-1{{FALSE}}
  if (Ret == EOF) {
    clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
  } else {
    clang_analyzer_eval(Ret == 0); // expected-warning{{TRUE}}
    clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                     // expected-warning@-1{{FALSE}}
  }
}

void test_feof(FILE *F) {
  errno = 0;
  feof(F);
  clang_analyzer_eval(F != NULL); // expected-warning{{TRUE}}
  if (errno) {} // no-warning
  clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                   // expected-warning@-1{{FALSE}}
}

void test_ferror(FILE *F) {
  errno = 0;
  ferror(F);
  clang_analyzer_eval(F != NULL); // expected-warning{{TRUE}}
  if (errno) {} // no-warning
  clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                   // expected-warning@-1{{FALSE}}
}

void test_clearerr(FILE *F) {
  errno = 0;
  clearerr(F);
  clang_analyzer_eval(F != NULL); // expected-warning{{TRUE}}
  if (errno) {} // no-warning
  clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                   // expected-warning@-1{{FALSE}}
}

void test_fileno(FILE *F) {
  errno = 0;
  int A = fileno(F);
  clang_analyzer_eval(F != NULL);  // expected-warning{{TRUE}}
  clang_analyzer_eval(A >= 0);     // expected-warning{{TRUE}}
  if (errno) {}                    // no-warning
  clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}
                                   // expected-warning@-1{{FALSE}}
}

void freadwrite_zerosize(FILE *F) {
  fwrite(WBuf, 1, 0, F);
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
  fwrite(WBuf, 0, 1, F);
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
  fread(RBuf, 1, 0, F);
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
  fread(RBuf, 0, 1, F);
  clang_analyzer_eval(feof(F)); // expected-warning {{UNKNOWN}}
  clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}

void freadwrite_zerosize_errno(FILE *F, int A) {
  switch (A) {
  case 1:
    fwrite(WBuf, 1, 0, F);
    if (errno) {} // expected-warning{{undefined}}
    break;
  case 2:
    fwrite(WBuf, 0, 1, F);
    if (errno) {} // expected-warning{{undefined}}
    break;
  case 3:
    fread(RBuf, 1, 0, F);
    if (errno) {} // expected-warning{{undefined}}
    break;
  case 4:
    fread(RBuf, 0, 1, F);
    if (errno) {} // expected-warning{{undefined}}
    break;
  }
}
