/*
 * good.c - see if a word or its root word
 * is in the dictionary.
 *
 * Pace Willisson, 1983
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "config.h"
#include "ispell.h"

static int wordok;
static char *orig_word;

extern int cflag;

int 
good (char *w)
{
  char nword[100];
  register char *p, *q;
  register n;

  /*
  ** Make an uppercase copy of the word we are checking.
  */
  for (p = w, q = nword; *p; p++, q++)
    {
      if (mylower (*p))
	*q = toupper (*p);
      else
	*q = *p;
    }
  *q = 0;

  rootword[0] = 0;

  if (cflag)
    {
      printf ("%s\n", w);
      orig_word = w;
    }
  else if (lookup (nword, q - nword, 1) != NULL)
    {
#ifdef CAPITALIZE
      return cap_ok (w, lastdent);
#else
      return (1);
#endif
    }

  /* try stripping off suffixes */

  n = strlen (w);
  if (n == 1)
    return (1);

  if (n < 4)
    return 0;

  wordok = 0;

  /* this part from 'check.mid' */
  switch (q[-1])
    {
    case 'D':
      d_ending (nword, n);
      break;			/* FOR "CREATED", "IMPLIED", "CROSSED" */
    case 'T':
      t_ending (nword, n);
      break;			/* FOR "LATEST", "DIRTIEST", "BOLDEST" */
    case 'R':
      r_ending (nword, n);
      break;			/* FOR "LATER", "DIRTIER", "BOLDER" */
    case 'G':
      g_ending (nword, n);
      break;			/* FOR "CREATING", "FIXING" */
    case 'H':
      h_ending (nword, n);
      break;			/* FOR "HUNDREDTH", "TWENTIETH" */
    case 'S':
      s_ending (nword, n);
      break;			/* FOR ALL SORTS OF THINGS ENDING IN "S" */
    case 'N':
      n_ending (nword, n);
      break;			/* "TIGHTEN", "CREATION", "MULIPLICATION" */
    case 'E':
      e_ending (nword, n);
      break;			/* FOR "CREATIVE", "PREVENTIVE" */
    case 'Y':
      y_ending (nword, n);
      break;			/* FOR "QUICKLY" */
    default:
      break;
    }

  if (wordok)
    {
      strcpy (rootword, lastdent->word);
#ifdef CAPITALIZE
      return cap_ok (w, lastdent);
#else
      return 1;
#endif
    }
  return 0;
}

#ifdef CAPITALIZE
int 
cap_ok (char *word, struct dent * dent)
{
  register char *dword;
  register char *w;
  int wcount;

  /*
  ** All caps is always legal.
  */
  for (dword = word; *dword; dword++)
    {
      if (mylower (*dword))
	break;
    }
  if (*dword == '\0')
    return 1;			/* It was all caps */
  if (dent->allcaps)
    return 0;			/* Not all caps and required to be */
  if (dent->followcase)
    {
      /*
      ** It's a followcase word.  The correct capitalizations are
      ** found following the main dent word.  When we find a
      ** mismatch between letters, we assume we are in the suffix,
      ** and begin insisting on the same case as the last letter
      ** that matched.
      */
      dword = dent->word + strlen (dent->word) + 1;
      wcount = *dword++ & 0xFF;
      while (--wcount >= 0)
	{
	  dword++;		/* Skip over keep flag */
	  for (w = word; *w; w++, dword++)
	    {
	      if (*dword != *w)
		{
		  /* Begin suffix processing.  */
		  if (myupper (dword[-1]))
		    {
		      while (*w && !mylower (*w))
			w++;
		      if (*w == '\0')
			return 1;
		    }
		  else
		    {
		      while (*w && !myupper (*w))
			w++;
		      if (*w == '\0')
			return 1;
		    }
		  break;
		}
	    }
	  if (*w == '\0')
	    return 1;
	  while (*dword++)	/* Skip to next prototype */
	    ;
	}
    }
  /*
  ** If it's a capitalize word, and the first letter is lowercase,
  ** it's illegal.  Note that all-lowercase followcase words will
  ** be found by the string scan above.
  */
  if (dent->capitalize && mylower (*word))
    return 0;
  /*
  ** If it's not a followcase word, or if the capitalize flag is set,
  ** capitalization (e.g. at the beginning of a sentence) is always
  ** legal.  All-lowercase is also legal for non-followcase words.
  */
  if (!dent->followcase || dent->capitalize)
    {
      for (dword = word + 1; *dword; dword++)
	{
	  if (myupper (*dword))
	    break;
	}
      if (*dword == '\0')
	return 1;		/* It was all-lower or capitalized */
    }
  return 0;			/* Word has a bad mix of cases */
}

#endif

void 
flagpr (char *w, int flag, char *modpoint)
{
  register char *orig;

  /*
  ** We refuse to print if the case at and after modpoint isn't
  ** consistent with the case just before there.  This prevents
  ** things like "OEM's" from being turned into OEM/M, which in
  ** turn will only accept "OEM'S".
  */
  orig = orig_word + (modpoint - w);
  if (myupper (orig[-1]))
    {
      while (*orig)
	{
	  if (mylower (*orig++))
	    return;
	}
    }
  else
    {
      while (*orig)
	{
	  if (myupper (*orig++))
	    return;
	}
    }
  /* Case is ok.  Now print it. */
  for (orig = orig_word; *w && w < modpoint; orig++, w++)
    putchar (*orig);
  if (myupper (orig[-1]))
    printf ("%s", w);
  else
    {
      for (; *w; w++)
	{
	  if (myupper (*w))
	    putchar (tolower (*w));
	  else
	    putchar (*w);
	}
    }
  printf ("/%c\n", flag);
}

void 
g_ending (char *w, int n)
{
  register char *p;
  register struct dent *dent;

  p = w + n - 3;		/* if the word ends in 'ing', then *p == 'i' */

  if (strcmp (p, "ING") != 0)
    return;

  *p = 'E';			/* change I to E, like in CREATING */
  *(p + 1) = 0;
  n -= 2;

  if (n < 2)
    return;

  if (cflag)
    flagpr (w, 'G', p);
  else if ((dent = lookup (w, n, 1)) != NULL
	   && dent->g_flag)
    {
      wordok = 1;
      return;
    }


  *p = 0;
  n--;

  if (n < 2)
    return;

  if (p[-1] == 'E')
    return;			/* this stops CREATEING */

  if (cflag)
    flagpr (w, 'G', p);
  else if ((dent = lookup (w, n, 1)) != NULL)
    {
      if (dent->g_flag)
	wordok = 1;
      return;
    }
  return;
}

void 
d_ending (char *w, int n)
{
  register char *p;
  register struct dent *dent;

  p = w + n - 2;

  if (strcmp (p, "ED") != 0)
    return;

  p[1] = 0;			/* kill 'D' */
  n--;

  if (cflag)
    flagpr (w, 'D', p + 1);
  else if ((dent = lookup (w, n, 1)) != NULL)
    {				/* eg CREATED */
      if (dent->d_flag)
	{
	  wordok = 1;
	  return;
	}
    }

  if (n < 3)
    return;

  p[0] = 0;
  n--;
  p--;

  /* ED is now completely gone */

  if (p[0] == 'I' && !vowel (p[-1]))
    {
      p[0] = 'Y';
      if (cflag)
	flagpr (w, 'D', p);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->d_flag)
	{
	  wordok = 1;
	  return;
	}
      p[0] = 'I';
    }

  if ((p[0] != 'E' && p[0] != 'Y') ||
      (p[0] == 'Y' && vowel (p[-1])))
    {
      if (cflag)
	flagpr (w, 'D', p + 1);
      else if ((dent = lookup (w, n, 1)) != NULL)
	{
	  if (dent->d_flag)
	    wordok = 1;
	  return;
	}
    }
}

void 
t_ending (char *w, int n)
{

  register char *p;
  register struct dent *dent;

  p = w + n - 3;

  if (strcmp (p, "EST") != 0)
    return;

  p[1] = 0;			/* kill "ST" */
  n -= 2;

  if (cflag)
    flagpr (w, 'T', p);
  else if ((dent = lookup (w, n, 1)) != NULL
	   && dent->t_flag)
    {
      wordok = 1;
      return;
    }

  if (n < 3)
    return;

  p[0] = 0;			/* kill 'E' */
  n--;
  p--;

  /* EST is now completely gone */

  if (p[0] == 'I' && !vowel (p[-1]))
    {
      p[0] = 'Y';
      if (cflag)
	flagpr (w, 'T', p);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->t_flag)
	{
	  wordok = 1;
	  return;
	}
      p[0] = 'I';
    }

  if ((p[0] != 'E' && p[0] != 'Y') ||
      (p[0] == 'Y' && vowel (p[-1])))
    {
      if (cflag)
	flagpr (w, 'T', p + 1);
      else if ((dent = lookup (w, n, 1)) != NULL)
	{
	  if (dent->t_flag)
	    wordok = 1;
	  return;
	}
    }

}


void 
r_ending (char *w, int n)
{
  register char *p;
  register struct dent *dent;

  p = w + n - 2;

  if (strcmp (p, "ER") != 0)
    return;

  p[1] = 0;			/* kill 'R' */
  n--;

  if (cflag)
    flagpr (w, 'R', p + 1);
  else if ((dent = lookup (w, n, 1)) != NULL
	   && dent->r_flag)
    {
      wordok = 1;
      return;
    }

  if (n < 3)
    return;

  p[0] = 0;			/* kill 'E' */
  n--;
  p--;

  /* ER is now completely gone */

  if (p[0] == 'I' && !vowel (p[-1]))
    {
      p[0] = 'Y';
      if (cflag)
	flagpr (w, 'R', p);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->r_flag)
	{
	  wordok = 1;
	  return;
	}
      p[0] = 'I';
    }

  if ((p[0] != 'E' && p[0] != 'Y') ||
      (p[0] == 'Y' && vowel (p[-1])))
    {
      if (cflag)
	flagpr (w, 'R', p + 1);
      else if ((dent = lookup (w, n, 1)) != NULL)
	{
	  if (dent->r_flag)
	    wordok = 1;
	  return;
	}
    }

}

void 
h_ending (char *w, int n)
{
  register char *p;
  register struct dent *dent;

  p = w + n - 2;

  if (strcmp (p, "TH") != 0)
    return;

  *p = 0;			/* kill "TH" */
  n -= 2;

  p -= 2;

  if (p[1] != 'Y')
    {
      if (cflag)
	flagpr (w, 'H', p + 2);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->h_flag)
	wordok = 1;
    }

  if (strcmp (p, "IE") != 0)
    return;

  p[0] = 'Y';			/* change "IE" to "Y" */
  p[1] = 0;
  n--;

  if (cflag)
    flagpr (w, 'H', p + 1);
  else if ((dent = lookup (w, n, 1)) != NULL)
    if (dent->h_flag)
      wordok = 1;

}

/*
 * check for flags: X, J, Z, S, P, M
 *
 * X	-ions or -ications or -ens
 * J	-ings
 * Z	-ers or -iers
 * S	-ies or -es or -s
 * P	-iness or -ness
 * M	-'S
 */

void 
s_ending (char *w, int n)
{
  register char *p;
  register struct dent *dent;

  p = w + n;

  p[-1] = 0;			/* kill 'S' */
  n--;

  if (strchr ("SXZHY", p[-2]) == NULL || (p[-2] == 'Y' && vowel (p[-3])))
    {
      if (cflag)
	flagpr (w, 'S', p - 1);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->s_flag)
	{
	  wordok = 1;
	  return;
	}
    }


  switch (p[-2])
    {				/* letter before S */
    case 'N':			/* X */
      if (strcmp (p - 4, "ION") == 0)
	{
	  p[-4] = 'E';		/* change "ION" to "E" */
	  p[-3] = 0;
	  n -= 2;
	  if (cflag)
	    flagpr (w, 'X', p - 4);
	  else if ((dent = lookup (w, n, 1)) != NULL
		   && dent->x_flag)
	    {
	      wordok = 1;
	      return;
	    }
	}
      if (strcmp (p - 8, "ICATE") == 0)
	{
	  p[-8] = 'Y';		/* change "ICATE" to "Y" */
	  p[-7] = 0;
	  n -= 4;
	  if (cflag)
	    flagpr (w, 'X', p - 8);
	  else if ((dent = lookup (w, n, 1)) != NULL
		   && dent->x_flag)
	    wordok = 1;
	  return;
	}
      if (strcmp (p - 3, "EN") == 0 && p[-4] != 'E' && p[-4] != 'Y')
	{
	  p[-3] = 0;		/* kill "EN" */
	  n -= 2;
	  if (cflag)
	    flagpr (w, 'X', p - 3);
	  else if ((dent = lookup (w, n, 1)) != NULL
		   && dent->x_flag)
	    wordok = 1;
	  return;
	}
      return;
    case 'G':			/* J */
      if (strcmp (p - 4, "ING") != 0)
	return;
      p[-4] = 'E';		/* change "ING" to "E" */
      p[-3] = 0;
      n -= 2;
      if (cflag)
	flagpr (w, 'J', p - 4);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->j_flag)
	{
	  wordok = 1;
	  return;
	}
      if (p[-5] == 'E')
	return;			/* This stops CREATEING */
      p[-4] = 0;		/* kill 'E' */
      n--;
      if (cflag)
	flagpr (w, 'J', p - 4);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->j_flag)
	wordok = 1;
      return;
    case 'R':			/* Z */
      if (strcmp (p - 3, "ER") != 0)
	return;

      p[-2] = 0;		/* kill 'R' */
      n--;
      if (cflag)
	flagpr (w, 'Z', p - 2);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->z_flag)
	{
	  wordok = 1;
	  return;
	}
      if (p[-4] == 'I' && !vowel (p[-5]))
	{
	  p[-4] = 'Y';		/* change "IE" to "Y" */
	  p[-3] = 0;
	  n--;
	  if (cflag)
	    flagpr (w, 'Z', p - 4);
	  else if ((dent = lookup (w, n, 1)) != NULL
		   && dent->z_flag)
	    {
	      wordok = 1;
	      return;
	    }
	  p[-4] = 'I';		/* change 'Y' to 'I' */
	}
      if ((p[-4] != 'E' && p[-4] != 'Y') ||
	  (p[-4] == 'Y' && vowel (p[-5])))
	{
	  if (p[-3])
	    n--;
	  p[-3] = 0;
	  if (cflag)
	    flagpr (w, 'Z', p - 3);
	  else if ((dent = lookup (w, n, 1)) != NULL
		   && dent->z_flag)
	    wordok = 1;
	}
      return;
    case 'E':			/* S (except simple adding of an S) */
      p[-2] = 0;		/* drop the E */
      n--;
      if (strchr ("SXZH", p[-3]) != NULL)
	{
	  if (cflag)
	    flagpr (w, 'S', p - 2);
	  else if ((dent = lookup (w, n, 1)) != NULL)
	    {
	      if (dent->s_flag)
		wordok = 1;;
	      return;
	    }
	}
      if (p[-3] == 'I' && !vowel (p[-4]))
	{
	  p[-3] = 'Y';
	  if (cflag)
	    flagpr (w, 'S', p - 3);
	  else if ((dent = lookup (w, n, 1)) != NULL
		   && dent->s_flag)
	    wordok = 1;
	  return;
	}
      return;

    case 'S':			/* P */
      if (strcmp (p - 4, "NES") != 0)
	return;

      p[-4] = 0;		/* kill "NES" */
      n -= 3;
      if (p[-5] != 'Y' || vowel (p[-6]))
	{
	  if (cflag)
	    flagpr (w, 'P', p - 4);
	  else if ((dent = lookup (w, n, 1)) != NULL
		   && dent->p_flag)
	    {
	      wordok = 1;
	      return;
	    }
	}
      if (p[-5] == 'I')
	{
	  p[-5] = 'Y';
	  if (cflag)
	    flagpr (w, 'P', p - 5);
	  else if ((dent = lookup (w, n, 1)) != NULL
		   && dent->p_flag)
	    wordok = 1;
	}
      return;
    case '\'':			/* M */
      p[-2] = '\0';		/* kill "'" */
      n--;
      if (cflag)
	flagpr (w, 'M', p - 2);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->m_flag)
	wordok = 1;
      return;
    }
}

/* only the N flag */
void 
n_ending (char *w, int n)
{
  register char *p;
  register struct dent *dent;

  p = w + n;

  if (p[-2] == 'E')
    {
      if (p[-3] == 'E' || p[-3] == 'Y')
	return;
      p[-2] = 0;		/* kill "EN" */
      n -= 2;
      if (cflag)
	flagpr (w, 'N', p - 2);
      else if ((dent = lookup (w, n, 1)) != NULL
	       && dent->n_flag)
	wordok = 1;
      return;
    }

  if (strcmp (p - 3, "ION") != 0)
    return;

  p[-3] = 'E';			/* change "ION" to "E" */
  p[-2] = 0;
  n -= 2;

  if (cflag)
    flagpr (w, 'N', p - 3);
  else if ((dent = lookup (w, n, 1)) != NULL)
    {
      if (dent->n_flag)
	wordok = 1;
      return;
    }

  if (strcmp (p - 7, "ICATE") != 0)	/* check is really against "ICATION" */
    return;

  p[-7] = 'Y';			/* change "ICATE" to "Y" */
  p[-6] = 0;
  n -= 4;

  if (cflag)
    flagpr (w, 'N', p - 7);
  else if ((dent = lookup (w, n, 1)) != NULL && dent->n_flag)
    wordok = 1;
  return;
}

/* flags: v */
void 
e_ending (char *w, int n)
{
  register char *p;
  register struct dent *dent;

  p = w + n;

  if (strcmp (p - 3, "IVE") != 0)
    return;
  p[-3] = 'E';			/* change "IVE" to "E" */
  p[-2] = 0;
  n -= 2;

  if (cflag)
    flagpr (w, 'V', p - 3);
  else if ((dent = lookup (w, n, 1)) != NULL
	   && dent->v_flag)
    {
      wordok = 1;
      return;
    }

  if (p[-4] == 'E')
    return;

  p[-3] = 0;			/* kill 'E' */
  n--;

  if (cflag)
    flagpr (w, 'V', p - 3);
  else if ((dent = lookup (w, n, 1)) != NULL && dent->v_flag)
    wordok = 1;
  return;
}

/* flags: y */
void 
y_ending (char *w, int n)
{
  register char *p;
  register struct dent *dent;

  p = w + n;

  if (strcmp (p - 2, "LY") != 0)
    return;

  p[-2] = 0;			/* kill "LY" */
  n -= 2;

  if (cflag)
    flagpr (w, 'Y', p - 2);
  else if ((dent = lookup (w, n, 1)) != NULL && dent->y_flag)
    wordok = 1;
  return;
}

int 
vowel (int c)
{
  return (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U');
}
