#include "hbapi.h"
#include "hbapifs.h"
#include "hbapiitm.h"

#define HB_ABS( a )  ( ( ( a ) > 0 ) ? ( a ) : -( a ) )

static const char * s_cSearch = "INEDTIERESTEON";
static const char * s_cRepl   = "[\\]^_`a";

/**
 *   Function: XForm()
 *    Purpose: Internal function to translate words to dictionary
 *  Arguments: cWord    - upper case word to format
 *    Returns: cXformed - translated word
 *
 *      Notes: I'm assuming that the passed word won't exceed 128 bytes.
 */
HB_FUNC( XFORM )
{
   char cRet[ 128 ];
   char *       cPtr     = cRet;
   HB_ISIZ      iRetLen  = 0;
   const char * cWord    = hb_parc( 1 ) + 2;
   HB_ISIZ      iWordLen = hb_parclen( 1 ) - 2;
   HB_ISIZ      x;
   HB_UINT      iKey;

   while( --iWordLen >= 1 && iRetLen < 128 )
   {
      iKey = *( ( HB_UINT * ) cWord );
      for( x = 0; x < 14; x += 2 )
      {
         if( *( ( HB_UINT * ) ( s_cSearch + x ) ) == iKey )
         {
            if( x == 0 )
            {
               if( *( cWord + 2 ) != 'G' )
               {
                  x = 14;
                  break;
               }
               else
               {
                  iWordLen--;
                  cWord++;
               }
            }
            iWordLen--;
            cWord  += 2;
            *cPtr++ = *( s_cRepl + ( x >> 1 ) );
            break;
         }
      }

      if( x >= 14 )
         *cPtr++ = *cWord++;

      iRetLen++;
   }

   if( iWordLen == 0 && iRetLen < 128 )
   {
      *cPtr++ = *cWord;
      iRetLen++;
   }

   for( x = iRetLen - 2; x >= 0; x-- )
      *( cRet + x ) -= 30;

   *( cRet + iRetLen - 1 ) += 128;
   hb_retclen( cRet, iRetLen );
}

/**
 *   Function: XUnForm()
 *    Purpose: Internal function to translate words from dictionary
 *  Arguments: cWord    - formatted word
 *    Returns: cXformed - unformatted word
 *
 *      Notes: I'm assuming that the returned word won't exceed 128 bytes.
 */
HB_FUNC( XUNFORM )
{
   char cRet[ 128 ];
   char *       cPtr    = cRet;
   HB_ISIZ      iRetLen = 0;
   char         c;
   const char * cWord    = hb_parc( 1 );
   HB_ISIZ      iWordLen = hb_parclen( 1 );
   HB_ISIZ      x;

   while( iWordLen > 0 && iRetLen < 128 )
   {
      iWordLen--;
      iRetLen++;
      c = *cWord++;
      if( iWordLen )
         c += 30;
      else
         c -= 128;

      if( ( x = ( HB_ISIZ ) memchr( s_cRepl, c, 7 ) ) <= 6 )
      {
         if( x == 0 )
         {
            if( iRetLen < 127 )
            {
               *cPtr++  = 'I';
               *cPtr++  = 'N';
               *cPtr++  = 'G';
               iRetLen += 2;
            }
         }
         else if( iRetLen < 128 )
         {
            x       = x << 1;
            *cPtr++ = *( s_cSearch + x++ );
            *cPtr++ = *( s_cSearch + x );
            iRetLen++;
         }
      }
      else
         *cPtr++ = c;
   }

   hb_retclen( cRet, iRetLen );
}

/**
 *   Function: Sp_Rate()
 *     Syntax: cRating := Sp_Rate( cFound, cWord )
 *    Purpose: Returns a letter code indicating how similar the two
 *             words are.  This is primarily used to sort the list
 *             of suggested words.
 *  Arguments: cFound   - Word from dictionary
 *             cWord    - Word to compare with dictionary word
 *    Returns: cRating  - Letter A-I or Z
 *   Category: INTERNAL
 *  Called by: Sp_Suggest()
 *      Notes: Sp_Rate() assigns a rating based upon how likely the
 *             word matches the dictionary word.  It compares the
 *             first 5 letters of the boths word, then the last 5,
 *             down to 2 letters.  This results in a rating from A-H.
 *             If none of these matched, then the function will return
 *             either an I if the words are the same length, or a Z.
 *
 *    C Notes: I'm assuming the words passed are already trimmed.
 */
HB_FUNC( SP_RATE )
{
   const char * cFound  = hb_parc( 1 );
   HB_ISIZ      nFound  = hb_parclen( 1 );
   const char * cWord   = hb_parc( 2 );
   HB_ISIZ      nWord   = hb_parclen( 2 );
   HB_ISIZ      nMinLen = HB_MIN( nFound, nWord );
   HB_ISIZ      x       = HB_ABS( nFound - nWord );
   HB_ISIZ      lim     = HB_MIN( nMinLen, 5 );
   char *       cRating = hb_strdup( "nZZ" );

   *cRating = ( char ) ( HB_MIN( x, 9 ) + '0' );

   for( x = 0; x < lim; x++ )
   {
      if( *( cFound + x ) != *( cWord + x ) )
         break;
      *( cRating + 1 ) = ( char ) ( 'A' - 1 + lim - x );
   }

   cFound = cFound + nFound - 1;
   cWord  = cWord + nWord - 1;

   for( x = 0; x < lim; x++ )
   {
      if( *cFound != *cWord )
         break;
      *( cRating + 2 ) = ( char ) ( 'A' - 1 + lim - x );
      cFound--;
      cWord--;
   }

   hb_retclen_buffer( cRating, 3 );
}

/**
 * Author: Clayton Neff
 * Copyright (c) 1992 by CoN Computer Consultants
 *
 * Revision: 1.0 Last Revised: 8/23/1992 at 16:47
 * Description: Original Creation.
 *---------------------------- ALL RIGHTS RESERVED ----------------------------*/

/*
   This function replaces the ft_Metaph() that Joe Booth used in his
   spelling checker article in the Aquarium.*/

HB_FUNC( C_METAFONE )
{
   char *  sReturn;            /* Pointer to the return string.   */
   char *  sMeta;              /* Pointer to the passed string.   */
   HB_SIZE iRetLen;            /* Length of the return string.    */
   HB_SIZE iStrLen;            /* Length of the passed string.    */
   HB_SIZE iRetPtr;            /* Pointer into the return string. */
   HB_SIZE iStrPtr;            /* Pointer into the passed string. */

   /* If no string was passed then return an empty string. */
   if( hb_pcount() == 0 )
   {
      sMeta      = hb_xgrab( 1 );
      sMeta[ 1 ] = '\0';
      iStrLen    = 0;
      iRetLen    = 4;
   }
   /* If no return lenght was passed, default to 4. */
   else if( hb_pcount() == 1 )
   {
      hb_itemGetWriteCL( hb_param( 1, HB_IT_STRING ), &sMeta, &iStrLen );
      iRetLen = 4;
   }
   else
   {
      hb_itemGetWriteCL( hb_param( 1, HB_IT_STRING ), &sMeta, &iStrLen );
      iRetLen = hb_parns( 2 );
   }

   /* Set up the buffer to hold the return string.
      Be sure to make it 1 character longer than needed
      so there is space for the null terminator. */
   sReturn = hb_xgrab( iRetLen + 1 );
   iRetPtr = 0;
   iStrPtr = 0;

   /* Handle the special prefixes. */
   switch( sMeta[ iStrPtr ] )
   {
      /* Since "KN" and "PN" both translate into 'N', we just stack their
         case statements with no break, and they will all execute the same
         block of code.  We cannot place "GN" here because we must test
         for "GI" as well. */

      /* KN->N */
      case 'K':
      /* PN->N */
      case 'P':
         if( sMeta[ iStrPtr + 1 ] == 'N' )
         {

            /* Put in the first return character and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'N';
            iStrPtr += 2;
         }
         break;

      /* AE->E */
      case 'A':
         if( sMeta[ iStrPtr + 1 ] == 'E' )
         {

            /* Put in the first return character and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'E';
            iStrPtr += 2;
         }
         break;

      /* X->S */
      case 'X':

         /* Put in the first return character and increment the pointer. */
         sReturn[ iRetPtr++ ] = 'S';
         iStrPtr++;
         break;

      /* We have three different possibilities for 'W'... */
      case 'W':

         /* WR->R */
         if( sMeta[ iStrPtr + 1 ] == 'R' )
         {
            /* Put in the first return character and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'R';
            iStrPtr += 2;
         }
         /* WHO->H */
         else if( sMeta[ iStrPtr + 1 ] == 'H' &&
                  sMeta[ iStrPtr + 2 ] == 'O' )
         {
            /* Put in the first return character and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'H';
            iStrPtr += 3;
         }
         /* WH->W */
         else if( sMeta[ iStrPtr + 1 ] == 'H' )
         {
            /* Put in the first return character and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'W';
            iStrPtr += 2;
         }
         break;

      /* Checking for Celtic prefixes... */
      case 'M':

         /* MCG->MK or MC->MK */
         if( sMeta[ iStrPtr + 1 ] == 'C' )
         {
            /* Put in the first return characters and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'M';
            sReturn[ iRetPtr++ ] = 'K';
            iStrPtr += ( sMeta[ iStrPtr + 2 ] == 'G' ) ? 3 : 2;
         }
         /* MACH->MX */
         else if( sMeta[ iStrPtr + 1 ] == 'A' &&
                  sMeta[ iStrPtr + 2 ] == 'C' &&
                  sMeta[ iStrPtr + 3 ] == 'H' )
         {
            /* Put in the first return characters and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'M';
            sReturn[ iRetPtr++ ] = 'X';
            iStrPtr += 4;
         }
         /* MACG->MK or MAC->MK */
         else if( sMeta[ iStrPtr + 1 ] == 'A' &&
                  sMeta[ iStrPtr + 2 ] == 'C' )
         {
            /* Put in the first return characters and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'M';
            sReturn[ iRetPtr++ ] = 'K';
            iStrPtr += ( sMeta[ iStrPtr + 3 ] == 'G' ) ? 4 : 3;
         }
         break;

      case 'G':
         /* GN   -> N */
         if( sMeta[ iStrPtr + 1 ] == 'N' )
         {
            /* Put in the first return character and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'N';
            iStrPtr += 2;
         }
         else if( sMeta[ iStrPtr + 1 ] == 'I' )
         {
            /* Put in the first return character and increment the pointer. */
            sReturn[ iRetPtr++ ] = 'K';
            iStrPtr += 2;
         }
         break;
   }                       /* End of prefix switch...case. */

   /* Now we want to loop until we have stepped through the entire
      passed string or we have filled the return string. */

   while( iStrPtr < iStrLen && iRetPtr < iRetLen )
   {
      /* Use a switch...case statement for each character combination. */
      switch( sMeta[ iStrPtr ] )
      {
         /* If it is a vowel other than 'I', skip it if it isn't the
            first character of the passed string. */
         case 'A':
         case 'E':
         case 'O':
         case 'U':
            if( iStrPtr == 0 )
               sReturn[ iRetPtr++ ] = sMeta[ iStrPtr ];
            iStrPtr++;
            break;

         /* If it is 'I', copy it if it is the first character.
            In addition, check for "ISCH" and replace it with "X". */
         case 'I':
            if( iStrPtr == 0 )
               sReturn[ iRetPtr++ ] = sMeta[ iStrPtr ];
            if( sMeta[ iStrPtr + 1 ] == 'S' &&
                sMeta[ iStrPtr + 2 ] == 'C' &&
                sMeta[ iStrPtr + 3 ] == 'H' )
            {
               sReturn[ iRetPtr++ ] = 'X';
               iStrPtr += 3;
            }
            iStrPtr++;
            break;

         /* Skip B if it is the last character and after an 'M'. */
         case 'B':
            if( ( ( iStrPtr == iStrLen - 1 ) ||
                  sMeta[ iStrPtr + 1 ] == ' ' ) &&
                sMeta[ iStrPtr - 1 ] == 'M' )
            {
               iStrPtr++;
               continue;
            }

         /* B, F, K, L. M. N, R - Skip the second occurance of a double.
            Otherwise, just copy the letter over. */
         case 'F':
         case 'K':
         case 'L':
         case 'M':
         case 'N':
         case 'R':
            if( sMeta[ iStrPtr + 1 ] == sMeta[ iStrPtr ] )
               iStrPtr++;
            sReturn[ iRetPtr++ ] = sMeta[ iStrPtr++ ];
            break;

         /* D - Many differnt sounds possible. */
         case 'D':

            /* If the letter is doubled, skip the first one. */
            if( sMeta[ iStrPtr + 1 ] == sMeta[ iStrPtr ] )
               iStrPtr++;

            /* DGE, DGI, DGY -> J */
            if( sMeta[ iStrPtr + 1 ] == 'G' )
            {
               if( sMeta[ iStrPtr + 2 ] == 'E' ||
                   sMeta[ iStrPtr + 2 ] == 'I' ||
                   sMeta[ iStrPtr + 2 ] == 'Y' )
               {
                  sReturn[ iRetPtr++ ] = 'J';
                  iStrPtr += 3;
               }
               /* If not one of those three, transform to T. */
               else
               {
                  sReturn[ iRetPtr++ ] = 'T';
                  iStrPtr++;
               }
            }
            /* D tranforms to T. */
            else
            {
               sReturn[ iRetPtr++ ] = 'T';
               iStrPtr++;
            }
            break;

         /* G - Many differnt sounds possible. */
         case 'G':

            /* If the letter is doubled, skip the first one. */
            if( sMeta[ iStrPtr + 1 ] == sMeta[ iStrPtr ] )
               iStrPtr++;

            /* GE, GI, GY -> J (if not following a double). */
            if( ( sMeta[ iStrPtr + 1 ] == 'E' ||
                  sMeta[ iStrPtr + 1 ] == 'I' ||
                  sMeta[ iStrPtr + 1 ] == 'Y' ) &&
                sMeta[ iStrPtr - 1 ] != 'G' )
            {
               sReturn[ iRetPtr++ ] = 'J';
               iStrPtr += 2;
            }
            /* vGHT -> T or vGH, vGHTH -> W leaving TH to be processed. */
            else if( sMeta[ iStrPtr + 1 ] == 'H' &&
                     ( sMeta[ iStrPtr - 1 ] == 'A' ||
                       sMeta[ iStrPtr - 1 ] == 'E' ||
                       sMeta[ iStrPtr - 1 ] == 'I' ||
                       sMeta[ iStrPtr - 1 ] == 'O' ||
                       sMeta[ iStrPtr - 1 ] == 'U' ) )
            {
               if( sMeta[ iStrPtr + 2 ] == 'T' &&
                   ! ( sMeta[ iStrPtr + 3 ] == 'H' ) )
               {
                  sReturn[ iRetPtr++ ] = 'T';
                  iStrPtr += 3;
               }
               else
               {
                  /* OUGH -> F */
                  if( sMeta[ iStrPtr - 2 ] == 'O' &&
                      sMeta[ iStrPtr - 1 ] == 'U' )
                  {
                     sReturn[ iRetPtr++ ] = 'F';
                     iStrPtr += 2;
                  }
                  else
                  {
                     sReturn[ iRetPtr++ ] = 'W';
                     iStrPtr += 2;
                  }
               }
            }
            /* GHv -> K */
            else if( sMeta[ iStrPtr + 1 ] == 'H' &&
                     ( sMeta[ iStrPtr + 2 ] == 'A' ||
                       sMeta[ iStrPtr + 2 ] == 'E' ||
                       sMeta[ iStrPtr + 2 ] == 'I' ||
                       sMeta[ iStrPtr + 2 ] == 'O' ||
                       sMeta[ iStrPtr + 2 ] == 'U' ) )
            {
               sReturn[ iRetPtr++ ] = 'K';
               iStrPtr += 2;
            }
            /* GN -> N */
            else if( sMeta[ iStrPtr + 1 ] == 'N' )
            {
               sReturn[ iRetPtr++ ] = 'N';
               iStrPtr += 2;
            }
            /* The suffix NG is skipped. */
            else if( ( ( iStrPtr == iStrLen - 1 ) ||
                       sMeta[ iStrPtr + 1 ] == ' ' ) &&
                     sMeta[ iStrPtr - 1 ] == 'N' )
               iStrPtr++;

            /* G -> K */
            else
            {
               sReturn[ iRetPtr++ ] = 'K';
               iStrPtr++;
            }
            break;

         /* J, W - Direct replacement. */
         case 'J':
         case 'W':
            sReturn[ iRetPtr++ ] = sMeta[ iStrPtr++ ];
            break;

         /* P - Check for PH. */
         case 'P':
            /* If the letter is doubled, skip the first one. */
            if( sMeta[ iStrPtr + 1 ] == sMeta[ iStrPtr ] )
               iStrPtr++;

            if( sMeta[ iStrPtr + 1 ] == 'H' )
            {
               sReturn[ iRetPtr++ ] = 'F';
               iStrPtr += 2;
            }
            else
               sReturn[ iRetPtr++ ] = sMeta[ iStrPtr++ ];
            break;

         /* Q -> K */
         case 'Q':
            sReturn[ iRetPtr++ ] = 'K';
            iStrPtr++;
            break;

         /* S - Many differnt sounds possible. */
         case 'S':

            /* If the letter is doubled, skip the first one. */
            if( sMeta[ iStrPtr + 1 ] == sMeta[ iStrPtr ] )
               iStrPtr++;

            /* SCH -> SK */
            if( sMeta[ iStrPtr + 1 ] == 'C' &&
                sMeta[ iStrPtr + 2 ] == 'H' )
            {
               sReturn[ iRetPtr++ ] = 'S';
               sReturn[ iRetPtr++ ] = 'K';
               iStrPtr += 3;
            }
            /* SIO, SIA, SH -> X */
            else if( ( sMeta[ iStrPtr + 1 ] == 'I' &&
                       ( sMeta[ iStrPtr + 2 ] == 'A' ||
                         sMeta[ iStrPtr + 2 ] == 'O' ) ) ||
                     sMeta[ iStrPtr + 1 ] == 'H' )
            {
               sReturn[ iRetPtr++ ] = 'X';
               iStrPtr += ( sMeta[ iStrPtr + 1 ] == 'H' ) ? 2 : 3;
            }
            /* SCE, SCI, SCY -> S */
            else if( sMeta[ iStrPtr + 1 ] == 'C' )
            {
               if( sMeta[ iStrPtr + 2 ] == 'E' ||
                   sMeta[ iStrPtr + 2 ] == 'I' ||
                   sMeta[ iStrPtr + 2 ] == 'Y' )
               {
                  sReturn[ iRetPtr++ ] = 'S';
                  iStrPtr += 3;
               }
               /* If not one of those three, just copy it over. */
               else
                  sReturn[ iRetPtr++ ] = sMeta[ iStrPtr++ ];
            }
            /* Just copy the letter over. */
            else
               sReturn[ iRetPtr++ ] = sMeta[ iStrPtr++ ];

            break;

         /* T - Many different sounds possible. */
         case 'T':

            /* If the letter is doubled, skip the first one. */
            if( sMeta[ iStrPtr + 1 ] == sMeta[ iStrPtr ] )
               iStrPtr++;

            /* TIA, TIO, TCH -> X */
            if( ( sMeta[ iStrPtr + 1 ] == 'I' &&
                  ( sMeta[ iStrPtr + 2 ] == 'A' ||
                    sMeta[ iStrPtr + 2 ] == 'O' ) ) ||
                ( sMeta[ iStrPtr + 1 ] == 'C' &&
                  sMeta[ iStrPtr + 2 ] == 'H' ) )
            {
               sReturn[ iRetPtr++ ] = 'X';
               iStrPtr += 3;
            }
            /* TH -> 0 */
            else if( sMeta[ iStrPtr + 1 ] == 'H' )
            {
               sReturn[ iRetPtr++ ] = '0';
               iStrPtr += 2;
            }
            /* Copy the letter over. */
            else
               sReturn[ iRetPtr++ ] = sMeta[ iStrPtr++ ];
            break;

         /* V -> F */
         case 'V':
            sReturn[ iRetPtr++ ] = 'F';
            iStrPtr++;
            break;

         /* X -> KS */
         case 'X':

            /* If the letter is doubled, skip the first one. */
            if( sMeta[ iStrPtr + 1 ] == sMeta[ iStrPtr ] )
               iStrPtr++;

            sReturn[ iRetPtr++ ] = 'K';

            /* Now we have to check for all the S possibilities, since
               we want to add one to the string. */

            /* If the followed by an S, skip over it. */
            if( sMeta[ iStrPtr + 1 ] == 'S' )
               iStrPtr++;


            /* SCH -> SK */
            if( sMeta[ iStrPtr + 1 ] == 'C' &&
                sMeta[ iStrPtr + 2 ] == 'H' )
            {
               sReturn[ iRetPtr++ ] = 'S';
               sReturn[ iRetPtr++ ] = 'K';
               iStrPtr += 3;
            }
            /* SIO, SIA, SH -> X */
            else if( ( sMeta[ iStrPtr + 1 ] == 'I' &&
                       ( sMeta[ iStrPtr + 2 ] == 'A' ||
                         sMeta[ iStrPtr + 2 ] == 'O' ) ) ||
                     sMeta[ iStrPtr + 1 ] == 'H' )
            {
               sReturn[ iRetPtr++ ] = 'X';
               iStrPtr += ( sMeta[ iStrPtr + 1 ] == 'H' ) ? 2 : 3;
            }
            /* SCE, SCI, SCY -> S */
            else if( sMeta[ iStrPtr + 1 ] == 'C' )
            {
               if( sMeta[ iStrPtr + 2 ] == 'E' ||
                   sMeta[ iStrPtr + 2 ] == 'I' ||
                   sMeta[ iStrPtr + 2 ] == 'Y' )
               {
                  sReturn[ iRetPtr++ ] = 'S';
                  iStrPtr += 3;
               }
               /* If not one of those three, just copy it over. */
               else
               {
                  sReturn[ iRetPtr++ ] = 'S';
                  iStrPtr++;
               }
            }
            /* Just copy an 'S' over. */
            else
            {
               sReturn[ iRetPtr++ ] = 'S';
               iStrPtr++;
            }
            break;

         /* Z -> S */
         case 'Z':

            /* If the letter is doubled, skip the first one. */
            if( sMeta[ iStrPtr + 1 ] == sMeta[ iStrPtr ] )
               iStrPtr++;
            sReturn[ iRetPtr++ ] = 'S';
            iStrPtr++;
            break;

         /* C - More sounds than any one letter should be allowed! */
         case 'C':

            /* CC, CH, CIA -> X */
            if( sMeta[ iStrPtr + 1 ] == 'C' ||
                sMeta[ iStrPtr + 1 ] == 'H' ||
                ( sMeta[ iStrPtr + 1 ] == 'I' &&
                  sMeta[ iStrPtr + 2 ] == 'A' ) )
            {
               sReturn[ iRetPtr++ ] = 'X';
               iStrPtr += ( sMeta[ iStrPtr + 1 ] == 'I' ) ? 3 : 2;
            }
            /* CE, CI, CY -> S */
            else if( sMeta[ iStrPtr + 1 ] == 'E' ||
                     sMeta[ iStrPtr + 1 ] == 'I' ||
                     sMeta[ iStrPtr + 1 ] == 'Y' )
            {
               sReturn[ iRetPtr++ ] = 'S';
               iStrPtr += 2;
            }
            /* CK - Skip. */
            else if( sMeta[ iStrPtr + 1 ] == 'K' )
               iStrPtr++;

            /* C -> K */
            else
            {
               sReturn[ iRetPtr++ ] = 'K';
               iStrPtr++;
            }
            break;

         /* Y - If not followed by a vowel, skip it. */
         case 'Y':
            if( sMeta[ iStrPtr + 1 ] == 'A' ||
                sMeta[ iStrPtr + 1 ] == 'E' ||
                sMeta[ iStrPtr + 1 ] == 'I' ||
                sMeta[ iStrPtr + 1 ] == 'O' ||
                sMeta[ iStrPtr + 1 ] == 'U' )
               sReturn[ iRetPtr++ ] = 'Y';
            iStrPtr++;
            break;

         /* H - Skip if preceeded by and not followed by a vowel. */
         case 'H':
            if( ( sMeta[ iStrPtr - 1 ] == 'A' ||
                  sMeta[ iStrPtr - 1 ] == 'E' ||
                  sMeta[ iStrPtr - 1 ] == 'I' ||
                  sMeta[ iStrPtr - 1 ] == 'O' ||
                  sMeta[ iStrPtr - 1 ] == 'U' ) && !
                ( sMeta[ iStrPtr + 1 ] == 'A' ||
                  sMeta[ iStrPtr + 1 ] == 'E' ||
                  sMeta[ iStrPtr + 1 ] == 'I' ||
                  sMeta[ iStrPtr + 1 ] == 'O' ||
                  sMeta[ iStrPtr + 1 ] == 'U' ) )
               iStrPtr++;
            else
            {
               sReturn[ iRetPtr++ ] = 'H';
               iStrPtr++;
            }
            break;

         /* Default action, skip it. */
         default:
            iStrPtr++;
      }
   }

   /* Return the prepared string.  Return only the lenght that we know
      is good so we don't return any uninitialized part of memory. */
   hb_retclen( sReturn, iRetPtr );

   /* Release the memory that we allocated for the return string. */
   hb_xfree( sReturn );
}

/**
 *  Purpose: Sets the given bit in a passed bit string.  Returns the previous
 *           value.  Be sure to pass the string by reference.  NOTE.  In order
 *           to stay as fast as possible, minimal parameter checking is
 *           performed.  It is up to the user to not be too stupid.
 *
 *   Syntax: bit( @<OptC String>, <OptN (1...n) Offset> [, <OptL Set/Clear>] )
 */
HB_FUNC( BIT )
{
   HB_UCHAR mask;
   char *   ptr;
   HB_SIZE  loc;
   HB_SIZE  offset = hb_parns( 2 ) - 1;
   HB_SIZE  res    = 0;

   loc = offset / 8;
   if( loc < hb_parclen( 1 ) )
   {
      HB_SIZE nLen;
      hb_itemGetWriteCL( hb_param( 1, HB_IT_STRING ), &ptr, &nLen );
      ptr += loc;
      loc  = offset % 8;
      res  = ( HB_UCHAR ) *ptr << loc & 0x80;

      if( hb_pcount() > 2 )
      {
         mask = ( HB_UCHAR ) 0x80 >> loc;
         if( hb_parl( 3 ) )
            *ptr = ( HB_UCHAR ) *ptr | mask;
         else
            *ptr = ( HB_UCHAR ) *ptr & ~mask;
      }
   }

   hb_retl( res != 0 );
}

static HB_BOOL WordSep( HB_UCHAR c )
{
   return c <= ' ' ||
          ( c != 39 && ( c > ' ' && c < '0' ) ) ||
          ( c > '9' && c < 'A' ) ||
          ( c > 'Z' && c < 'a' ) ||
          ( c > 'z' && c < 128 ); /* Support international characters, too. */
}

/**
 *  Author: John F. Kaster
 *   Notes: Copyright (c) 1994 by John F. Kaster and Joseph D. Booth
 *          Written for Grumpfish Speller to make it way faster than Prolixity.
 *
 *  Syntax: Sp_Line( <cText>, @<nOffset>, <nLineLen> ) -> cLine
 *
 *  Arguments:
 *  <cText>     Text from which to retrieve a wrapped line
 *  <nOffset>   Offset (usually passed by reference) for start of wrapped line.
 *              Will be set to 0 when the end of the string is encountered.
 *              Defaults to start of string.
 *  <nLineLen>  Maximum wrap length for line.  Defaults to 75.
 */
HB_FUNC( SP_LINE )
{
   int          nArgs      = hb_pcount();
   HB_BOOL      bLineBreak = HB_FALSE;
   HB_ISIZ      nCount     = 0;
   HB_ISIZ      nWrap      = 0;
   HB_SIZE      nOffset    = 0;
   const char * cIn;
   const char * p;
   HB_BYTE      cTest;
   HB_ISIZ      nLineLen;
   HB_SIZE      nStop;

   if( nArgs > 0 && HB_ISCHAR( 1 ) )
   {
      cIn   = hb_parc( 1 );
      nStop = hb_parclen( 1 );
      if( nArgs > 1 && HB_ISNUM( 2 ) )
      {
         nOffset = hb_parns( 2 );
         if( nOffset > 0 )
            nOffset--;
      }

      if( nOffset < nStop )                           /* In string somewhere */
      {
         /* Default line len to 75 */
         nLineLen = nArgs > 2 && HB_ISNUM( 3 ) ? hb_parns( 3 ) - 1 : 75;
         p        = &cIn[ nOffset ];                  /* Starting pointer */

         if( nOffset + nLineLen > nStop )             /* Past end of string? */
            nLineLen = nStop - nOffset;               /* Limit to end of string */

         while( ! bLineBreak && nCount++ < nLineLen )
         {
            cTest = *p++;
            if( cTest == 13 || cTest == 141 )         /* Hard or soft return? */
               bLineBreak = HB_TRUE;
            else if( WordSep( cTest ) )               /* Wrappable character? */
               nWrap = nCount - 1;
         }

         if( ! bLineBreak && nWrap > 0 )              /* Back up to wrap pos */
            nCount = nWrap;

         hb_retclen( &( cIn[ nOffset ] ), nCount + 1 - ( bLineBreak ? 2 : 0 ) );
         nOffset += nCount + 1;
         if( ! bLineBreak )
         {
            while( cIn[ nOffset ] == ' ' )            /* Remove leading spaces */
               nOffset++;
         }
         nOffset++;                                   /* +1 for Harbour string */
      }
      else
      {
         nOffset = 0;
         hb_retc_null();
      }
   }
   else
      hb_retc_null();

   hb_storns( nOffset, 2 );
}
