+ bin/check.hb
+ new source verifier. Able to all documented (and more)
required text file and filename properties.
* bin/commit.hb
+ added feature to verify all files to be committed to
conform with certain naming and text file content rules
+ added some more feedback
* config/postinst.hb
+ include commit.hb and check.hb in 'install'
* include/extend.h
* contrib/hbcairo/paths.c
* contrib/hbcairo/png.c
* contrib/hbcairo/util.c
* contrib/hbtpathy/readme.txt
* tests/harbour.ini
* tests/parseins.ini
! multiple EOL at EOF
* contrib/hbhpdf/tests/files/cp932.txt
* contrib/hbhpdf/tests/files/cp936.txt
* contrib/hbhpdf/tests/files/elic_jp.txt
! line ending spaces
! missing EOL at EOF
* contrib/hbhttpd/tests/tpl/app/account/edit.tpl
* contrib/hbhttpd/tests/tpl/app/login.tpl
* contrib/hbhttpd/tests/tpl/app/logout.tpl
* contrib/hbhttpd/tests/tpl/app/main.tpl
* contrib/hbhttpd/tests/tpl/app/register.tpl
* lib/3rd/win/bcc/unicows_license.txt
* lib/3rd/win/mingw/libunicows_license.txt
! missing EOL at EOF
* COPYING.txt
* src/rtl/gtcrs/hb-charmap.def
* tests/big5_gen.prg
! tabs
* extras/gfspell/spell.prg
* src/rtl/gtwin/gtwin.c
! ASCII 127
* src/codepage/cp_tpl.c
! converted to UTF-8 (just guessing what the original CP was)
* src/pp/hbpp.c
! do not generate SVN header in hbverbld.h
+ avoid SVN header string in source as is to miss matched
in source verifier
* src/vm/extrap.c
! do not generate whitespace at EOL in output
* tests/lang2po.hb
* tests/po2lang.hb
+ minor improvements
! fixed to generate consistent EOL
* bin/3rdpatch.hb
* newline at top deleted
* extras/hbdoc/hbdoc.hbp
* extras/hbdoc/hbdoc.prg
* extras/hbdoc/_tmplates.prg -> extras/hbdoc/_tmplate.prg
* contrib/hbcairo/tests/lightning.prg -> contrib/hbcairo/tests/lightnin.prg
! long filename
* .gitattributes
* extras/guestbk/guestbk.prg
* extras/guestbk/guestbk.txt
* extras/guestbk/guestbk.htm -> extras/guestbk/guestbk.html
* deleted .htm file, now all HTML has extension .html
* .exe references in doc made OS neutral
; Can only be used on non-MS-DOS systems, so it's okay
1468 lines
47 KiB
Plaintext
1468 lines
47 KiB
Plaintext
// Author....: Joseph D. Booth
|
|
// Copyright.: (C)1993, Joseph D. Booth, All Rights Reserved
|
|
//
|
|
// Purpose...: Library of functions to allow a Harbour program to
|
|
// spell check a string and to also work with individual
|
|
// words.
|
|
//
|
|
// Sp_Add() - Add word to the dictionary
|
|
// Sp_Cache() - Add word to spelling cache
|
|
// Sp_Check() - Is word spelled correctly?
|
|
// Sp_Clear() - Clear the spelling cache
|
|
// Sp_Init() - Initialize the spelling dictionary
|
|
// Sp_GetSet() - Get/Set global parameters
|
|
// Sp_LoadAux() - Loads an auxiliary dictionary
|
|
// Sp_Suggest() - Offer list of sound alike suggestions
|
|
// Sp_Quick() - Offer list of suggested spellings
|
|
// Sp_WildCard() - List of wildcard matches
|
|
//
|
|
// DBF2Dic() - Convert a DBF file to a DIC file
|
|
// Dic2DBF() - Convert a DIC file to a DBF file
|
|
//
|
|
|
|
#include "fileio.ch"
|
|
|
|
#define EACH_WORD 6
|
|
#define NSIZE ( 26 * 26 * EACH_WORD )
|
|
#define CRLF Chr( 13 ) + Chr( 10 )
|
|
#define FOUR_BYTES hb_BChar( 0 ) + hb_BChar( 0 ) + hb_BChar( 0 ) + hb_BChar( 0 )
|
|
#define MAX_STRING 40000
|
|
|
|
#define COMMON_WORDS t_aGlobal[ 1 ]
|
|
#define CACHE_WORDS t_aGlobal[ 2 ]
|
|
#define DICTIONARY_PATH t_aGlobal[ 3 ]
|
|
#define DICTIONARY_NAME t_aGlobal[ 4 ]
|
|
#define AUXILIARY_DICTIONARY t_aGlobal[ 5 ]
|
|
#define EXTRA_CODE_BLOCK t_aGlobal[ 6 ]
|
|
#define ADD_SUFFIXES t_aGlobal[ 7 ]
|
|
#define ADD_PREFIXES t_aGlobal[ 8 ]
|
|
#define ADD_PLURALS t_aGlobal[ 9 ]
|
|
#define SORT_SUGGESTIONS t_aGlobal[ 10 ]
|
|
#define SUGGEST_PREFERENCE t_aGlobal[ 11 ]
|
|
#define MINIMUM_WORD_LENGTH t_aGlobal[ 12 ]
|
|
#define METAPHONE_SIZE t_aGlobal[ 13 ]
|
|
#define MAX_DIFFERENCE t_aGlobal[ 14 ]
|
|
#define THESAURUS_NAME t_aGlobal[ 15 ]
|
|
#define CHECK_RUNONS t_aGlobal[ 16 ]
|
|
|
|
THREAD STATIC t_aGlobal := { ;
|
|
NIL, ;
|
|
"", ;
|
|
"", ;
|
|
"dict.dic", ;
|
|
"", ;
|
|
NIL, ;
|
|
.T., ;
|
|
.T., ;
|
|
.T., ;
|
|
.T., ;
|
|
"B", ; // (M)etaphone, (A)lgorithmic, (B)oth
|
|
2, ;
|
|
5, ;
|
|
1, ;
|
|
"thes.dic", ;
|
|
.T. }
|
|
|
|
STATIC sc_aContracts := { ;
|
|
{ "CAN'T" ,"CANNOT" }, ;
|
|
{ "WON'T" ,"WILL NOT" }, ;
|
|
{ "AREN'T" ,"ARE NOT" }, ;
|
|
{ "AIN'T" ,"ARE NOT" }, ;
|
|
{ "THEY'RE" ,"THEY ARE" }, ;
|
|
{ "THEY'VE" ,"THEY HAVE" }, ;
|
|
{ "IT'S" ,"IT IS" }, ;
|
|
{ "I'LL" ,"I WILL" }, ;
|
|
{ "I'D" ,"I WOULD" }, ;
|
|
{ "DON'T" ,"DO NOT" } }
|
|
|
|
|
|
THREAD STATIC t_nHandle := F_ERROR
|
|
THREAD STATIC t_cOffsets
|
|
|
|
|
|
// Function: Sp_Add()
|
|
// Purpose: Adds a word to the dictionary
|
|
// Syntax: <logical> := Sp_Add( cWord )
|
|
// Arguments: cWord - Word to add to the auxiliary dictionary
|
|
// Returns: <logical> - TRUE if added,
|
|
// FALSE otherwise
|
|
//
|
|
// Notes: Does not check to see if the word already exists in the
|
|
// dictionary.
|
|
|
|
FUNCTION Sp_Add( cWord )
|
|
|
|
LOCAL was_added := .F. // Was word written to dictionary?
|
|
LOCAL nAuxHandle // Dictionary file handle
|
|
LOCAL nWritten // Number of bytes written
|
|
|
|
// Initialize the spell checker and make sure
|
|
// an auxiliary dictionary name was specified
|
|
|
|
IF Sp_Init() .AND. ! Empty( AUXILIARY_DICTIONARY )
|
|
|
|
cWord := Upper( AllTrim( cWord ) )
|
|
|
|
//
|
|
// If the auxiliary dictionary does not exist,
|
|
// we will create it for the user
|
|
//
|
|
|
|
IF hb_FileExists( AUXILIARY_DICTIONARY )
|
|
nAuxHandle := FOpen( AUXILIARY_DICTIONARY, FO_READWRITE + FO_DENYWRITE )
|
|
ELSE
|
|
nAuxHandle := FCreate( AUXILIARY_DICTIONARY )
|
|
ENDIF
|
|
IF nAuxHandle != F_ERROR
|
|
|
|
FSeek( nAuxHandle, 0, FS_END ) // Bottom of the file
|
|
nWritten := FWrite( nAuxHandle, cWord + CRLF ) // Write word into file
|
|
FClose( nAuxHandle ) // Close the file
|
|
Sp_Cache( cWord ) // Add word to cache
|
|
|
|
was_added := ( nWritten == hb_BLen( cWord ) + hb_BLen( CRLF ) )
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN was_added
|
|
|
|
// Function: Sp_Cache()
|
|
// Purpose: To add a word to the cache list
|
|
// Syntax: <logical> := Sp_Cache( cWord )
|
|
// Arguments: cWord - upper case, all trimmed word to add
|
|
// Returns: <logical> - TRUE if added,
|
|
// FALSE otherwise
|
|
//
|
|
// Static: CACHE_WORDS - String of cache words
|
|
//
|
|
// Notes: Check to see if the word already exists in the cache
|
|
|
|
FUNCTION Sp_Cache( cWord )
|
|
|
|
LOCAL cTemp := "|" + Upper( AllTrim( cWord ) ) + "|"
|
|
LOCAL lAdded := .F.
|
|
|
|
IF ! cTemp $ CACHE_WORDS .AND. Len( CACHE_WORDS ) < MAX_STRING
|
|
CACHE_WORDS += cTemp
|
|
lAdded := .T.
|
|
ENDIF
|
|
|
|
RETURN lAdded
|
|
|
|
// Function: Sp_Check()
|
|
// Purpose: To check the spelling of a word
|
|
// Syntax: <logical> := Sp_Check( cWord )
|
|
// Arguments: cWord - upper case, all trimmed word to check
|
|
// Returns: <logical> - TRUE if valid spelling
|
|
// FALSE otherwise
|
|
//
|
|
// Static: CACHE_WORDS - String of cache words
|
|
// COMMON_WORDS - String of common words
|
|
// t_cOffsets -
|
|
// t_nHandle - Handle DIC file is opened on
|
|
|
|
FUNCTION Sp_Check( cWord )
|
|
|
|
THREAD STATIC t_cBuf := ""
|
|
THREAD STATIC t_cLast
|
|
|
|
THREAD STATIC t_nDicCount := 0
|
|
THREAD STATIC t_nCacheCount := 0
|
|
THREAD STATIC t_nBuffCount := 0
|
|
|
|
LOCAL ok := .T.
|
|
LOCAL cLookup := Upper( RTrim( cWord ) )
|
|
LOCAL nRow
|
|
LOCAL nCol
|
|
LOCAL x
|
|
LOCAL y
|
|
LOCAL z := 4
|
|
LOCAL cTemp
|
|
|
|
IF Sp_Init()
|
|
IF Len( cLookup ) == 1 .AND. cLookup $ "IA"
|
|
RETURN .T.
|
|
ENDIF
|
|
IF Len( cLookup ) < MINIMUM_WORD_LENGTH
|
|
RETURN .T.
|
|
ENDIF
|
|
IF Right( cLookup, 2 ) == "'S"
|
|
cLookUp := SubStr( cLookup, 1, Len( cLookup ) -2 )
|
|
ENDIF
|
|
cTemp := "|" + cLookup + "|"
|
|
IF At( cTemp, COMMON_WORDS ) == 0 // Check the common words first
|
|
IF At( cTemp, CACHE_WORDS ) == 0 // then check the cache words
|
|
ok := .F.
|
|
nRow := Asc( SubStr( cLookup, 1, 1 ) ) - 64
|
|
nCol := Asc( SubStr( cLookup, 2, 1 ) ) - 64
|
|
IF ( nRow > 0 .AND. nRow <= 26 ) .AND. ( nCol > 0 .AND. nCol <= 26 )
|
|
|
|
x := Bin2L( hb_BSubStr( t_cOffsets, ( ( nRow - 1 ) * 156 ) + ( ( nCol - 1 ) * EACH_WORD + 1 ), 4 ) )
|
|
y := Bin2W( hb_BSubStr( t_cOffsets, ( ( nRow - 1 ) * 156 ) + ( ( nCol - 1 ) * EACH_WORD + 5 ), 2 ) )
|
|
|
|
IF ! Empty( x )
|
|
IF !( t_cLast == SubStr( cLookup, 1, 2 ) )
|
|
t_cBuf := Space( y )
|
|
FSeek( t_nHandle, x, FS_SET )
|
|
FRead( t_nHandle, @t_cBuf, y )
|
|
t_nDicCount++
|
|
ELSE
|
|
t_nBuffCount++
|
|
ENDIF
|
|
IF Len( cLookup ) == 3
|
|
z := Asc( SubStr( cLookup, 3, 1 ) ) - 64
|
|
ok := bit( @t_cBuf, z )
|
|
ELSEIF Len( cLookup ) < 3
|
|
ok := bit( @t_cBuf, 27 )
|
|
ELSEIF y > 4
|
|
cTemp := XForm( cLookup )
|
|
DO WHILE z < y
|
|
z := bfat( cTemp, t_cBuf, z )
|
|
IF z < 6
|
|
EXIT
|
|
ELSEIF hb_BSubStr( t_cBuf, z - 1, 1 ) < hb_BChar( 128 )
|
|
z++
|
|
ELSE
|
|
EXIT
|
|
ENDIF
|
|
ENDDO
|
|
ok := z > 4 .AND. z < y
|
|
ENDIF
|
|
t_cLast := SubStr( cLookup, 1, 2 )
|
|
ENDIF
|
|
ENDIF
|
|
ELSE
|
|
t_nCacheCount++
|
|
ENDIF
|
|
ELSE
|
|
t_nCacheCount++
|
|
ENDIF
|
|
ELSE
|
|
ok := .F.
|
|
ENDIF
|
|
IF ! ok .AND. EXTRA_CODE_BLOCK != NIL
|
|
ok := Eval( EXTRA_CODE_BLOCK, cWord )
|
|
ENDIF
|
|
|
|
RETURN ok
|
|
|
|
// Function: Sp_GetBuf()
|
|
// Purpose: To get all words within the buffer
|
|
// Syntax: Sp_GetBuf( <cLetters> )
|
|
// Arguments: <cLetters> - First two letters
|
|
// Returns: cString - Buffer string from DIC file
|
|
|
|
STATIC FUNCTION sp_GetBuf( cLetters )
|
|
|
|
LOCAL x
|
|
LOCAL y
|
|
LOCAL cBuf := ""
|
|
LOCAL nRow := Asc( SubStr( cLetters, 1, 1 ) ) - 64
|
|
LOCAL nCol := Asc( SubStr( cLetters, 2, 1 ) ) - 64
|
|
|
|
IF ( nRow > 0 .AND. nRow <= 26 ) .AND. ( nCol > 0 .AND. nCol <= 26 )
|
|
x := Bin2L( hb_BSubStr( t_cOffsets, ( ( nRow - 1 ) * 156 ) + ( ( nCol - 1 ) * EACH_WORD + 1 ), 4 ) )
|
|
IF ! Empty( x )
|
|
y := Bin2W( hb_BSubStr( t_cOffsets, ( ( nRow - 1 ) * 156 ) + ( ( nCol - 1 ) * EACH_WORD + 5 ), 2 ) )
|
|
cBuf := Space( y )
|
|
FSeek( t_nHandle, x + 4, FS_SET )
|
|
FRead( t_nHandle, @cBuf, y - 4 )
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN cBuf
|
|
|
|
// Function: Sp_Clear()
|
|
// Purpose: To clear out the cache list
|
|
// Syntax: Sp_Clear()
|
|
// Returns: NIL
|
|
//
|
|
// Static: CACHE_WORDS - String of cache words
|
|
|
|
FUNCTION Sp_Clear()
|
|
|
|
CACHE_WORDS := ""
|
|
|
|
RETURN NIL
|
|
|
|
// Function: Sp_GetSet()
|
|
// Purpose: To get/set a parameter for the spell check function
|
|
// Syntax: <xOldValue> := Sp_GetSet( nWhich [, xNewSetting] )
|
|
// Parameters: nWhich - Which parameter to get/set
|
|
// xNewSetting - Value to set parameter to
|
|
//
|
|
// Returns: <logical> TRUE if word succesfully added
|
|
// FALSE if an error occurs, usually network lock
|
|
// problem
|
|
|
|
FUNCTION Sp_GetSet( nWhich, xNewSetting )
|
|
|
|
LOCAL xOld := NIL
|
|
|
|
IF nWhich != NIL .AND. ( nWhich > 0 .AND. nWhich <= Len( t_aGlobal ) )
|
|
xOld := t_aGlobal[ nWhich ]
|
|
IF ValType( xNewSetting ) == ValType( xOld )
|
|
t_aGlobal[ nWhich ] := xNewSetting
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN xOld
|
|
|
|
// Function: Sp_LoadAux()
|
|
// Purpose: To load an auxiliary dictionary of words
|
|
|
|
FUNCTION Sp_LoadAux( cFile )
|
|
|
|
LOCAL is_ok := .F.
|
|
LOCAL x := 0
|
|
LOCAL cStr := ""
|
|
LOCAL nSize
|
|
|
|
IF ! Empty( cFile )
|
|
IF hb_FileExists( cFile )
|
|
x := FOpen( cFile, FO_READ + FO_DENYNONE )
|
|
ELSE
|
|
x := FCreate( cFile )
|
|
ENDIF
|
|
Sp_GetSet( 5, cFile )
|
|
|
|
IF x != F_ERROR
|
|
nSize := FSeek( x, 0, FS_END )
|
|
IF nSize < MAX_STRING
|
|
cStr := Space( nSize )
|
|
FSeek( x, 0, FS_SET )
|
|
FRead( x, @cStr, nSize )
|
|
cStr := "|" + StrTran( cStr, CRLF, "|" )
|
|
CACHE_WORDS += Upper( cStr )
|
|
is_ok := .T.
|
|
ENDIF
|
|
FClose( x )
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN is_ok
|
|
|
|
// Function: Sp_Suggest()
|
|
// Purpose: To return an array of possible spellings
|
|
// Syntax: aSuggest_ := Sp_Suggest( cWord [, lInclude] )
|
|
// Arguments: cWord - Word to look for suggestions for
|
|
// lInclude - Should word be included in list?
|
|
// Returns: aSuggest_ - List of suggested words
|
|
|
|
FUNCTION Sp_Suggest( cWord, lInclude )
|
|
|
|
STATIC sc_aParts_ := { ;
|
|
{"A" , { "AI", "AO", "AU", "AY", "EA", "EI", "EIGH", "ET", "EY", "E", "I", "O" } }, ;
|
|
{"AIR" , { "ARE" } }, ;
|
|
{"AIT" , { "ATE" } }, ;
|
|
{"C" , { "CK" } }, ;
|
|
{"CH" , { "TCH", "TI", "TU" } }, ;
|
|
{"CKS" , { "X" } }, ;
|
|
{"D" , { "ED" } }, ;
|
|
{"E" , { "A", "AE", "AI", "AY", "EA", "EI", "EO", "IE", "I", "U", "O" } }, ;
|
|
{"EM" , { "TEM" } }, ;
|
|
{"ER" , { "OUR", "RE", "URE", "YR" } }, ;
|
|
{"ERE" , { "EIR", "EAR", "IER" } }, ;
|
|
{"F" , { "GH", "LF", "PH", "G" } }, ;
|
|
{"FIZ" , { "PHYS" } }, ;
|
|
{"G" , { "GH", "GU", "GUE" } }, ;
|
|
{"GZ" , { "X" } }, ;
|
|
{"H" , { "WH" } }, ;
|
|
{"I" , { "A", "E", "EE", "IA", "IE", "O", "U", "UI", "Y", "YE", "UY", "EI", "IGH" } }, ;
|
|
{"IS" , { "US", "ACE", "ICE" } }, ;
|
|
{"ISE" , { "IZE" } }, ;
|
|
{"J" , { "D", "DG", "DI", "DJ", "G", "GG" } }, ;
|
|
{"K" , { "C", "CC", "CH", "CK", "CQU", "CU", "LK", "Q", "QU", "QUE" } }, ;
|
|
{"KW" , { "QU" } }, ;
|
|
{"L" , { "SL" } }, ;
|
|
{"LE" , { "TLE", "AL" } }, ;
|
|
{"M" , { "CHM", "GM", "LM", "MB", "MN", "N" } }, ;
|
|
{"N" , { "M", "PN", "GN", "KN", "MN" } }, ;
|
|
{"NG" , { "N", "NGUE" } }, ;
|
|
{"O" , { "AU", "EAU", "EO", "EW", "OA", "OE", "OH", "OU", "OUGH", "OW", "A", "AH", "AW", "UO", "E" } }, ;
|
|
{"OE" , { "OWE", "OUGH" } }, ;
|
|
{"OO" , { "O", "EU", "EW", "OE", "OU", "OUGH", "U", "UE", "UI" } }, ;
|
|
{"OU" , { "OUGH", "OW" } }, ;
|
|
{"PER" , { "PRO", "PRI", "PRA", "PRU" } }, ;
|
|
{"PRE" , { "PRO", "PRI", "PRA", "PRU" } }, ;
|
|
{"R" , { "RH", "WR" } }, ;
|
|
{"S" , { "C", "CE", "PS", "SC", "SCH" } }, ;
|
|
{"SH" , { "CE", "CH", "CI", "S", "SCH", "SCI", "SE", "SI", "SS", "SSI", "TI" } }, ;
|
|
{"SI" , { "PSY", "CY" } }, ;
|
|
{"T" , { "ED", "GHT", "PT", "TH" } }, ;
|
|
{"TION" , { "SION", "CION", "CEAN", "CIAN" } }, ;
|
|
{"TIOUS", { "SEOUS" } }, ;
|
|
{"TURE" , { "TEUR" } }, ;
|
|
{"U" , { "O", "OE", "OO", "OU", "EUA", "EU", "EUE", "EW", "IEU", "IEW", "UE", "UI", "YOU", "YU" } }, ;
|
|
{"UR" , { "EAR", "ER", "IR", "YR" } }, ;
|
|
{"V" , { "F", "LV", "PH" } }, ;
|
|
{"W" , { "O", "U", "WH" } }, ;
|
|
{"X" , { "CKS", "GZ", "K" } }, ;
|
|
{"Y" , { "I", "J", "IE", "EI" } }, ;
|
|
{"Z" , { "S", "SC", "SS", "X", "GE", "SI", "ZI" } }, ;
|
|
{"ZI" , { "XY"} } }
|
|
|
|
STATIC sc_aEnds := { "ED", "ER", "ING", "LY", "AL", "FUL", "NESS", "MENT", "IVE" }
|
|
STATIC sc_aBegs := { "UN", "IN", "DIS", "MIS", "EN", "WEL", "AL" }
|
|
|
|
LOCAL jj, kk, zz, ii
|
|
LOCAL cHold
|
|
LOCAL aRet_ := {} // List of suggested words
|
|
LOCAL cTemp
|
|
LOCAL nSugg
|
|
LOCAL nSize := Len( sc_aParts_ )
|
|
LOCAL nSuffix := Len( sc_aEnds )
|
|
LOCAL cMeta
|
|
LOCAL cFirst
|
|
LOCAL cKey
|
|
LOCAL arr_
|
|
|
|
cWord := Upper( RTrim( cWord ) )
|
|
zz := Len( cWord )
|
|
|
|
IF zz == 1 // Don't offer suggestions for
|
|
RETURN aRet_ // single letter words
|
|
ENDIF
|
|
|
|
IF lInclude == NIL
|
|
lInclude := .F.
|
|
ENDIF
|
|
|
|
//
|
|
// Should the current word be included?
|
|
//
|
|
|
|
IF Sp_Check( cWord )
|
|
AAdd( aRet_, "A0AA" + cWord )
|
|
ENDIF
|
|
|
|
IF "'" $ cWord
|
|
cHold := Sp_Expand( cWord )
|
|
IF ! Empty( cHold )
|
|
AAdd( aRet_, "A1AA" + cHold )
|
|
ENDIF
|
|
ENDIF
|
|
|
|
IF CHECK_RUNONS
|
|
arr_ := Sp_Split( cWord )
|
|
FOR jj := 1 TO Len( arr_ )
|
|
AAdd( aRet_, "A1AA" + arr_[ jj ] )
|
|
NEXT
|
|
ENDIF
|
|
|
|
IF SUGGEST_PREFERENCE $ "AB"
|
|
|
|
//
|
|
// Step One - Do letter doubling
|
|
//
|
|
|
|
FOR jj := 2 TO zz
|
|
IF SubStr( cWord, jj, 1 ) $ "BCDEFGKLMNOPRSTZ"
|
|
|
|
cHold := Left( cWord, jj ) + SubStr( cWord, jj, 1 ) + ;
|
|
SubStr( cWord, jj + 1 )
|
|
|
|
//
|
|
// If the word is not already in the list, then check
|
|
// to see if it is a valid word.
|
|
//
|
|
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cHold } ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( aRet_, "B" + Sp_Rate( cHold, cWord ) + cHold )
|
|
ENDIF
|
|
ENDIF
|
|
NEXT
|
|
|
|
//
|
|
// Step Two - Remove extra letters
|
|
//
|
|
|
|
FOR jj := 1 TO zz
|
|
cHold := Left( cWord, jj - 1 ) + SubStr( cWord, jj + 1 )
|
|
//
|
|
// If the word is not already in the list, then check
|
|
// to see if it is a valid word.
|
|
//
|
|
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cHold } ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( aRet_, "B" + Sp_Rate( cHold, cWord ) + cHold )
|
|
ENDIF
|
|
NEXT
|
|
|
|
//
|
|
// Step Three - Transpose the letters
|
|
//
|
|
|
|
FOR jj := 2 TO zz
|
|
cHold := Left( cWord, jj - 2 ) + SubStr( cWord, jj, 1 ) + ;
|
|
SubStr( cWord, jj - 1, 1 ) + SubStr( cWord, jj + 1 )
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cHold } ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( aRet_, "B" + Sp_Rate( cHold, cWord ) + cHold )
|
|
ENDIF
|
|
NEXT
|
|
|
|
//
|
|
// Step Four - Try adding a silent E to the end
|
|
//
|
|
|
|
cHold := cWord + "E"
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cHold } ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( aRet_, "B" + Sp_Rate( cHold, cWord ) + cHold )
|
|
ENDIF
|
|
|
|
//
|
|
// Step Five - Do sound alike substitutions
|
|
//
|
|
|
|
FOR jj := 1 TO nSize
|
|
IF sc_aParts_[ jj, 1 ] $ cWord
|
|
IF sc_aParts_[ jj, 1 ] $ "AEIOUT"
|
|
ii := 0
|
|
DO WHILE ( ii := fat( sc_aParts_[ jj, 1 ], cWord, ii ) ) > 0
|
|
FOR kk := 1 TO Len( sc_aParts_[ jj, 2 ] )
|
|
cHold := SubStr( cWord, 1, ii - 1 ) + sc_aParts_[ jj, 2, kk ] + SubStr( cWord, ii + 1 )
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cHold } ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( aRet_, "B" + Sp_Rate( cHold, cWord ) + cHold )
|
|
ENDIF
|
|
NEXT
|
|
ii++
|
|
ENDDO
|
|
ELSE
|
|
FOR kk := 1 TO Len( sc_aParts_[ jj, 2 ] )
|
|
cHold := StrTran( cWord, sc_aParts_[ jj, 1 ], sc_aParts_[ jj, 2, kk ] )
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cHold } ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( aRet_, "B" + Sp_Rate( cHold, cWord ) + cHold )
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
ENDIF
|
|
NEXT
|
|
nSugg := Len( aRet_ )
|
|
|
|
//
|
|
// At this point, we have a list of words in aRet_, which now need to
|
|
// be checked for suffixes, prefixes, and plurals.
|
|
//
|
|
|
|
FOR jj := 1 TO nSugg
|
|
cHold := RTrim( SubStr( aRet_[ jj ], 5 ) ) // Extract the word
|
|
zz := Len( cHold )
|
|
//
|
|
// Check suffixes
|
|
//
|
|
//
|
|
IF ADD_SUFFIXES
|
|
FOR kk := 1 TO nSuffix
|
|
cTemp := cHold + sc_aEnds[ kk ]
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
NEXT
|
|
//
|
|
// Try doubling the last letter
|
|
//
|
|
|
|
FOR kk := 1 TO nSuffix
|
|
cTemp := cHold + SubStr( cHold, zz, 1 ) + sc_aEnds[ kk ]
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
NEXT
|
|
//
|
|
// Accomodate words ending in C
|
|
//
|
|
|
|
IF SubStr( cHold, -1, 1 ) == "C"
|
|
FOR kk := 1 TO nSuffix
|
|
cTemp := cHold + "K" + sc_aEnds[ kk ]
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
//
|
|
// Accomodate words ending in ND
|
|
//
|
|
|
|
IF SubStr( cHold, -2, 2 ) == "ND"
|
|
cTemp := SubStr( cHold, 1, zz - 1 ) + "SE"
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
FOR kk := 1 TO nSuffix
|
|
cTemp := SubStr( cHold, 1, zz - 1 ) + "SE" + sc_aEnds[ kk ]
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
cTemp := SubStr( cHold, 1, zz - 1 ) + "S" + sc_aEnds[ kk ]
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
//
|
|
// Words ending in E, remove the E and try
|
|
//
|
|
|
|
IF SubStr( cHold, zz, 1 ) == "E"
|
|
FOR kk := 1 TO nSuffix
|
|
cTemp := SubStr( cHold, 1, zz - 1 ) + sc_aEnds[ kk ]
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
ENDIF
|
|
|
|
IF ADD_PREFIXES
|
|
FOR kk := 1 TO 7
|
|
cTemp := sc_aBegs[ kk ] + cHold
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
|
|
IF ADD_PLURALS
|
|
cTemp := cHold + "S"
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
|
|
IF SubStr( cHold, zz, 1 ) == "Y"
|
|
cTemp := SubStr( cHold, 1, zz - 1 ) + "IES"
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
ELSEIF SubStr( cHold, zz, 1 ) == "F"
|
|
cTemp := SubStr( cHold, 1, zz - 1 ) + "VES"
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
ELSEIF SubStr( cHold, zz, 1 ) == "O"
|
|
cTemp := cHold + "ES"
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cTemp } ) == 0 .AND. ;
|
|
Sp_Check( cTemp )
|
|
AAdd( aRet_, "C" + Sp_Rate( cTemp, cWord ) + cTemp )
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
|
|
//
|
|
// Check metaphone matches, only if SUGGEST_PREFERENCE is metafone or both,
|
|
// because this search can slow things down a bit
|
|
//
|
|
|
|
IF SUGGEST_PREFERENCE $ "MB"
|
|
IF METAPHONE_SIZE == 0
|
|
zz := Min( 5, Len( cWord ) )
|
|
IF zz < 3
|
|
zz := 3
|
|
ENDIF
|
|
ELSE
|
|
zz := METAPHONE_SIZE
|
|
ENDIF
|
|
cTemp := Sp_GetBuf( cWord )
|
|
ii := Len( cTemp )
|
|
cMeta := C_Metafone( cWord, zz )
|
|
zz := Len( cMeta )
|
|
cFirst := SubStr( cWord, 1, 2 )
|
|
|
|
IF ii > 0
|
|
|
|
IF SubStr( cMeta, 2, 1 ) >= "N"
|
|
|
|
kk := ii - 5
|
|
jj := ii - 4
|
|
|
|
DO WHILE kk > 0
|
|
IF Asc( SubStr( cTemp, kk, 1 ) ) >= 128 // End of word
|
|
cHold := cFirst + XUnForm( SubStr( cTemp, kk + 1, jj - kk ) )
|
|
cKey := C_Metafone( cHold, zz )
|
|
IF cMeta == C_Metafone( cHold, zz )
|
|
IF MAX_DIFFERENCE < 0 .OR. Abs( Len( cWord ) -Len( cHold ) ) <= MAX_DIFFERENCE
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cHold } ) == 0
|
|
AAdd( aRet_, "B" + Sp_Rate( cHold, cWord ) + cHold )
|
|
ENDIF
|
|
ENDIF
|
|
ELSEIF cKey < cMeta
|
|
EXIT
|
|
ENDIF
|
|
jj := kk
|
|
ENDIF
|
|
kk--
|
|
ENDDO
|
|
|
|
ELSE
|
|
kk := 1
|
|
jj := 1
|
|
|
|
DO WHILE kk < ii
|
|
IF Asc( SubStr( cTemp, kk, 1 ) ) >= 128 // End of word
|
|
cHold := cFirst + XUnForm( SubStr( cTemp, jj, kk - jj + 1 ) )
|
|
cKey := C_Metafone( cHold, zz )
|
|
IF cMeta == C_Metafone( cHold, zz )
|
|
IF MAX_DIFFERENCE < 0 .OR. Abs( Len( cWord ) -Len( cHold ) ) <= MAX_DIFFERENCE
|
|
IF AScan( aRet_, {| xx | SubStr( xx, 5 ) == cHold } ) == 0
|
|
AAdd( aRet_, "B" + Sp_Rate( cHold, cWord ) + cHold )
|
|
ENDIF
|
|
ENDIF
|
|
ELSEIF cKey > cMeta
|
|
EXIT
|
|
ENDIF
|
|
jj := kk + 1
|
|
ENDIF
|
|
kk++
|
|
ENDDO
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
|
|
nSugg := Len( aRet_ )
|
|
IF nSugg > 1 .AND. SORT_SUGGESTIONS
|
|
ASort( aRet_ )
|
|
ENDIF
|
|
|
|
FOR kk := 1 TO nSugg
|
|
aRet_[ kk ] := SubStr( aRet_[ kk ], 5 )
|
|
NEXT
|
|
|
|
RETURN aRet_
|
|
|
|
// Function: Sp_Quick()
|
|
// Purpose: To return an array of quick spellings
|
|
// Syntax: aSuggest := Sp_Quick( cWord )
|
|
// Arguments: cWord - Word to look for suggestions for
|
|
// Returns: aSuggest - List of suggested words
|
|
|
|
FUNCTION Sp_Quick( cWord )
|
|
|
|
STATIC sc_aTryThese := { ;
|
|
"AI$AO$AU$AY$EA$EI$EIGH$ET$EY$E$O$", ;
|
|
"A$AE$AI$AY$EA$EI$EO$IE$U$O$", ;
|
|
"A$E$EE$IA$IE$O$U$UI$Y$YE$UY$EI$IGH$", ;
|
|
"AU$EAU$EO$EW$OA$OE$OH$OU$OUGH$OW$A$AH$AW$UO$E$", ;
|
|
"O$OE$OO$OU$EUA$EU$EUE$EW$IEU$IEW$UE$UI$YOU$YU$", ;
|
|
"ED$GHT$PT$TH$" }
|
|
|
|
LOCAL ii := 0
|
|
LOCAL nOld, jj, kk
|
|
LOCAL zz
|
|
LOCAL arr_ := {}
|
|
LOCAL cHold, ll, cTemp
|
|
|
|
cWord := Upper( RTrim( cWord ) )
|
|
|
|
zz := Len( cWord )
|
|
|
|
IF zz < 3 // Don't offer suggestions for
|
|
RETURN {} // one or two letter words
|
|
ENDIF
|
|
|
|
//
|
|
// Step One - Do letter doubling
|
|
//
|
|
|
|
FOR jj := 2 TO zz
|
|
IF SubStr( cWord, jj, 1 ) $ "BCDEFGKLMNOPRSTZ"
|
|
cHold := Left( cWord, jj ) + SubStr( cWord, jj, 1 ) + ;
|
|
SubStr( cWord, jj + 1 )
|
|
//
|
|
// If the word is not already in the list, then check
|
|
// to see if it is a valid word.
|
|
//
|
|
|
|
IF AScan( arr_, cHold ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( arr_, cHold )
|
|
ENDIF
|
|
ENDIF
|
|
NEXT
|
|
|
|
//
|
|
// Step Two - Remove extra letters
|
|
//
|
|
|
|
FOR jj := 1 TO zz
|
|
cHold := Left( cWord, jj - 1 ) + SubStr( cWord, jj + 1 )
|
|
//
|
|
// If the word is not already in the list, then check
|
|
// to see if it is a valid word.
|
|
//
|
|
|
|
IF AScan( arr_, cHold ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( arr_, cHold )
|
|
ENDIF
|
|
NEXT
|
|
|
|
//
|
|
// Step Three - Transpose the letters
|
|
//
|
|
|
|
FOR jj := 2 TO zz
|
|
cHold := Left( cWord, jj - 2 ) + SubStr( cWord, jj, 1 ) + ;
|
|
SubStr( cWord, jj - 1, 1 ) + SubStr( cWord, jj + 1 )
|
|
IF AScan( arr_, cHold ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( arr_, cHold )
|
|
ENDIF
|
|
NEXT
|
|
|
|
//
|
|
// Step Four - Try adding a silent E to the end
|
|
//
|
|
|
|
cHold := cWord + "E"
|
|
IF AScan( arr_, cHold ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( arr_, cHold )
|
|
ENDIF
|
|
|
|
//
|
|
// Step Five - Do sound alike substitutions
|
|
//
|
|
|
|
FOR jj := 1 TO 6
|
|
IF SubStr( "AEIOUT", jj, 1 ) $ cWord
|
|
ii := fat( SubStr( "AEIOUT", jj, 1 ), cWord, ii )
|
|
nold := 1
|
|
DO WHILE ii > 0
|
|
FOR kk := 1 TO ChrCount( "$", sc_aTryThese[ jj ] )
|
|
ll := fat( "$", sc_aTryThese[ jj ], nOld )
|
|
cTemp := SubStr( sc_aTryThese[ jj ], nOld, ll - nOld )
|
|
nOld := ll + 1
|
|
cHold := SubStr( cWord, 1, ii - 1 ) + cTemp + SubStr( cWord, ii + 1 )
|
|
IF AScan( arr_, cHold ) == 0 .AND. Sp_Check( cHold )
|
|
AAdd( arr_, cHold )
|
|
ENDIF
|
|
NEXT
|
|
ii++
|
|
ii := fat( SubStr( "AEIOUT", jj, 1 ), cWord, ii )
|
|
ENDDO
|
|
ENDIF
|
|
NEXT
|
|
|
|
RETURN arr_
|
|
|
|
STATIC FUNCTION ChrCount( cChar, cString )
|
|
RETURN Len( cString ) - Len( StrTran( cString, cChar ) )
|
|
|
|
FUNCTION Sp_Split( cWord )
|
|
|
|
LOCAL arr_ := {}
|
|
LOCAL x
|
|
LOCAL cWord1
|
|
LOCAL cWord2
|
|
|
|
FOR x := 2 TO Len( cWord )
|
|
cWord1 := Left( cWord, x - 1 )
|
|
cWord2 := SubStr( cWord, x )
|
|
IF Len( cWord1 ) > 1 .AND. Len( cWord2 ) > 1
|
|
IF Sp_Check( cWord1 ) .AND. Sp_Check( cWord2 )
|
|
AAdd( arr_, cWord1 + " " + cWord2 )
|
|
ENDIF
|
|
ENDIF
|
|
NEXT
|
|
|
|
RETURN arr_
|
|
|
|
FUNCTION Sp_Expand( cWord )
|
|
|
|
LOCAL x
|
|
LOCAL cExpand
|
|
|
|
cWord := Upper( AllTrim( cWord ) )
|
|
x := AScan( sc_aContracts, {| jj | Left( jj[ 1 ], Len( cWord ) ) == cWord } ) // LEFTEQUAL()
|
|
IF x > 0
|
|
cExpand := sc_aContracts[ x, 2 ]
|
|
ENDIF
|
|
|
|
RETURN cExpand
|
|
|
|
// Function: Sp_WildCard()
|
|
// Purpose: To return an array of wildcard matches
|
|
// Syntax: aSuggest := Sp_WildCard( cPattern )
|
|
// Arguments: cPattern - Pattern to match using * or ?'s
|
|
// Returns: aSuggest - List of matching words
|
|
|
|
FUNCTION Sp_WildCard( cPattern )
|
|
|
|
LOCAL cTemp
|
|
LOCAL ii, kk, jj, cFirst, cHold, x, z
|
|
LOCAL arr_ := {}
|
|
LOCAL nStart, nEnd
|
|
|
|
cPattern := Upper( cPattern )
|
|
|
|
IF Sp_Init()
|
|
x := At( "*", cPattern )
|
|
IF x == 0
|
|
x := At( "?", cPattern )
|
|
ENDIF
|
|
IF x == 1 // Can't handle wildcards in first position
|
|
RETURN arr_
|
|
ENDIF
|
|
IF x == 2
|
|
nStart := 1
|
|
nEnd := 26
|
|
ELSE
|
|
nStart := Asc( SubStr( cPattern, 2, 1 ) ) - 64
|
|
nEnd := nStart
|
|
ENDIF
|
|
|
|
|
|
FOR z := nStart TO nEnd
|
|
cTemp := Sp_GetBuf( SubStr( cPattern, 1, 1 ) + Chr( z + 64 ) )
|
|
ii := Len( cTemp )
|
|
cFirst := SubStr( cPattern, 1, 1 ) + Chr( z + 64 )
|
|
|
|
IF ii > 0
|
|
kk := 1
|
|
jj := 1
|
|
DO WHILE kk < ii
|
|
IF Asc( SubStr( cTemp, kk, 1 ) ) >= 128 // End of word
|
|
cHold := cFirst + XUnForm( SubStr( cTemp, jj, kk - jj + 1 ) )
|
|
IF WildCard( cPattern, cHold )
|
|
AAdd( arr_, cHold )
|
|
ENDIF
|
|
jj := kk + 1
|
|
ENDIF
|
|
kk++
|
|
ENDDO
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
|
|
RETURN arr_
|
|
|
|
// The following functions are internal and should not be modified.
|
|
|
|
// Function: Sp_Init()
|
|
// Purpose: Internal function to initialize the dictionary
|
|
// Returns: <logical> - Was dictionary initialized?
|
|
|
|
FUNCTION Sp_Init()
|
|
|
|
LOCAL isok := .T.
|
|
LOCAL cBuf
|
|
LOCAL i
|
|
LOCAL j
|
|
LOCAL nOther := 0
|
|
LOCAL cOther := ""
|
|
LOCAL nFileSize
|
|
|
|
IF t_cOffsets == NIL
|
|
isok := .F.
|
|
t_nHandle := FOpen( DICTIONARY_PATH + DICTIONARY_NAME, FO_READ + FO_DENYWRITE )
|
|
IF t_nHandle != F_ERROR
|
|
|
|
cBuf := Space( NSIZE + 6 )
|
|
FRead( t_nHandle, @cBuf, NSIZE + 6 )
|
|
|
|
IF SubStr( cBuf, 1, 2 ) == "JJ"
|
|
nOther := Bin2L( SubStr( cBuf, 3, 4 ) )
|
|
t_cOffsets := hb_BSubStr( cBuf, 7 )
|
|
nFileSize := FSeek( t_nHandle, 0, FS_END )
|
|
IF nFileSize - nOther > 0
|
|
cOther := Space( nFileSize - nOther )
|
|
FSeek( t_nHandle, nOther, FS_SET )
|
|
FRead( t_nHandle, @cOther, ( nFileSize - nOther ) )
|
|
ENDIF
|
|
t_aGlobal[ 2 ] += cOther
|
|
isok := .T.
|
|
ENDIF
|
|
IF ! Empty( AUXILIARY_DICTIONARY )
|
|
Sp_LoadAux( AUXILIARY_DICTIONARY )
|
|
ENDIF
|
|
ENDIF
|
|
IF t_aGlobal[ 1 ] == NIL
|
|
Sp_Common()
|
|
ENDIF
|
|
|
|
// Thesaurus comented out as not needed
|
|
#if 0
|
|
IF ! Empty( THESAURUS_NAME )
|
|
IF hb_FileExists( DICTIONARY_PATH + THESAURUS_NAME )
|
|
Sp_OpenThes( DICTIONARY_PATH + THESAURUS_NAME )
|
|
ELSEIF hb_FileExists( THESAURUS_NAME )
|
|
Sp_OpenThes( THESAURUS_NAME )
|
|
ENDIF
|
|
ENDIF
|
|
#endif
|
|
|
|
ENDIF
|
|
|
|
RETURN isok
|
|
|
|
|
|
// Function: DBF2Dic()
|
|
// Purpose: To create a DIC file from a DBF list of words
|
|
// Syntax: nStatus := DBF2Dic( <cDBF_file>, <cDIC_file> )
|
|
// Arguments: <cDBF_File> - DBF containing sorted list of upper
|
|
// case words. The field name must be
|
|
// WORD and of type character
|
|
// <cDIC_File> - Name of DIC file to create, assumes
|
|
// 'dict.dic'
|
|
// Returns: nStatus - 0 if successful
|
|
// -1 DBF file does not exist
|
|
// -2 Field WORD does not exist in DBF
|
|
// >0 File error occurred, see FError()
|
|
|
|
FUNCTION DBF2Dic( cDbf, cDictionary, lTalk )
|
|
|
|
LOCAL nStatus := 0 // Status code
|
|
LOCAL i // Loop counters
|
|
LOCAL j
|
|
LOCAL x
|
|
LOCAL nH // File handle
|
|
LOCAL nWhere
|
|
LOCAL temp
|
|
LOCAL cBits
|
|
LOCAL cSave
|
|
LOCAL nSize
|
|
LOCAL cOther := ""
|
|
LOCAL nCurRec := 0
|
|
|
|
//
|
|
// DEFAULT name for dictionary file
|
|
//
|
|
|
|
IF cDictionary == NIL
|
|
cDictionary := "dict.dic"
|
|
ENDIF
|
|
|
|
IF lTalk == NIL
|
|
lTalk := .F.
|
|
ENDIF
|
|
|
|
//
|
|
// See if the DBF file exists
|
|
//
|
|
|
|
IF ! "." $ cDBF
|
|
cDBF += ".dbf"
|
|
ENDIF
|
|
|
|
IF ! hb_FileExists( cDBF )
|
|
RETURN -1
|
|
ENDIF
|
|
|
|
USE ( cDbf ) ALIAS DICT NEW
|
|
IF FieldPos( "WORD" ) == 0
|
|
USE
|
|
RETURN -2
|
|
ENDIF
|
|
|
|
IF lTalk
|
|
|
|
cSave := SaveScreen( 8, 30, 11, 48 )
|
|
hb_DispBox( 8, 30, 11, 48,, "W+/R" )
|
|
hb_DispOutAt( 8, 33, " Creating DIC ", "W+/R" )
|
|
hb_DispOutAt( 9, 31, " Indexing words ", "W/R" )
|
|
hb_DispOutAt( 10, 31, " ", "W/R" )
|
|
|
|
nSize := DICT->( LastRec() )
|
|
ENDIF
|
|
|
|
INDEX ON SubStr( DICT->word, 1, 2 ) + PadR( C_Metafone( AllTrim( DICT->word ), 5 ), 6 ) TO ( "$$temp" )
|
|
dbGoTop()
|
|
|
|
IF lTalk
|
|
hb_DispOutAt( 9, 31, "Percent ", "W/R" )
|
|
hb_DispOutAt( 10, 31, "Complete: ", "W/R" )
|
|
ENDIF
|
|
|
|
//
|
|
// Create the dictionary file
|
|
//
|
|
|
|
// ADDED - 1996-02-08 - JAMES
|
|
IF t_nHandle != F_ERROR // Is dictionary already open?
|
|
FClose( t_nHandle ) // Yes, close it
|
|
t_nHandle := F_ERROR
|
|
ENDIF // END OF ADDITION
|
|
|
|
nH := FCreate( cDictionary )
|
|
|
|
IF nH != F_ERROR
|
|
//
|
|
// Write out enough bytes to hold the index information
|
|
//
|
|
|
|
FWrite( nH, "JJ" + L2Bin( NSIZE + 4 ) + Replicate( hb_BChar( 0 ), NSIZE ) + Space( 10 ) )
|
|
|
|
FOR i := 1 TO 26
|
|
DO WHILE Left( DICT->word, 1 ) == Chr( i + 64 ) .AND. ! Eof()
|
|
FOR j := 1 TO 26
|
|
temp := ""
|
|
cBits := FOUR_BYTES
|
|
DO WHILE ! IsAlpha( SubStr( DICT->word, 2, 1 ) ) .AND. ! Eof()
|
|
IF Len( AllTrim( DICT->word ) ) > 0
|
|
cOther += "|" + AllTrim( DICT->word )
|
|
ENDIF
|
|
dbSkip()
|
|
ENDDO
|
|
DO WHILE SubStr( DICT->word, 2, 1 ) == Chr( j + 64 ) .AND. ! Eof()
|
|
IF Len( RTrim( DICT->word ) ) == 3
|
|
bit( @cBits, Asc( SubStr( DICT->word, 3, 1 ) ) - 64, .T. )
|
|
ELSEIF Len( RTrim( DICT->word ) ) == 2
|
|
bit( @cBits, 27, .T. )
|
|
ELSE
|
|
temp += XForm( RTrim( DICT->word ) )
|
|
ENDIF
|
|
dbSkip()
|
|
IF lTalk
|
|
nCurRec++
|
|
hb_DispOutAt( 10, 41, Transform( ( nCurRec / nSize ) * 100, "999.9%" ), "W+/R" )
|
|
ENDIF
|
|
ENDDO
|
|
IF ! Empty( temp ) .OR. !( cBits == FOUR_BYTES )
|
|
|
|
nWhere := FSeek( nH, 0, FS_END )
|
|
|
|
FSeek( nH, ( ( i - 1 ) * 26 * EACH_WORD ) + ( ( j - 1 ) * EACH_WORD ) + 6 )
|
|
FWrite( nH, L2Bin( nWhere ) + I2Bin( hb_BLen( temp ) + 4 ), EACH_WORD )
|
|
FSeek( nH, 0, FS_END )
|
|
FWrite( nH, cBits + temp )
|
|
ENDIF
|
|
NEXT
|
|
ENDDO
|
|
NEXT
|
|
j := FSeek( nH, 0, FS_END )
|
|
FSeek( nH, 2, FS_SET )
|
|
FWrite( nH, L2Bin( j ) )
|
|
IF ! Empty( cOther )
|
|
FSeek( nH, j, FS_SET )
|
|
cOther += "|"
|
|
FWrite( nH, cOther )
|
|
ENDIF
|
|
FClose( nH )
|
|
ELSE
|
|
nStatus := nH
|
|
ENDIF
|
|
|
|
IF lTalk
|
|
RestScreen( 8, 30, 11, 48, cSave )
|
|
ENDIF
|
|
|
|
SELECT DICT
|
|
USE
|
|
FErase( "$$temp" + IndexExt() )
|
|
|
|
RETURN nStatus
|
|
|
|
// Function: Dic2DBF()
|
|
// Purpose: To create a DBF file from a DIC file
|
|
// Syntax: nStatus := Dic2DBF( <cDIC_file>, <cDBF_file> )
|
|
// Arguments: <cDIC_File> - Name of DIC file to read, assumes
|
|
// dict.dic
|
|
// <cDBF_File> - Name of DBF to create, assumes dict.dbf
|
|
// It will contain a single field called
|
|
// WORD of type character
|
|
// Returns: nStatus - 0 if successful
|
|
// -1 DIC file does not exist
|
|
// -2 DIC file is not a valid dictionary
|
|
|
|
FUNCTION Dic2DBF( cDictionary, cDBF, lTalk )
|
|
|
|
LOCAL nStatus := 0 // Status code
|
|
LOCAL i // Loop counters
|
|
LOCAL j
|
|
LOCAL x, y, z
|
|
LOCAL temp
|
|
LOCAL cBuf
|
|
LOCAL cWord
|
|
LOCAL cSave
|
|
LOCAL nSize
|
|
|
|
//
|
|
// DEFAULT name for dictionary file
|
|
//
|
|
|
|
IF cDictionary == NIL
|
|
cDictionary := "dict.dic"
|
|
ENDIF
|
|
|
|
IF lTalk == NIL
|
|
lTalk := .F.
|
|
ENDIF
|
|
|
|
IF ! hb_FileExists( cDictionary )
|
|
RETURN -1
|
|
ENDIF
|
|
|
|
DICTIONARY_NAME := cDictionary
|
|
|
|
//
|
|
// Read the dictionary file
|
|
//
|
|
|
|
IF ! Sp_Init()
|
|
RETURN -2
|
|
ENDIF
|
|
|
|
//
|
|
// See if the DBF file exists
|
|
//
|
|
|
|
IF ! "." $ cDBF
|
|
cDBF += ".dbf"
|
|
ENDIF
|
|
|
|
dbCreate( cDbf, { { "WORD", "C", 25, 0 } } )
|
|
|
|
USE ( cDbf ) ALIAS DICT NEW
|
|
|
|
IF lTalk
|
|
cSave := SaveScreen( 8, 30, 12, 48 )
|
|
hb_DispBox( 8, 30, 12, 48,, "W+/R" )
|
|
hb_DispOutAt( 8, 34, " Creating DBF ", "W+/R" )
|
|
hb_DispOutAt( 9, 31, "Percent ", "W/R" )
|
|
hb_DispOutAt( 10, 31, "Complete: ", "W/R" )
|
|
hb_DispOutAt( 11, 31, " Record: ", "W/R" )
|
|
|
|
temp := Directory( cDictionary )
|
|
nSize := temp[ 1, 2 ]
|
|
|
|
ENDIF
|
|
|
|
j := 2
|
|
FOR i := 2 TO Len( CACHE_WORDS )
|
|
IF SubStr( CACHE_WORDS, i, 1 ) == "|"
|
|
dbAppend()
|
|
DICT->word := SubStr( CACHE_WORDS, j, i - j )
|
|
j := i + 1
|
|
ENDIF
|
|
NEXT
|
|
|
|
FOR i := 1 TO 26
|
|
FOR j := 1 TO 26
|
|
temp := Chr( i + 64 ) + Chr( j + 64 )
|
|
x := Bin2L( hb_BSubStr( t_cOffsets, ( ( i - 1 ) * 156 ) + ( ( j - 1 ) * EACH_WORD + 1 ), 4 ) )
|
|
IF ! Empty( x )
|
|
y := Bin2W( hb_BSubStr( t_cOffsets, ( ( i - 1 ) * 156 ) + ( ( j - 1 ) * EACH_WORD + 5 ), 2 ) )
|
|
|
|
IF lTalk
|
|
hb_DispOutAt( 10, 43, Transform( ( x / nSize ) * 100, "999%" ), "W+/R" )
|
|
hb_DispOutAt( 11, 41, Transform( LastRec() , "99,999" ), "W+/R" )
|
|
ENDIF
|
|
|
|
cBuf := Space( y )
|
|
FSeek( t_nHandle, x, FS_SET )
|
|
FRead( t_nHandle, @cBuf, y )
|
|
FOR z := 1 TO 26
|
|
IF bit( @cBuf, z )
|
|
dbAppend()
|
|
DICT->word := temp + Chr( z + 64 )
|
|
ENDIF
|
|
NEXT
|
|
IF bit( @cBuf, 27 )
|
|
dbAppend()
|
|
DICT->word := temp
|
|
ENDIF
|
|
cBuf := SubStr( cBuf, 5 )
|
|
z := 1
|
|
DO WHILE ! Empty( cBuf )
|
|
IF SubStr( cBuf, z, 1 ) >= hb_BChar( 128 )
|
|
cWord := SubStr( cBuf, 1, z )
|
|
dbAppend()
|
|
DICT->word := temp + XUnForm( cWord )
|
|
cWord := ""
|
|
cBuf := SubStr( cBuf, z + 1 )
|
|
z := 1
|
|
|
|
IF lTalk
|
|
hb_DispOutAt( 11, 41, Transform( LastRec(), "99,999" ), "W+/R" )
|
|
ENDIF
|
|
ELSE
|
|
z++
|
|
ENDIF
|
|
ENDDO
|
|
IF ! Empty( cWord )
|
|
ENDIF
|
|
cWord := ""
|
|
ENDIF
|
|
NEXT
|
|
NEXT
|
|
|
|
IF lTalk
|
|
RestScreen( 8, 30, 12, 48, cSave )
|
|
ENDIF
|
|
|
|
SELECT DICT
|
|
USE
|
|
|
|
RETURN nStatus
|
|
|
|
// Function: Sp_Common()
|
|
// Purpose: Loads the COMMON word static array element
|
|
// Syntax: Sp_Common()
|
|
// Returns: NIL
|
|
//
|
|
// Notes: The common word list represents some of the most commonly
|
|
// used English words. They are stored in RAM to prevent a
|
|
// dictionary lookup for 70-80% of the words.
|
|
|
|
STATIC FUNCTION Sp_Common()
|
|
|
|
t_aGlobal[ 1 ] := ;
|
|
"|THE|OF|AND|TO|A|IN|THAT|FOR|IS|WAS|IT|HE|I|AS|WITH|ON|HIS|BE|AT|BY" + ;
|
|
"|NOT|THIS|HAD|HAVE|YOU|BUT|FROM|ARE|OR|WHICH|AN|THEY|WILL|ONE|WERE|ALL" + ;
|
|
"|WE|HER|SHE|WOULD|THERE|HAS|BEEN|HIM|THEIR|IF|WHEN|SO|MORE|NO|WHO|YOUR" + ;
|
|
"|OUT|MY|WHAT|UP|CAN|THEM|ABOUT|INTO|ITS|TIME|SOME|THAN|ME|OTHER|ONLY|NEW" + ;
|
|
"|COULD|SAID|ANY|THESE|MAY|TWO|THEN|DO|FIRST|NOW|MAN|SUCH|VERY|LIKE|OVER" + ;
|
|
"|OUR|EVEN|MOST|AFTER|MADE|ALSO|DID|MANY|SHOULD|BEFORE|MUST|THROUGH|YEARS" + ;
|
|
"|WHERE|MUCH|WAY|WELL|DOWN|GOOD|BECAUSE|HERE|EACH|THOSE|PEOPLE|STATE|HOW" + ;
|
|
"|TOO|LITTLE|WORLD|LAST|WORK|STILL|BACK|OWN|SEE|MEN|LONG|GET|JUST|GO|HOUSE" + ;
|
|
"|BETWEEN|BOTH|LIFE|BEING|UNDER|YEAR|NEVER|DAY|SAME|ANOTHER|KNOW|WHILE|MIGHT" + ;
|
|
"|US|GREAT|OLD|OFF|COME|AGAINST|SINCE|CAME|RIGHT|TAKE|USED|THREE|SMALL|MAKE" + ;
|
|
"|STATES|HIMSELF|FEW|DURING|WITHOUT|AGAIN|PLACE|AROUND|HOWEVER|HOME|FOUND" + ;
|
|
"|SAY|WHOSE|THOUGHT|NIGHT|DEAR|ONCE|WENT|GENERAL|HIGH|UPON|SCHOOL|EVERY|PART" + ;
|
|
"|DON'T|DOES|GOT|UNITED|LEFT|NUMBER|COURSE|WAR|GOING|UNTIL|ALWAYS|AWAY" + ;
|
|
"|SOMETHING|FACT|THOUGH|WATER|LESS|PUBLIC|PUT|THINK|ALMOST|HAND|ENOUGH|FAR" + ;
|
|
"|TOOK|YET|GOVERNMENT|SYSTEM|BETTER|EYES|SET|TOLD|NOTHING|PRESIDENT|END" + ;
|
|
"|HEAD|WHY|CALLED|DIDN'T|FIND|LOOK|ASKED|LATER|POINT|NEXT|PROGRAM|KNEW|CITY" + ;
|
|
"|BUSINESS|GIVE|GROUP|TOWARD|YOUNG|DAYS|LET|ROOM|WITHIN|CHILDREN|SIDE|SOCIAL" + ;
|
|
"|GIVEN|ORDER|PRESENT|SEVERAL|NATIONAL|USE|RATHER|SECOND|FACE|PER|POSSIBLE" + ;
|
|
"|AMONG|FORM|IMPORTANT|OFTEN|THINGS|LOOKED|EARLY|WHITE|BECOME|CASE|SEND" + ;
|
|
"|BIG|LARGE|NEED|FOUR|FELT|ALONG|GOD|SAW|BEST|CHURCH|EVER|LEAST|POWER" + ;
|
|
"|DEVELOPMENT|LIGHT|THING|FAMILY|INTEREST|SEEMED|WANT|TODAY|MEMBERS|MIND" + ;
|
|
"|COUNTRY|AREA|OTHERS|TURNED|ALTHOUGH|DONE|OPEN|SERVICE|CERTAIN|KIND|PROBLEM" + ;
|
|
"|BEGAN|DIFFERENT|DOOR|THUS|HELP|MEANS|SENSE|WHOLE|MATTER|PERHAPS|ITSELF" + ;
|
|
"|IT'S|TIMES|HUMAN|LAW|LINE|ABOVE|NAME|EXAMPLE|COMPANY|HANDS|LOCAL|SHOW" + ;
|
|
"|BODY|FIVE|HISTORY|WHETHER|GAVE|EITHER|ACROSS|ACT|ACTION|FEET|ANYTHING" + ;
|
|
"|PAST|QUITE|TAKEN|HAVING|SEEN|DEATH|EXPERIENCE|HALF|REALLY|WEEK|WORDS" + ;
|
|
"|CAR|FIELD|MONEY|WORD|ALREADY|THEMSELVES|INFORMATION|I'M|TELL|CLOSE" + ;
|
|
"|COLLEGE|PERIOD|SHALL|TOGETHER|HELD|KEEP|SURE|PROBABLY|FREE|REAL|BEHIND" + ;
|
|
"|SEEMS|CANNOT|POLITICAL|AIR|QUESTION|OFFICE|WOMAN|BROUGHT|MAJOR|MOTHER" + ;
|
|
"|SPECIAL|HEARD|PROBLEMS|AGO|BECAME|AVAILABLE|FEDERAL|MOMENT|STUDY" + ;
|
|
"|KNOWN|RESULT|STREET|BOY|ECONOMIC|CHANGE|POSITION|REASON|SOUTH|BOARD" + ;
|
|
"|INDIVIDUAL|JOB|SOCIETY|AREAS|WEST|SPACE|TURN|LOVE|COMMUNITY|TOWN|TRUE" + ;
|
|
"|COURT|FORCE|FULL|SEEM|AGE|AM|COST|WIFE|FUTURE|VOICE|WANTED|CAN'T|DEPARTMENT" + ;
|
|
"|USE|CENTER|COMMON|CONTROL|NECESSARY|POLICY|FOLLOWING|FRONTSOMETIMES|FATHER" + ;
|
|
"|GIRL|SIX|WOMEN|CLEAR|MILITARY|FURTHER|ABLE|FIGURE|LAND|FEEL|MUSIC|PARTY" + ;
|
|
"|PROVIDE|CENTURY|EDUCATION|UNIVERSITY|CHILD|EFFECT|STUDENTS|RUN|SHORT" + ;
|
|
"|STOOD|MORNING|TOTAL|OUTSIDE|ART|RATE|SAYS|YOU'RE|CLASS|TYPE|EVIDENCE" + ;
|
|
"|EXCEPT|LEAVE|MILLION|MISS|NORTH|PLAN|SOUND|THEREFORE|TOP|USUALLY|BLACK" + ;
|
|
"|HARD|SCHOOLS|STRONG|BELIEVE|PLAY|VARIOUS|SURFACE|MAKING|MEAN|SOON|VALUE" + ;
|
|
"|LINES|MODERN|NEAR|TABLE|PRIVATE|RED|ROAD|TAX|ALONE|MINUTES|PERSONAL|PROCESS" + ;
|
|
"|SITUATION|THAT'S|GONE|IDEA|INCREASE|NOR|PEACE|SECTION|LIVING|STARTED|BOOK" + ;
|
|
"|LONGER|CUT|FINALLY|NATURE|SECRETARY|MONTHS|THIRD|CALL|COMPLETE|GREATER" + ;
|
|
"|EXPECTED|FIRE|NEEDED|KEPT|VALUES|VIEW|BASIS|DARK|EVERYTHING|PRESSURE" + ;
|
|
"|GROUND|EAST|RECENT|REQUIRED|SPIRIT|UNION|HOPE|I'LL|MOVED|NATIONS|WROTE" + ;
|
|
"|CONDITIONS|RETURN|SUPPORT|ATTENTION|LATE|PARTICULAR|BROWN|COSTS|ELSE|NATION" + ;
|
|
"|BEYOND|COULDN'T|FORCES|HOURS|NUCLEAR|PERSON|TAKING|COMING|DEAD|INSIDE|LOW" + ;
|
|
"|MATERIAL|REPORT|STAGE|ADDED|AMOUNT|BASIC|DATA|FEELING|FOLLOWED|HEART|INSTEAD" + ;
|
|
"|LOOKING|LOST|MILES|PAY|SINGLE|COLD|HUNDRED|INCLUDING|INDUSTRY|MOVE|RESEARCH" + ;
|
|
"|SHOWN|BRIDGE|DEVELOPED|SIMPLY|TRIED|HOLD|REACHED|SHARE|SORT|COMMITTEE|DEFENSE" + ;
|
|
"|EQUIPMENT|ISLAND|SENDING|ACTUALLY|EARTH|BEGINNING|RELIGIOUS|RIVER|TEN|CENTRAL" + ;
|
|
"|DOING|GETTING|LETTER|RECEIVED|REST|TERMS|TRYING|CARE|FRIENDS|INDEED|MEDICAL" + ;
|
|
"|PICTURE|DIFFICULT|FINE|SIMPLE|STATUS|SUBJECT|BUILDING|ESPECIALLY|HIGHER|RANGE" + ;
|
|
"|WALL|BRING|MEETING|WALKED|CENT|FLOOR|FOREIGN|NEEDS|PAPER|PASSED|SIMILAR" + ;
|
|
"|FINAL|NATURAL|PROPERTY|TRAINING|COUNTY|GROWTH|HOT|INTERNATIONAL|LIVE" + ;
|
|
"|MARKET|POLICE|START|TALK|WASN'T|WRITTEN|BOOKS|ANSWER|CONGRESS|HEAR|PRIMARY" + ;
|
|
"|STORY|SUDDENLY|CONSIDERED|COUNTRIES|HALL|ISSUE|LIKELY|PARTICULARLY|TRULY" + ;
|
|
"|WORKING|EFFORT|SAT|ENTIRE|HAPPENED|LABOR|PURPOSE|RESULTS|CASES|DIFFERENCE" + ;
|
|
"|HAIR|PRODUCTION|STAND|I'VE|I'D|WON'T|"
|
|
|
|
RETURN NIL
|
|
|
|
FUNCTION WildCard( cPattern, cString )
|
|
|
|
LOCAL lMatch := .F.
|
|
LOCAL x := At( "*", cPattern )
|
|
LOCAL cBefore
|
|
LOCAL cAfter
|
|
LOCAL y
|
|
LOCAL nStrSize, nPatSize
|
|
|
|
cString := Upper( AllTrim( cString ) )
|
|
|
|
IF cPattern == "*"
|
|
RETURN .T.
|
|
ENDIF
|
|
//
|
|
// Do a * match
|
|
//
|
|
IF x > 0
|
|
cBefore := Upper( SubStr( cPattern, 1, x - 1 ) )
|
|
cAfter := Upper( SubStr( cPattern, x + 1 ) )
|
|
DO CASE
|
|
CASE Empty( cBefore )
|
|
lMatch := Right( cString, Len( cAfter ) ) == cAfter
|
|
CASE Empty( cAfter )
|
|
lMatch := Left( cString, Len( cBefore ) ) == cBefore
|
|
OTHERWISE
|
|
lMatch := ( Left( cString, Len( cBefore ) ) == cBefore ) .AND. ;
|
|
Right( cString, Len( cAfter ) ) == cAfter
|
|
ENDCASE
|
|
ELSE
|
|
x := At( "?", cPattern )
|
|
IF x > 0
|
|
nPatSize := Len( cPattern )
|
|
nStrSize := Len( cString )
|
|
IF nPatSize == nStrSize
|
|
lMatch := .T.
|
|
FOR y := 1 TO nPatSize
|
|
IF !( SubStr( cPattern, y, 1 ) == "?" ) .AND. ;
|
|
!( SubStr( cPattern, y, 1 ) == SubStr( cString, y, 1 ) )
|
|
lMatch := .F.
|
|
EXIT
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
ELSE
|
|
lMatch := Left( Upper( cPattern ), Len( Upper( cString ) ) ) == Upper( cString )
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN lMatch
|
|
|
|
FUNCTION AWords( cLine )
|
|
|
|
LOCAL aWords_ := {}
|
|
LOCAL nSize := Len( RTrim( cLine ) )
|
|
LOCAL x, y, z
|
|
LOCAL cWord := ""
|
|
LOCAL nOffset
|
|
|
|
z := 0
|
|
DO WHILE z <= nSize
|
|
z++
|
|
y := Asc( SubStr( cLine, z, 1 ) )
|
|
IF y >= Asc( "0" ) .AND. ! Chr( y ) $ ":;<=>?@[\^]_`{|}~" + Chr( 127 )
|
|
nOffset := z
|
|
cWord := Chr( y )
|
|
z++
|
|
y := Asc( SubStr( cLine, z, 1 ) )
|
|
WHILE ( y >= Asc( "0" ) .AND. ! Chr( y ) $ ":;<=>?@[\^]_`{|}~" + Chr( 127 ) ) .OR. y == "'"
|
|
cWord += Chr( y )
|
|
z++
|
|
IF z > nSize
|
|
EXIT
|
|
ENDIF
|
|
y := Asc( SubStr( cLine, z, 1 ) )
|
|
ENDDO
|
|
AAdd( aWords_, { cWord, nOffset } )
|
|
ENDIF
|
|
ENDDO
|
|
|
|
RETURN aWords_
|
|
|
|
// Find an occurrence of 'f_str' in 'l_str' starting from position 'f_rom'
|
|
FUNCTION fat( f_str, l_str, f_rom )
|
|
RETURN At( f_str, SubStr( l_str, iif( PCount() < 3, 1, f_rom ) ) )
|
|
|
|
STATIC FUNCTION bfat( f_str, l_str, f_rom )
|
|
RETURN hb_BAt( f_str, hb_BSubStr( l_str, f_rom ) )
|