/**
 *  CPE 101 Winter 2008
 *  -------------------
 *  Program to test students' Lab 8.
 *
 *  @author Kevin O'Gorman
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/* pull in the standard library string things. */
#include <string.h>

#define CATCH_SIGNED_CHAR

int mystrlen(char *);
char * mystrcpy(char*, char*);
char * mystrncpy(char*, char*, size_t);
char * mystrchr(char *, int);
char * mystrrchr(char *, int);
char * mystrstr(char *, char *);
char * mystrcat(char *, char *);
char * mystrncat(char *, char *, size_t);
int mystrcmp(char *, char *);
int mystrncmp(char *, char *, size_t);

void check(int logic);
void newset(int setnum);
void endset(void);
void clearbufs(char *buf1, char *buf2);
void checkcpy(char *dest1, char *dest2, char *src);
void checkncpy(char *dest1, char *dest2, char *src);
void checkcat(char *dest1, char *dest2, char *src1, char *src2);
void checkncat(char *dest1, char *dest2, char *src1, char *src2);
void checkstrstr(char *s1, char *s2);
void checkstrchr(char *s, int ch);
void checkstrrchr(char *s, int ch);
void checkcmp(char *s1, char *s2);
void checkncmp(char *s1, char *s2);
int signum(int x);

/* Gobal data.  Not usually allowed. */
int errors = 0;
int tests = 0;
int set;
int setErrors;
int test;

/* There's no safety in it, but we can try. */
#define SAFETY 4096
#define LIMLIM 256
#define CLEAR_TO '\xff'

int
main(int argc, char *argv[])
{
   char s1[] = "";
   char s2[] = "a";
   char s3[] = "ab";
   char s4[] = "xxxxabxxx";
   char s5[] = "A beverage that was almost but not quite entirely unlike tea.";
   char s6[] = "On breaking the ice:\nCandy is dandy\nBut liquor is quicker.\n"
         "         -- Ogden Nash\n";
#ifdef CATCH_SIGNED_CHAR
   char s7[] = "\xff\x7f\x80\x01";
#else
   char s7[] = "\x01\x7f\x80\xff";
#endif

   char ss1[] = "a";
   char ss2[] = "tea";
   char ss3[] = "xxxx";
   char ss4[] = "liquor";
   char ss5[] = "\x80";

   char buf1[SAFETY];
   char buf2[SAFETY];

   /* First we test "my" functs that are likely to be used by other "my"functs. */
   /* mystrlen */
   newset(1);
   check(strlen(s1) == mystrlen(s1));
   check(strlen(s2) == mystrlen(s2));
   check(strlen(s3) == mystrlen(s3));
   check(strlen(s4) == mystrlen(s4));
   check(strlen(s5) == mystrlen(s5));
   check(strlen(s6) == mystrlen(s6));
   check(strlen(s7) == mystrlen(s7));
   endset();

   /* mystrcpy */
   newset(2);
   checkcpy(buf1, buf2, s1);
   checkcpy(buf1, buf2, s2);
   checkcpy(buf1, buf2, s3);
   checkcpy(buf1, buf2, s4);
   checkcpy(buf1, buf2, s5);
   checkcpy(buf1, buf2, s6);
   checkcpy(buf1, buf2, s7);
   endset();

   /* mystrncpy */
   newset(3);
   checkncpy(buf1, buf2, s1);
   checkncpy(buf1, buf2, s2);
   checkncpy(buf1, buf2, s3);
   checkncpy(buf1, buf2, s4);
   checkncpy(buf1, buf2, s5);
   checkncpy(buf1, buf2, s6);
   checkncpy(buf1, buf2, s7);
   endset();

   /* mystrcat */
   newset(4);
   checkcat(buf1, buf2, s1, s1);
   checkcat(buf1, buf2, s1, s2);
   checkcat(buf1, buf2, s1, s3);
   checkcat(buf1, buf2, s1, s4);
   checkcat(buf1, buf2, s1, s5);
   checkcat(buf1, buf2, s1, s6);
   checkcat(buf1, buf2, s1, s7);

   checkcat(buf1, buf2, s1, s1);
   checkcat(buf1, buf2, s2, s1);
   checkcat(buf1, buf2, s3, s1);
   checkcat(buf1, buf2, s4, s1);
   checkcat(buf1, buf2, s5, s1);
   checkcat(buf1, buf2, s6, s1);
   checkcat(buf1, buf2, s7, s1);

   checkcat(buf1, buf2, s1, s7);
   checkcat(buf1, buf2, s2, s6);
   checkcat(buf1, buf2, s3, s5);
   checkcat(buf1, buf2, s4, s4);
   checkcat(buf1, buf2, s5, s3);
   checkcat(buf1, buf2, s6, s2);
   checkcat(buf1, buf2, s7, s1);
   endset();

   /* mystrncat */
   newset(5);
   checkncat(buf1, buf2, s1, s1);
   checkncat(buf1, buf2, s1, s2);
   checkncat(buf1, buf2, s1, s3);
   checkncat(buf1, buf2, s1, s4);
   checkncat(buf1, buf2, s1, s5);
   checkncat(buf1, buf2, s1, s6);
   checkncat(buf1, buf2, s1, s7);

   checkncat(buf1, buf2, s1, s1);
   checkncat(buf1, buf2, s2, s1);
   checkncat(buf1, buf2, s3, s1);
   checkncat(buf1, buf2, s4, s1);
   checkncat(buf1, buf2, s5, s1);
   checkncat(buf1, buf2, s6, s1);
   checkncat(buf1, buf2, s7, s1);

   checkncat(buf1, buf2, s1, s7);
   checkncat(buf1, buf2, s2, s6);
   checkncat(buf1, buf2, s3, s5);
   checkncat(buf1, buf2, s4, s4);
   checkncat(buf1, buf2, s5, s3);
   checkncat(buf1, buf2, s6, s2);
   checkncat(buf1, buf2, s7, s1);
   endset();

   /* mystrstr */
   newset(6);
   checkstrstr(s1, ss1);
   checkstrstr(s1, ss2);
   checkstrstr(s1, ss3);
   checkstrstr(s1, ss4);
   checkstrstr(s1, ss5);

   checkstrstr(s2, ss1);
   checkstrstr(s2, ss2);
   checkstrstr(s2, ss3);
   checkstrstr(s2, ss4);
   checkstrstr(s2, ss5);

   checkstrstr(s3, ss1);
   checkstrstr(s3, ss2);
   checkstrstr(s3, ss3);
   checkstrstr(s3, ss4);
   checkstrstr(s3, ss5);

   checkstrstr(s4, ss1);
   checkstrstr(s4, ss2);
   checkstrstr(s4, ss3);
   checkstrstr(s4, ss4);
   checkstrstr(s4, ss5);

   checkstrstr(s5, ss1);
   checkstrstr(s5, ss2);
   checkstrstr(s5, ss3);
   checkstrstr(s5, ss4);
   checkstrstr(s5, ss5);

   checkstrstr(s6, ss1);
   checkstrstr(s6, ss2);
   checkstrstr(s6, ss3);
   checkstrstr(s6, ss4);
   checkstrstr(s6, ss5);

   checkstrstr(s7, ss1);
   checkstrstr(s7, ss2);
   checkstrstr(s7, ss3);
   checkstrstr(s7, ss4);
   checkstrstr(s7, ss5);

   endset();

   /* mystrchr */
   newset(7);
   checkstrchr(s1, 'a');
   checkstrchr(s1, 'A');
   checkstrchr(s1, 'z');
   checkstrchr(s1, 'Z');
   checkstrchr(s1, '\x01');
   checkstrchr(s1, '\x7f');
   checkstrchr(s1, '\x80');
   checkstrchr(s1, '\xff');

   checkstrchr(s2, 'a');
   checkstrchr(s2, 'A');
   checkstrchr(s2, 'z');
   checkstrchr(s2, 'Z');
   checkstrchr(s2, '\x01');
   checkstrchr(s2, '\x7f');
   checkstrchr(s2, '\x80');
   checkstrchr(s2, '\xff');

   checkstrchr(s3, 'a');
   checkstrchr(s3, 'A');
   checkstrchr(s3, 'z');
   checkstrchr(s3, 'Z');
   checkstrchr(s3, '\x01');
   checkstrchr(s3, '\x7f');
   checkstrchr(s3, '\x80');
   checkstrchr(s3, '\xff');

   checkstrchr(s4, 'a');
   checkstrchr(s4, 'A');
   checkstrchr(s4, 'z');
   checkstrchr(s4, 'Z');
   checkstrchr(s4, '\x01');
   checkstrchr(s4, '\x7f');
   checkstrchr(s4, '\x80');
   checkstrchr(s4, '\xff');

   checkstrchr(s5, 'a');
   checkstrchr(s5, 'A');
   checkstrchr(s5, 'z');
   checkstrchr(s5, 'Z');
   checkstrchr(s5, '\x01');
   checkstrchr(s5, '\x7f');
   checkstrchr(s5, '\x80');
   checkstrchr(s5, '\xff');

   checkstrchr(s6, 'a');
   checkstrchr(s6, 'A');
   checkstrchr(s6, 'z');
   checkstrchr(s6, 'Z');
   checkstrchr(s6, '\x01');
   checkstrchr(s6, '\x7f');
   checkstrchr(s6, '\x80');
   checkstrchr(s6, '\xff');

   checkstrchr(s7, 'a');
   checkstrchr(s7, 'A');
   checkstrchr(s7, 'z');
   checkstrchr(s7, 'Z');
   checkstrchr(s7, '\x01');
   checkstrchr(s7, '\x7f');
   checkstrchr(s7, '\x80');
   checkstrchr(s7, '\xff');

   endset();

   /* mystrrchr */
   newset(8);
   checkstrrchr(s1, 'a');
   checkstrrchr(s1, 'A');
   checkstrrchr(s1, 'z');
   checkstrrchr(s1, 'Z');
   checkstrrchr(s1, '\x01');
   checkstrrchr(s1, '\x7f');
   checkstrrchr(s1, '\x80');
   checkstrrchr(s1, '\xff');

   checkstrrchr(s2, 'a');
   checkstrrchr(s2, 'A');
   checkstrrchr(s2, 'z');
   checkstrrchr(s2, 'Z');
   checkstrrchr(s2, '\x01');
   checkstrrchr(s2, '\x7f');
   checkstrrchr(s2, '\x80');
   checkstrrchr(s2, '\xff');

   checkstrrchr(s3, 'a');
   checkstrrchr(s3, 'A');
   checkstrrchr(s3, 'z');
   checkstrrchr(s3, 'Z');
   checkstrrchr(s3, '\x01');
   checkstrrchr(s3, '\x7f');
   checkstrrchr(s3, '\x80');
   checkstrrchr(s3, '\xff');

   checkstrrchr(s4, 'a');
   checkstrrchr(s4, 'A');
   checkstrrchr(s4, 'z');
   checkstrrchr(s4, 'Z');
   checkstrrchr(s4, '\x01');
   checkstrrchr(s4, '\x7f');
   checkstrrchr(s4, '\x80');
   checkstrrchr(s4, '\xff');

   checkstrrchr(s5, 'a');
   checkstrrchr(s5, 'A');
   checkstrrchr(s5, 'z');
   checkstrrchr(s5, 'Z');
   checkstrrchr(s5, '\x01');
   checkstrrchr(s5, '\x7f');
   checkstrrchr(s5, '\x80');
   checkstrrchr(s5, '\xff');

   checkstrrchr(s6, 'a');
   checkstrrchr(s6, 'A');
   checkstrrchr(s6, 'z');
   checkstrrchr(s6, 'Z');
   checkstrrchr(s6, '\x01');
   checkstrrchr(s6, '\x7f');
   checkstrrchr(s6, '\x80');
   checkstrrchr(s6, '\xff');

   checkstrrchr(s7, 'a');
   checkstrrchr(s7, 'A');
   checkstrrchr(s7, 'z');
   checkstrrchr(s7, 'Z');
   checkstrrchr(s7, '\x01');
   checkstrrchr(s7, '\x7f');
   checkstrrchr(s7, '\x80');
   checkstrrchr(s7, '\xff');
   endset();

   /* mystrcmp */
   newset(9);
   checkcmp(s1, s1);
   checkcmp(s1, s2);
   checkcmp(s1, s3);
   checkcmp(s1, s4);
   checkcmp(s1, s5);
   checkcmp(s1, s6);
   checkcmp(s1, s7);

   checkcmp(s1, s1);
   checkcmp(s2, s1);
   checkcmp(s3, s1);
   checkcmp(s4, s1);
   checkcmp(s5, s1);
   checkcmp(s6, s1);
   checkcmp(s7, s1);

   checkcmp(s1, s7);
   checkcmp(s2, s6);
   checkcmp(s3, s5);
   checkcmp(s4, s4);
   checkcmp(s5, s3);
   checkcmp(s6, s2);
   checkcmp(s7, s1);
   endset();

   /* mystrncmp */
   newset(10);
   checkncmp(s1, s1);
   checkncmp(s1, s2);
   checkncmp(s1, s3);
   checkncmp(s1, s4);
   checkncmp(s1, s5);
   checkncmp(s1, s6);
   checkncmp(s1, s7);

   checkncmp(s1, s1);
   checkncmp(s2, s1);
   checkncmp(s3, s1);
   checkncmp(s4, s1);
   checkncmp(s5, s1);
   checkncmp(s6, s1);
   checkncmp(s7, s1);

   checkncmp(s1, s7);
   checkncmp(s2, s6);
   checkncmp(s3, s5);
   checkncmp(s4, s4);
   checkncmp(s5, s3);
   checkncmp(s6, s2);
   checkncmp(s7, s1);
   endset();

   printf("\n\n");
   if (errors)
   {
      printf("    >>> Failed %d tests out of %d.\n", errors, tests);
      return EXIT_FAILURE;
   }
   else
   {
      printf("    >>> Passed all %d tests.\n", tests);
      return EXIT_SUCCESS;
   }
}

void check(int logic)
{
   if (!logic) {
      printf("** Failed set %d test %d\n", set, test);
      errors++;
      setErrors++;
   }
   test++;
   tests++;
}

void newset(int theSet)
{
   set = theSet;
   setErrors = 0;
   test = 1;
   printf("\n");
   printf("Beginning set %d\n", set);
}

void endset(void)
{
   if (!setErrors)
   {
      printf("++ Passed set %d\n", set);
   }
   else
   {
      printf("-- Failed set %d with %d errors.\n", set, setErrors);
   }
}


void
checkcpy(char *dest1, char *dest2, char *src)
{
   char *result;
   memset(dest1, CLEAR_TO, SAFETY);
   memset(dest2, CLEAR_TO, SAFETY);
   result = mystrcpy(dest1, src);
   strcpy(dest2, src);
   check(memcmp(dest1, dest2, SAFETY) == 0 && result == dest1);
}

void
checkncpy(char *dest1, char *dest2, char *src)
{
   int i;
   int okay = 1;
   char *result;

   for (i = 0; i < LIMLIM; i++) {
      memset(dest1, CLEAR_TO, SAFETY);
      memset(dest2, CLEAR_TO, SAFETY);
      result = mystrncpy(dest1, src, test);
      strncpy(dest2, src, test);
      okay = (okay && memcmp(dest1, dest2, SAFETY) == 0
            && result == dest1);
   }
   check(okay);
}

void
checkcat(char *dest1, char *dest2, char *src1, char *src2)
{
   char *result;

   memset(dest1, CLEAR_TO, SAFETY);
   memset(dest2, CLEAR_TO, SAFETY);
   strcpy(dest1, src1);
   strcpy(dest2, src1);
   result = mystrcat(dest1, src2);
   strcat(dest2, src2);
   check(memcmp(dest1, dest2, SAFETY) == 0
         && result == dest1);
}

void
checkncat(char *dest1, char *dest2, char *src1, char *src2)
{
   int i;
   int okay = 1;
   char *result;

   for (i = 0; i < LIMLIM; i++) {
      memset(dest1, CLEAR_TO, SAFETY);
      memset(dest2, CLEAR_TO, SAFETY);
      strcpy(dest1, src1);
      strcpy(dest2, src1);
      result = mystrncat(dest1, src2, test);
      strncat(dest2, src2, test);
      okay = (okay && memcmp(dest1, dest2, SAFETY) == 0
            && result == dest1);
   }
   check(okay);
}

void
checkstrstr(char *s1, char *s2)
{
   check(strstr(s1, s2) == mystrstr(s1, s2));
}

void
checkstrchr(char *s, int ch)
{
   check(strchr(s, ch) == mystrchr(s, ch));
}

void
checkstrrchr(char *s, int ch)
{
   check(strrchr(s, ch) == mystrrchr(s, ch));
}

void
checkcmp(char *s1, char *s2)
{
   check(signum(strcmp(s1, s2)) == signum(mystrcmp(s1, s2)));
}

void
checkncmp(char *s1, char *s2)
{
   int i;
   int okay = 1;

   for (i = 0; i < LIMLIM; i++) {
      okay = (okay && signum(strncmp(s1, s2, i)) == signum(mystrncmp(s1, s2, i)));
   }
   check(okay);
}

int
signum(int x)
{
   return x < 0 ? -1 : x > 0 ? 1 : 0;
}