home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 16 / 16.iso / w / w048 / 2.ddi / MSSRC.ARC / MSSPELL.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1987-12-21  |  38.6 KB  |  1,249 lines

  1. {                            MSSPELL.PAS
  2.                                MS 4.0
  3.                 Copyright (c) 1985, 87 by Borland International, Inc.         }
  4.  
  5. {$I msdirect.inc}
  6.  
  7. unit MsSpell;
  8.   {-Spelling checker for MicroStar - uses Turbo Lightning}
  9.  
  10. interface
  11.  
  12. uses
  13.   Crt,                       {Basic video operations - standard unit}
  14.   Dos,                       {DOS interface - standard unit}
  15.   Errors,                    {Runtime error handler}
  16.   MsVars,                    {Global types and declarations}
  17.   MsScrn1,                   {Fast screen writing routines}
  18.   MsString,                  {String primitives}
  19.   MsPtrOp,                   {Pointer primitives}
  20.   EscSeq,                    {Returns text string for extended scan codes}
  21.   MsCmds,                    {Maps keystrokes to commands}
  22.   Int24,                     {DOS critical error handler}
  23.   Message,                   {Message system}
  24.   MsUser,                    {User keyboard input, line edit, error report, help}
  25.   MsMemOp,                   {Text buffer allocation and deallocation}
  26.   MsBack,                    {Background processes}
  27.   MsScrn2,                   {Editor screen updating}
  28.   MsMenu,                    {Pulldown and custom menu system}
  29.   MsDir,                     {Popup directory picker}
  30.   MsEdit,                    {Basic editing commands}
  31.   MsFile;                    {File I/O routines}
  32.  
  33. procedure EdSpellingCheck;
  34.   {-Check spelling of block or file using Turbo Lightning}
  35.  
  36.   {==========================================================================}
  37.  
  38. implementation
  39.  
  40. const
  41.   LightDictFileExt : ExtString = 'DIC';
  42.   MarkStr : string[1] = '~'; {Marks unrecognized words during spelling check}
  43.   LastHashBucket = 211;      {PRIME number of entries in hash table}
  44.   {Must match that in MSSPELL.ASM}
  45.   LightChars : string[28] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'#39;
  46.  
  47. type
  48.  
  49.   {Interface to Turbo Lightning}
  50.   DITtype =
  51.   record
  52.     Block : Word;            {Dictionary block number}
  53.     WordNum : Word;          {Intrablock word number}
  54.     GlobalNum : array[0..2] of Byte; {Global Word number}
  55.     MinGlobal : array[0..2] of Byte; {Min word number for range}
  56.     MaxGlobal : array[0..2] of Byte; {Max word number for range}
  57.     TokenLen : Word;         {token length (word length)}
  58.     RtokenLen : Word;        {returned word token length}
  59.     DisplayCount : Word;     {Display count}
  60.     FscPointer : Word;       {offset of synonym area}
  61.     MastDicOfs : Word;       {offset of master dic index}
  62.     OutDicOfs : Word;        {offset of outboard dic index}
  63.     UtokenOfs : Word;        {offset of untranslated token}
  64.     HotKeyOfs : Word;        {offset of hot key area}
  65.     EnvOfs : Word;           {offset of environment area}
  66.     RamFileOfs : Word;       {offset of Ram Dictionary File name}
  67.     MastFileOfs : Word;      {offset of Disk Dictionary File name}
  68.     AuxFileOfs : Word;       {offset of Aux Dictionary File name}
  69.     SynFileOfs : Word;       {offset of Synonym Dictionary File name}
  70.     MinMaxOfs : Word;        {offset of Max, Min token len bytes}
  71.     DictModeOfs : Word;      {offset of Dictionary mode word}
  72.     LikelyWordOfs : Word;    {offset of likely word list}
  73.     InsOvwOfs : Word;        {offset of Insert/Overwrite byte}
  74.     CurEnvOfs : Word;        {offset of Current Environment pointer}
  75.     RAMDictOfs : Word;       {offset of RAM dictionary Index}
  76.     AllIndexSeg : Word;      {segment of all indices}
  77.   end;
  78.   DITPtrType = ^DITtype;
  79.   HotKeyType = array[1..7] of Word;
  80.   HotKeyPtrType = ^HotKeyType;
  81.  
  82.   {Corrective actions available when unrecognized word is found}
  83.   SplActionType =
  84.   (SplIgnore,                {Ignore}
  85.    SplAddLocal,              {Add to local dictionary in RAM}
  86.    SplChkLikely,             {Check likely words via Lightning}
  87.    SplEdit,                  {Edit the word on the fly}
  88.    SplMark,                  {Mark in text}
  89.    SplAddauxi,               {Add to auxi dictionary}
  90.    SplMarkRest,              {Mark all in text without prompt}
  91.    SplQuit                   {Quit checking}
  92.    );
  93.  
  94.   {Support for RAM dictionary local to MicroStar}
  95.   RamDictPtr = ^RamDictRec;
  96.   WordPtr = ^VarString;
  97.   RamDictRec =
  98.   record
  99.     Next : RamDictPtr;       {Points to next hash table entry}
  100.     Word : WordPtr;          {Points to string}
  101.   end;
  102.   RamDictTable = array[0..LastHashBucket] of RamDictPtr;
  103.  
  104. var
  105.   DITPtr : DITPtrType;       {Points to Lightning Data Interchange Table}
  106.   HotKeyPtr : HotKeyPtrType; {Points to Lightning hot key table}
  107.   SaveHotKeys : HotKeyType;  {Saves Lightning interactive hot keys}
  108.   SaveAutoProof : Boolean;   {Saves Lightning interactive autoproof state}
  109.   LastLikelyWordCount : Integer; {Number of soundalike words returned by Lightning}
  110.   BadWord : VarString;       {Most recent unknown word}
  111.  
  112.   {$L MSSPELL}
  113.  
  114.   function EdRamHash(var S) : Word; external;
  115.   {-Given a string, return a Word between 0 and LastHashBucket}
  116.  
  117.   function EdInitLocalPtr : Pointer; external;
  118.   {-Return a pointer to the start of the local dictionary}
  119.  
  120.   function EdEngine(Fcode, AlValue, CxValue, DxValue : Word; var AnyString) : Word;
  121.     {-Call the Lightning engine}
  122.   var
  123.     regs : registers;
  124.  
  125.   begin                      {EdEngine}
  126.     with regs do begin
  127.       Al := lo(AlValue);
  128.       Ah := $ED;
  129.       Bh := $ED;
  130.       Bl := lo(Fcode);
  131.       Cx := CxValue;
  132.       Dx := DxValue;
  133.       Ds := Seg(AnyString);
  134.       Si := Ofs(AnyString);
  135.       intr($16, regs);
  136.       EdEngine := ax;
  137.     end;
  138.   end;                       {EdEngine}
  139.  
  140.   function EdLightningPresent : Boolean;
  141.     {-Find out if Lightning is in memory}
  142.   var
  143.     Junk : VarString;
  144.  
  145.   begin                      {EdLightningPresent}
  146.     EdLightningPresent := (EdEngine(0, 0, 0, 0, Junk) = $5205);
  147.   end;                       {EdLightningPresent}
  148.  
  149.   function EdLoadAuxDict(AuxName : Filepath) : Boolean;
  150.     {-Load Lightning's auxiliary dictionary}
  151.  
  152.   begin                      {EdLoadAuxDict}
  153.     AuxName[Succ(Length(AuxName))] := Null;
  154.     EdLoadAuxDict := (EdEngine(4, 0, 0, 0, AuxName) <= 2);
  155.   end;                       {EdLoadAuxDict}
  156.  
  157.   procedure EdReserveLightning(Reserve : Boolean; var AuxName : Filepath);
  158.     {-Prepare or release Lightning for this application}
  159.   var
  160.     AuxAvailable, Junk : Boolean;
  161.  
  162.     function EdAutoProof(On : Boolean) : Boolean;
  163.       {-Change the AutoProof mode and return the previous state}
  164.     var
  165.       NewState : Word;
  166.       Junk : VarString;
  167.  
  168.     begin                    {EdAutoProof}
  169.       if On then
  170.         NewState := $FF
  171.       else
  172.         NewState := 0;
  173.       EdAutoProof := (EdEngine(6, NewState, 0, 0, Junk) <> 0);
  174.     end;                     {EdAutoProof}
  175.  
  176.     function EdDITAddress : DITPtrType;
  177.       {-Return a pointer to the Lightning internal data structure}
  178.     var
  179.       Junk : VarString;
  180.  
  181.     begin                    {EdDITAddress}
  182.       EdDITAddress := Ptr(EdEngine(2, 0, 0, 0, Junk), EdEngine(3, 0, 0, 0, Junk));
  183.     end;                     {EdDITAddress}
  184.  
  185.     function EdGetAuxDictName : Filepath;
  186.       {-Return the auxiliary dictionary file name from Lightning}
  187.     type
  188.       ASCIIZ = array[0..255] of Char;
  189.     var
  190.       AuxNamePtr : ^ASCIIZ;
  191.       AuxName : Filepath;
  192.       AuxLen : Byte absolute AuxName;
  193.  
  194.     begin                    {EdGetAuxDictName}
  195.       {Point into the Lightning data interchange table}
  196.       AuxNamePtr := Ptr(Seg(DITPtr^), DITPtr^.AuxFileOfs);
  197.       {Convert ASCIIZ string to Turbo string}
  198.       AuxLen := 0;
  199.       while AuxNamePtr^[AuxLen] <> Null do
  200.         Inc(AuxLen);
  201.       Move(AuxNamePtr^, AuxName[1], AuxLen);
  202.       {Add default extension}
  203.       EdDefaultExtension(LightDictFileExt, AuxName);
  204.       EdGetAuxDictName := AuxName;
  205.     end;                     {EdGetAuxDictName}
  206.  
  207.   begin                      {EdReserveLightning}
  208.  
  209.     if Reserve then begin
  210.  
  211.       {Set up pointers to Lightning data structures}
  212.       DITPtr := EdDITAddress;
  213.       HotKeyPtr := Ptr(Seg(DITPtr^), DITPtr^.HotKeyOfs);
  214.  
  215.       {Save the current hot keys}
  216.       SaveHotKeys := HotKeyPtr^;
  217.  
  218.       {Get the filename of the auxiliary dictionary}
  219.       AuxName := EdGetAuxDictName;
  220.  
  221.       {Have Lightning load the auxiliary dictionary if available}
  222.       AuxAvailable := EdLoadAuxDict(AuxName);
  223.  
  224.       {Disable the Lightning hot keys}
  225.       FillChar(HotKeyPtr^, SizeOf(HotKeyType), $FF);
  226.  
  227.       {Turn auto proof mode off}
  228.       SaveAutoProof := EdAutoProof(False);
  229.  
  230.     end else begin
  231.  
  232.       {Release Lightning for interactive use}
  233.       Junk := EdAutoProof(SaveAutoProof);
  234.       HotKeyPtr^ := SaveHotKeys;
  235.  
  236.     end;
  237.   end;                       {EdReserveLightning}
  238.  
  239.   procedure EdInitEndPoints(var T : BlockMarker);
  240.     {-Set window and end marker for the region to be spell checked}
  241.   var
  242.     W : Pwindesc;
  243.     Rezoom : Boolean;
  244.  
  245.   begin                      {EdInitEndPoints}
  246.     if EdNoBlock then begin
  247.       {No marked block, check from cursor to end of document}
  248.       with T do begin
  249.         EdSetPtrNil(Line);
  250.         Col := 0;
  251.       end;
  252.  
  253.     end else begin
  254.       {Block marked and visible, check just the block}
  255.       T := Blockto;
  256.       W := EdFindWindow(Blockfrom.Line);
  257.       Rezoom := Zoomed and (W <> Curwin);
  258.       if Rezoom then
  259.         EdZoomWindow(False);
  260.       Curwin := W;
  261.       if Rezoom then
  262.         EdZoomWindow(False);
  263.       EdJumpMarker(Blockfrom);
  264.  
  265.     end;
  266.  
  267.     {Align cursor to start on a Lightning word boundary}
  268.     with Curwin^ do begin
  269.       {Back up beyond start of current word}
  270.       while (Colno >= 1) and (Pos(Upcase(Curline^.Txt^[Colno]), LightChars) <> 0) do
  271.         Dec(Colno);
  272.       Inc(Colno);
  273.     end;
  274.   end;                       {EdInitEndPoints}
  275.  
  276.   function EdAddWord(var RamDict : RamDictTable; P : WordPtr) : Boolean;
  277.     {-Add a word to the RAM dictionary, returning True if successful}
  278.   var
  279.     H : Word;
  280.     Old : RamDictPtr;
  281.  
  282.   begin                      {EdAddWord}
  283.     {Assure sufficient memory}
  284.     if EdMemAvail(SizeOf(RamDictRec), FreeListPerm) then begin
  285.       {Compute hash}
  286.       H := EdRamHash(P^);
  287.  
  288.       {Add to hash table}
  289.       Old := RamDict[H];
  290.       GetMem(RamDict[H], SizeOf(RamDictRec));
  291.       with RamDict[H]^ do begin
  292.         Next := Old;
  293.         Word := P;
  294.       end;
  295.       EdAddWord := True;
  296.     end else
  297.       EdAddWord := False;
  298.   end;                       {EdAddWord}
  299.  
  300.   procedure EdSetWindowPos(var Xmin, Ymin : Integer; Width, Height : Integer);
  301.     {-Return upper left corner of window such that window doesn't overwrite current position}
  302.  
  303.   begin                      {EdSetWindowPos}
  304.     with Curwin^ do begin
  305.       if Colno-Leftedge+Leftcol-(Length(BadWord) shr 1) <= (PhyScrCols shr 1) then
  306.         {Left half of screen, put menu on right}
  307.         Xmin := Succ(PhyScrCols-Width)
  308.       else
  309.         {Right half of screen}
  310.         Xmin := 1;
  311.  
  312.       if Pred(Firsttextno+Lineno) <= (PhyscrRows shr 1) then
  313.         {Top half of screen, put menu on bottom}
  314.         Ymin := Succ(PhyscrRows-Height)
  315.       else
  316.         {Bottom half of screen}
  317.         Ymin := LogtopScr;
  318.     end;
  319.   end;                       {EdSetWindowPos}
  320.  
  321.   function EdPickLikelyWord(ScanDict : Boolean) : VarString;
  322.     {-Display a menu and return a likely word}
  323.   const
  324.     WordsPerPage = 12;
  325.   var
  326.     Wid, Cnt, Num, Row, Lines, Xmin, Ymin : Integer;
  327.     Ch : Char;
  328.     Quitting : Boolean;
  329.     W : WindowRec;
  330.     Title : VarString;
  331.  
  332.     function EdLikelyWordCount : Word;
  333.       {-Return the number of likely substitutes for the last word checked}
  334.     var
  335.       Junk : VarString;
  336.  
  337.     begin                    {EdLikelyWordCount}
  338.       EdLikelyWordCount := EdEngine($F, 0, 0, 0, Junk);
  339.     end;                     {EdLikelyWordCount}
  340.  
  341.     function EdGetLikelyWord(N : Word) : VarString;
  342.       {-Return the n'th likely word}
  343.     var
  344.       S, O, I : Word;
  345.       P : ^VarString;
  346.  
  347.     begin                    {EdGetLikelyWord}
  348.       S := Seg(DITPtr^);
  349.       O := DITPtr^.LikelyWordOfs;
  350.       for I := 1 to N do begin
  351.         P := Ptr(S, O);
  352.         O := O+Succ(Ord(P^[0]));
  353.       end;
  354.       EdGetLikelyWord := P^;
  355.     end;                     {EdGetLikelyWord}
  356.  
  357.     function EdMaxLikelyWordWidth(Cnt : Integer) : Integer;
  358.       {-Return the maximum word length}
  359.     var
  360.       Max, I : Integer;
  361.       W : VarString;
  362.  
  363.     begin                    {EdMaxLikelyWordWidth}
  364.       Max := 0;
  365.       for I := 1 to Cnt do begin
  366.         W := EdGetLikelyWord(I);
  367.         if Length(W) > Max then
  368.           Max := Length(W);
  369.       end;
  370.       EdMaxLikelyWordWidth := Max;
  371.     end;                     {EdMaxLikelyWordWidth}
  372.  
  373.     procedure EdWriteEntry(Num : Integer; Row, Attr : Byte);
  374.       {-Write one word to the screen}
  375.  
  376.     begin                    {EdWriteEntry}
  377.       with W do
  378.         EdFastWrite(EdPadEntry(EdGetLikelyWord(Num), XSize-2), YPosn+Row, Succ(XPosn), Attr);
  379.     end;                     {EdWriteEntry}
  380.  
  381.     procedure EdDrawFullPage(Num, Lines : Integer);
  382.       {-Draw one full window full of entries, starting at entry num}
  383.     var
  384.       I : Integer;
  385.  
  386.     begin                    {EdDrawFullPage}
  387.       for I := 1 to Lines do
  388.         EdWriteEntry(Pred(Num+I), I, ScreenAttr[MnColor]);
  389.     end;                     {EdDrawFullPage}
  390.  
  391.   begin                      {EdPickLikelyWord}
  392.  
  393.     if ScanDict then begin
  394.       EdWritePromptLine(EdGetMessage(281));
  395.       Cnt := EdLikelyWordCount;
  396.       LastLikelyWordCount := Cnt;
  397.     end else
  398.       Cnt := LastLikelyWordCount;
  399.  
  400.     if Cnt <= 0 then begin
  401.       EdDisplayPromptWindow(
  402.                             EdGetMessage(280)+'-'+EdGetMessage(305), 13, [#27], Ch, NormalBox);
  403.       EdPickLikelyWord := '';
  404.       Exit;
  405.     end;
  406.  
  407.     EdWritePromptLine(EdGetMessage(304));
  408.  
  409.     {Get max width of likely candidates}
  410.     Wid := EdMaxLikelyWordWidth(Cnt);
  411.     if Wid < 16 then
  412.       Wid := 16;
  413.  
  414.     if Cnt > WordsPerPage then
  415.       Lines := WordsPerPage
  416.     else
  417.       Lines := Cnt;
  418.  
  419.     {See where window should go to avoid overwriting word}
  420.     EdSetWindowPos(Xmin, Ymin, Wid+4, Lines+2);
  421.  
  422.     {Put up a window}
  423.     EdSaveTextWindow(Border, EdGetMessage(279), Xmin, Ymin, Xmin+Wid+3, Succ(Ymin+Lines), W);
  424.  
  425.     if Cnt > WordsPerPage then
  426.       {Indicate that there are more entries}
  427.       with W do begin
  428.         Title := EdGetMessage(263);
  429.         EdFastWrite(Title, Pred(YPosn+YSize), XPosn+XSize-14, ScreenAttr[MfColor]);
  430.       end;
  431.  
  432.     Num := 1;
  433.     Row := 1;
  434.     EdDrawFullPage(Num, Lines);
  435.     Quitting := False;
  436.  
  437.     repeat
  438.  
  439.       EdWriteEntry(Num, Row, ScreenAttr[MsColor]);
  440.       if Cnt > WordsPerPage then
  441.         {Indicate that other entries are off ends of menus}
  442.         with W do begin
  443.           if Num > Row then
  444.             {More words are above top of window}
  445.             Ch := ^X
  446.           else
  447.             Ch := Blank;
  448.           EdFastWrite(Ch, Pred(YPosn+YSize), XPosn+XSize-13, ScreenAttr[MfColor]);
  449.           if Num < Cnt+Row-Lines then
  450.             {More words are below bottom of window}
  451.             Ch := ^Y
  452.           else
  453.             Ch := Blank;
  454.           EdFastWrite(Ch, Pred(YPosn+YSize), XPosn+XSize-12, ScreenAttr[MfColor]);
  455.         end;
  456.  
  457.       case EdGetCursorCommand(DirCmdSet) of
  458.  
  459.         ^M :                 {Select}
  460.           Quitting := True;
  461.  
  462.         ^[ :                 {Escape}
  463.           begin
  464.             Num := 0;
  465.             Quitting := True;
  466.           end;
  467.  
  468.         ^E :                 {Scroll up}
  469.           if Num > 1 then begin
  470.             EdWriteEntry(Num, Row, ScreenAttr[MnColor]);
  471.             Dec(Num);
  472.             if Row = 1 then begin
  473.               GoToXY(1, 1);
  474.               InsLine;
  475.             end else
  476.               Dec(Row);
  477.           end else if Lines >= Cnt then begin
  478.             {Wrap to end}
  479.             Num := Cnt;
  480.             Row := Cnt;
  481.             EdDrawFullPage(Succ(Num-Row), Lines);
  482.           end;
  483.  
  484.         ^X :                 {Scroll down}
  485.           if Num < Cnt then begin
  486.             EdWriteEntry(Num, Row, ScreenAttr[MnColor]);
  487.             Inc(Num);
  488.             if Row >= Lines then begin
  489.               GoToXY(1, 1);
  490.               DelLine;
  491.               Row := Lines;
  492.             end else
  493.               Inc(Row);
  494.           end else if Lines >= Cnt then begin
  495.             {Wrap to begin}
  496.             Num := 1;
  497.             Row := 1;
  498.             EdDrawFullPage(Num, Lines);
  499.           end;
  500.  
  501.         ^R :                 {Page up}
  502.           if Num > 1 then begin
  503.             Num := Num-Lines;
  504.             if Num < 1 then
  505.               Num := 1;
  506.             Row := 1;
  507.             EdDrawFullPage(Num, Lines);
  508.           end;
  509.  
  510.         ^C :                 {Page down}
  511.           if Num < Cnt then begin
  512.             Num := Num+Lines;
  513.             if Num > Cnt then
  514.               Num := Cnt;
  515.             Row := Lines;
  516.             if Row > Cnt then
  517.               Row := Cnt;
  518.             EdDrawFullPage(Succ(Num-Row), Lines);
  519.           end;
  520.  
  521.         ^T :                 {Top of list}
  522.           if Num > 1 then begin
  523.             Num := 1;
  524.             Row := 1;
  525.             EdDrawFullPage(Num, Lines);
  526.           end;
  527.  
  528.         ^B :                 {Bottom of list}
  529.           if Num < Cnt then begin
  530.             Num := Cnt;
  531.             if Cnt < Lines then
  532.               Row := Cnt
  533.             else
  534.               Row := Lines;
  535.             EdDrawFullPage(Succ(Num-Row), Lines);
  536.           end;
  537.  
  538.       end;
  539.     until Abortcmd or Quitting;
  540.  
  541.     {Return the new word, empty string if none}
  542.     if Abortcmd or (Num = 0) then
  543.       EdPickLikelyWord := ''
  544.     else
  545.       EdPickLikelyWord := EdGetLikelyWord(Num);
  546.  
  547.     {Restore screen}
  548.     EdRestoreTextWindow(W);
  549.     EdWritePromptLine(EdGetMessage(278));
  550.     EdSetCursor(CursorOff);
  551.  
  552.   end;                       {EdPickLikelyWord}
  553.  
  554.   function EdGetFixAction : SplActionType;
  555.     {-Prompt for a fix type}
  556.   const
  557.     MaxChoices = 7;
  558.   var
  559.     Menu : CustomMenuRec;
  560.     CalcXmin, CalcYmin, Maxlen, Choice : Integer;
  561.  
  562.     function EdBuildMessages(var Menu : CustomMenuRec; var Maxlen : Integer) : Boolean;
  563.       {-Build the message table for the menu}
  564.     var
  565.       Item : Integer;
  566.       S : VarString;
  567.  
  568.     begin                    {EdBuildMessages}
  569.       EdBuildMessages := False;
  570.       with Menu do begin
  571.  
  572.         if EdMemAvail(Succ(MaxChoice) shl 2, FreeListTemp) then
  573.           {Get the pointers}
  574.           GetMem(Messages, Succ(MaxChoice) shl 2)
  575.         else
  576.           Exit;
  577.  
  578.         Maxlen := 0;
  579.  
  580.         {Get the string space and fill in the strings}
  581.         for Item := MinChoice to MaxChoice do begin
  582.           S := EdGetMessage(263+Item);
  583.           if Length(S) > Maxlen then
  584.             Maxlen := Length(S);
  585.           if EdMemAvail(Succ(Length(S)), FreeListTemp) then
  586.             GetMem(Messages^[Item], Succ(Length(S)))
  587.           else
  588.             Exit;
  589.           Messages^[Item]^ := S;
  590.         end;
  591.       end;
  592.       EdBuildMessages := True;
  593.     end;                     {EdBuildMessages}
  594.  
  595.   begin                      {EdGetFixAction}
  596.  
  597.     {Initialize the menu}
  598.     with Menu do begin
  599.       MessageNum := 257;
  600.       PromptNum := 277;
  601.       MinChoice := 1;
  602.       MaxChoice := MaxChoices;
  603.       InitChoice := 1;
  604.       CmdSet := PrtCmdSet;
  605.       UseLetters := True;
  606.     end;
  607.     if not(EdBuildMessages(Menu, Maxlen)) then begin
  608.       EdErrormsg(35);
  609.       Exit;
  610.     end;
  611.  
  612.     {See where window should go to avoid overwriting word}
  613.     EdSetWindowPos(CalcXmin, CalcYmin, Maxlen+4, MaxChoices+2);
  614.     with Menu do begin
  615.       Xmin := CalcXmin;
  616.       Ymin := CalcYmin;
  617.     end;
  618.  
  619.     {Get the menu choice}
  620.     EdGetCustomMenuChoice(Menu, Choice);
  621.  
  622.     if not(Abortcmd) then
  623.       EdGetFixAction := SplActionType(Pred(Choice));
  624.  
  625.     EdWritePromptLine(EdGetMessage(278));
  626.  
  627.   end;                       {EdGetFixAction}
  628.  
  629.   procedure EdAddAuxiDict(W : VarString; AuxName : Filepath);
  630.     {-Add a word to the auxiliary dictionary}
  631.   label
  632.     ExitPoint;
  633.   const
  634.     MaxChoices = 4;
  635.   var
  636.     AuxFile : Text;
  637.     Choice, CalcXmin, CalcYmin : Integer;
  638.     Menu : CustomMenuRec;
  639.  
  640.     function EdBuildMessages(var Menu : CustomMenuRec) : Boolean;
  641.       {-Build the message table for the menu}
  642.     var
  643.       Item : Integer;
  644.       S : VarString;
  645.  
  646.     begin                    {EdBuildMessages}
  647.       EdBuildMessages := False;
  648.       with Menu do begin
  649.  
  650.         if EdMemAvail(Succ(MaxChoice) shl 2, FreeListTemp) then
  651.           {Get the pointers}
  652.           GetMem(Messages, Succ(MaxChoice) shl 2)
  653.         else
  654.           Exit;
  655.  
  656.         {Get the string space and fill in the strings}
  657.         for Item := MinChoice to MaxChoice do begin
  658.           S := EdGetMessage(258+Item);
  659.           if EdMemAvail(Succ(Length(S)), FreeListTemp) then
  660.             GetMem(Messages^[Item], Succ(Length(S)))
  661.           else
  662.             Exit;
  663.           Messages^[Item]^ := S;
  664.         end;
  665.       end;
  666.       EdBuildMessages := True;
  667.     end;                     {EdBuildMessages}
  668.  
  669.   begin                      {EdAddAuxiDict}
  670.  
  671.     {See where window should go to avoid overwriting word}
  672.     EdSetWindowPos(CalcXmin, CalcYmin, 30, MaxChoices+2);
  673.  
  674.     {Initialize the menu}
  675.     with Menu do begin
  676.       Xmin := CalcXmin;
  677.       Ymin := CalcYmin;
  678.       MessageNum := 271;
  679.       PromptNum := 276;
  680.       MinChoice := 1;
  681.       MaxChoice := MaxChoices;
  682.       InitChoice := 1;
  683.       CmdSet := PrtCmdSet;
  684.       UseLetters := True;
  685.     end;
  686.     if not(EdBuildMessages(Menu)) then begin
  687.       EdErrormsg(35);
  688.       goto ExitPoint;
  689.     end;
  690.  
  691.     {Get the menu choice}
  692.     EdGetCustomMenuChoice(Menu, Choice);
  693.  
  694.     if Abortcmd then
  695.       goto ExitPoint;
  696.  
  697.     {Modify word based on choice}
  698.     case Choice of
  699.       1 :                    {Add in lower case}
  700.         EdLocase(W);
  701.  
  702.       2 :                    {First capitalized, rest lowercase}
  703.         begin
  704.           EdLocase(W);
  705.           W[1] := Upcase(W[1]);
  706.         end;
  707.  
  708.       3 :                    {All uppercase}
  709.         EdUpcase(W);
  710.     end;
  711.  
  712.     {Add to dictionary file}
  713.     if EdExistFile(AuxName) then begin
  714.       Assign(AuxFile, AuxName);
  715.       Append(AuxFile);
  716.       if EdFileerror then begin
  717.         {Force return to main spelling menu}
  718.         Abortcmd := True;
  719.         goto ExitPoint;
  720.       end;
  721.     end else begin
  722.       EdErrormsg(58);
  723.       {Force return to main spelling menu}
  724.       Abortcmd := True;
  725.       goto ExitPoint;
  726.     end;
  727.  
  728.     WriteLn(AuxFile, W);
  729.     Write(AuxFile, ^Z);
  730.     if EdFileerror then begin
  731.       {Force return to main spelling menu}
  732.       Abortcmd := True;
  733.       goto ExitPoint;
  734.     end;
  735.     Close(AuxFile);
  736.     if EdFileerror then begin
  737.       {Force return to main spelling menu}
  738.       Abortcmd := True;
  739.       goto ExitPoint;
  740.     end;
  741.  
  742.     {Reload Auxi dictionary}
  743.     if not(EdLoadAuxDict(AuxName)) then begin
  744.       {Unable to reload it}
  745.       EdErrormsg(74);
  746.       Abortcmd := True;
  747.     end;
  748.  
  749. ExitPoint:
  750.     EdWritePromptLine(EdGetMessage(278));
  751.  
  752.   end;                       {EdAddAuxiDict}
  753.  
  754.   procedure EdSpellingCheck;
  755.     {-Check spelling of block or file using Turbo Lightning}
  756.   var
  757.     Marking, Junk, SaveCursor : Boolean;
  758.     AuxName : Filepath;
  759.     RamDict : RamDictTable;
  760.     Action : SplActionType;
  761.     CheckTo : BlockMarker;
  762.  
  763.     procedure EdCheckWords(CheckTo : BlockMarker);
  764.       {-Scan the text stream and check words}
  765.     const
  766.       LastCh : Char = ' ';   {Previous character in line}
  767.     var
  768.       Done : Boolean;
  769.       W, NewWord : VarString;
  770.       Ch : Char;
  771.       Wptr : WordPtr;
  772.  
  773.       procedure EdGetNextWord(var Done : Boolean; var W : VarString);
  774.         {-Return the next word in the text, set Done when no more to go}
  775.       var
  776.         StartCol, Len, WlenInt : Integer;
  777.         GotStart : Boolean;
  778.         Ch : Char;
  779.  
  780.       begin                  {EdGetNextWord}
  781.         with Curwin^, CheckTo do begin
  782.  
  783.           Len := Curline^.Bufflen;
  784.  
  785.           repeat
  786.  
  787.             {Done when past the CheckTo marker}
  788.             Done := (Curline = Line) and (Colno >= Col);
  789.  
  790.             {Get next line if previous one exhausted}
  791.             while not(Done) and (Colno > Len) do begin
  792.               {Move to next line}
  793.               if EdPtrIsNil(Curline^.Fwdlink) then
  794.                 Done := True
  795.               else begin
  796.                 EdFwdPtr(Curline);
  797.                 if Lineno > (Lastlineno-Firsttextno) shr 1 then
  798.                   {Keep current line roughly centered in window}
  799.                   EdFwdPtr(TopLine)
  800.                 else
  801.                   Inc(Lineno);
  802.                 Colno := 1;
  803.                 Inc(Clineno);
  804.                 {Approximation of crelpos for display here}
  805.                 Crelpos := (LongInt(1000)*LongInt(Clineno)) div LongInt(TlineNo);
  806.                 {Have we passed the end point?}
  807.                 Done := (Curline = Line) and (Colno >= Col);
  808.               end;
  809.               if not(Done) then begin
  810.                 Len := Curline^.Bufflen;
  811.                 LastCh := Blank;
  812.               end;
  813.             end;
  814.  
  815.             GotStart := False;
  816.             if not(Done) then
  817.               with Curline^ do
  818.                 {Scan to next word starting point}
  819.                 while (Colno <= Len) and not(GotStart) do begin
  820.                   Ch := Upcase(Txt^[Colno]);
  821.                   if (Pos(LastCh, LightChars) = 0) then
  822.                     GotStart := (Pos(Ch, LightChars) <> 0);
  823.                   LastCh := Ch;
  824.                   Inc(Colno);
  825.                 end;
  826.  
  827.           until Done or GotStart;
  828.  
  829.           if GotStart then
  830.             with Curline^ do begin
  831.               {Store start position}
  832.               StartCol := Pred(Colno);
  833.               {Find end of word}
  834.               Ch := Upcase(Txt^[Colno]);
  835.               while (Colno <= Len) and
  836.               ((Pos(Ch, LightChars) <> 0) or ((Ch = '-') and (Colno < Len) and not(Txt^[Succ(Colno)] = '-')))
  837.               do begin
  838.                 Inc(Colno);
  839.                 Ch := Upcase(Txt^[Colno]);
  840.               end;
  841.               {Store string}
  842.               if Colno > StartCol then begin
  843.                 WlenInt := Colno-StartCol;
  844.                 if WlenInt > 32 then
  845.                   WlenInt := 32;
  846.                 W[0] := Chr(WlenInt);
  847.                 Move(Txt^[StartCol], W[1], Length(W));
  848.               end;
  849.             end;
  850.         end;
  851.  
  852.       end;                   {EdGetNextWord}
  853.  
  854.       function EdInLocalDict(var RamDict : RamDictTable; W : VarString) : Boolean;
  855.         {-Return true if word is in local RAM dictionary}
  856.       var
  857.         P : RamDictPtr;
  858.  
  859.       begin                  {EdInLocalDict}
  860.  
  861.         {Map the word into lower case as used by the dictionary}
  862.         EdLocase(W);
  863.  
  864.         {Compute start of hash list}
  865.         P := RamDict[EdRamHash(W)];
  866.  
  867.         while EdPtrNotNil(P) do
  868.           if P^.Word^ = W then begin
  869.             {Found it, get out now}
  870.             EdInLocalDict := True;
  871.             Exit;
  872.           end else
  873.             P := P^.Next;
  874.  
  875.         EdInLocalDict := False;
  876.       end;                   {EdInLocalDict}
  877.  
  878.       function EdWordInDict(var RamDict : RamDictTable; var Word : VarString) : Boolean;
  879.         {-See if word is in dictionary}
  880.  
  881.       begin                  {EdWordInDict}
  882.         if Length(Word) = 1 then
  883.           {All single characters are legal words}
  884.           EdWordInDict := True
  885.         else if EdInLocalDict(RamDict, Word) then
  886.           {in local RAM dictionary}
  887.           EdWordInDict := True
  888.         else if EdEngine($E, 0, 0, 0, Word) <> 1 then
  889.           {in Lightning RAM or Auxi dictionary}
  890.           EdWordInDict := True
  891.         else
  892.           {check Lightning disk dictionary}
  893.           EdWordInDict := (EdEngine(1, 0, 0, 0, Word) = 0);
  894.       end;                   {EdWordInDict}
  895.  
  896.       function EdInsertRoom(P : PlineDesc; Start, Num : Integer) : Boolean;
  897.         {-Insert num spaces at position start of line p, returning True if successful}
  898.       var
  899.         Len, NewLen : Integer;
  900.  
  901.       begin                  {EdInsertRoom}
  902.  
  903.         Len := EdTextLength(P);
  904.         if Start > Len then
  905.           NewLen := Succ(Start+Num)
  906.         else
  907.           NewLen := Succ(Len+Num);
  908.  
  909.         {Size up the line}
  910.         if not EdSizeline(P, NewLen, True) then begin
  911.           EdInsertRoom := False;
  912.           Exit;
  913.         end;
  914.  
  915.         {Move the text over}
  916.         with P^ do
  917.           if Start <= Len then
  918.             Move(Txt^[Start], Txt^[Start+Num], Succ(Len-Start));
  919.  
  920.         {Fix up markers}
  921.         EdFixMarkInsertedSpace(P, Start, Num);
  922.         EdFixBlockInsertedSpace(P, Start, Num);
  923.  
  924.         EdInsertRoom := True;
  925.  
  926.       end;                   {EdInsertRoom}
  927.  
  928.       procedure EdInsertFix(Wlen : Integer; NewWord : VarString);
  929.         {-Delete old word, insert new one}
  930.       var
  931.         Start : Integer;
  932.  
  933.       begin                  {EdInsertFix}
  934.         with Curwin^ do begin
  935.           Start := Colno-Wlen;
  936.           {Remove the existing word}
  937.           while Colno > Start do
  938.             EdDeleteLeftChar;
  939.           {Insert space in line for the fix}
  940.           if EdInsertRoom(Curline, Start, Length(NewWord)) then begin
  941.             {Move the fix into place}
  942.             Move(NewWord[1], Curline^.Txt^[Start], Length(NewWord));
  943.             {Leave the cursor at the start of the new word so it is rechecked}
  944.             LastCh := Blank;
  945.             Modified := True;
  946.           end;
  947.         end;
  948.       end;                   {EdInsertFix}
  949.  
  950.       procedure EdInsertMark(Wlen : Integer);
  951.         {-Insert editing mark noting where problems are}
  952.       var
  953.         Start : Integer;
  954.  
  955.       begin                  {EdInsertMark}
  956.         with Curwin^ do begin
  957.           Start := Colno-Wlen;
  958.           {Insert space in line for mark}
  959.           if EdInsertRoom(Curline, Start, Length(MarkStr)) then begin
  960.             {Move the mark into place}
  961.             Move(MarkStr[1], Curline^.Txt^[Start], Length(MarkStr));
  962.             Colno := Colno+Length(MarkStr);
  963.             Modified := True;
  964.           end;
  965.         end;
  966.       end;                   {EdInsertMark}
  967.  
  968.     begin                    {EdCheckWords}
  969.  
  970.       with Curwin^ do begin
  971.  
  972.         {Initialize for first word}
  973.         Done := False;
  974.         Marking := False;
  975.         if Colno = 1 then
  976.           LastCh := Blank
  977.         else
  978.           LastCh := Curline^.Txt^[Pred(Colno)];
  979.  
  980.         {Assure line and column counts up to date}
  981.         EdGenLineOne(Curwin);
  982.  
  983.         {Update the screen}
  984.         EdUpdatewindow(Curwin);
  985.         EdWritePromptLine(EdGetMessage(278));
  986.  
  987.         {Loop checking every word}
  988.         repeat
  989.  
  990.           EdGetNextWord(Done, W);
  991.  
  992.           if EdKeyInterrupt then begin
  993.             {Should we stop checking?}
  994.             {Clear keyboard buffer}
  995.             while EdKeyInterrupt do
  996.               Ch := EdGetAnyChar;
  997.             Done := EdYesNo(EdGetMessage(275));
  998.             if Done then
  999.               Abortcmd := True
  1000.             else begin
  1001.               EdSetCursor(CursorOff);
  1002.               if Marking then
  1003.                 EdWritePromptLine(EdGetMessage(274))
  1004.               else
  1005.                 EdWritePromptLine(EdGetMessage(278));
  1006.               Abortcmd := False;
  1007.             end;
  1008.           end;
  1009.  
  1010.           if not(Done) then begin
  1011.  
  1012.             {Show some screen action while checking}
  1013.             EdUpdateStatusLine(Curwin);
  1014.  
  1015.             {See if word in dictionary}
  1016.             if not(EdWordInDict(RamDict, W)) then begin
  1017.  
  1018.               if Marking then
  1019.  
  1020.                 {Non-interactive, insert mark into text stream}
  1021.                 EdInsertMark(Length(W))
  1022.  
  1023.               else begin
  1024.  
  1025.                 {Put the bad word in a global for access by other routines}
  1026.                 BadWord := W;
  1027.                 {Show the current line with the error highlighted}
  1028.                 EdHighlightScreen(Colno-Length(W), Pred(Colno), ScreenAttr[CursorColor], False);
  1029.                 {Restore Lightning for interactive use during course of prompting}
  1030.                 EdReserveLightning(False, AuxName);
  1031.  
  1032.                 repeat
  1033.  
  1034.                   Action := EdGetFixAction;
  1035.                   if Abortcmd then begin
  1036.  
  1037.                     {Quit checking}
  1038.                     Done := True;
  1039.                     Action := SplQuit;
  1040.  
  1041.                   end else
  1042.                     case Action of
  1043.  
  1044.                       SplIgnore : {Ignore once}
  1045.                         ;
  1046.  
  1047.                       SplAddLocal : {Add to local dictionary in RAM}
  1048.                         if EdMemAvail(Succ(Length(W)), FreeListTemp) then begin
  1049.                           EdLocase(W);
  1050.                           GetMem(Wptr, Succ(Length(W)));
  1051.                           Move(W, Wptr^, Succ(Length(W)));
  1052.                           if EdAddWord(RamDict, Wptr) then
  1053.                             ;
  1054.                         end else
  1055.                           EdErrormsg(35);
  1056.  
  1057.                       SplMark : {Mark in text}
  1058.                         begin
  1059.                           EdInsertMark(Length(W));
  1060.                           EdUpdateScreen;
  1061.                         end;
  1062.  
  1063.                       SplMarkRest : {Mark remaining words in text without prompt}
  1064.                         begin
  1065.                           EdInsertMark(Length(W));
  1066.                           EdWritePromptLine(EdGetMessage(274));
  1067.                           Marking := True;
  1068.                         end;
  1069.  
  1070.                       SplChkLikely : {Check likely alternatives}
  1071.                         begin
  1072.                           {Assure the Lightning data area is correctly initialized}
  1073.                           Junk := EdWordInDict(RamDict, W);
  1074.                           {Choose from soundalike words}
  1075.                           NewWord := EdPickLikelyWord(True);
  1076.                           if Abortcmd or EdStringEmpty(NewWord) then
  1077.                             {Loop around to try again}
  1078.                             Abortcmd := False
  1079.                           else begin
  1080.                             EdInsertFix(Length(W), NewWord);
  1081.                             EdUpdateScreen;
  1082.                             {Go on}
  1083.                             Action := SplIgnore;
  1084.                           end;
  1085.                         end;
  1086.  
  1087.                       SplEdit : {Edit the word}
  1088.                         begin
  1089.                           EdWritePromptLine(EdGetMessage(273));
  1090.                           NewWord := W;
  1091.                           EdAskfor(EdGetMessage(272), 10, 17, 60, NewWord);
  1092.                           if Abortcmd then begin
  1093.                             {Give another chance}
  1094.                             Action := SplChkLikely;
  1095.                             Abortcmd := False;
  1096.                           end else begin
  1097.                             EdInsertFix(Length(W), NewWord);
  1098.                             EdUpdateScreen;
  1099.                           end;
  1100.                           EdWritePromptLine(EdGetMessage(278));
  1101.                         end;
  1102.  
  1103.                       SplAddauxi : {Add to auxi dictionary}
  1104.                         begin
  1105.                           EdAddAuxiDict(W, AuxName);
  1106.                           if Abortcmd then begin
  1107.                             {Give another chance}
  1108.                             Action := SplChkLikely;
  1109.                             Abortcmd := False;
  1110.                           end;
  1111.                         end;
  1112.  
  1113.                     end;
  1114.  
  1115.                   EdSetCursor(CursorOff);
  1116.  
  1117.                 until Action <> SplChkLikely;
  1118.  
  1119.                 {Reserve Lightning again}
  1120.                 EdReserveLightning(True, AuxName);
  1121.  
  1122.               end;
  1123.             end;
  1124.           end;
  1125.         until Done;
  1126.  
  1127.       end;
  1128.  
  1129.       {Assure cursor not past end of line}
  1130.       EdMoveCursorIntoLine;
  1131.  
  1132.     end;                     {EdCheckWords}
  1133.  
  1134.     procedure EdInitLocalDict(var RamDict : RamDictTable);
  1135.       {-Load a dictionary of most commonly used words for fastest checking}
  1136.     type
  1137.       DictBuffer = array[1..MaxInt] of Byte;
  1138.     var
  1139.       LocalPtr : ^DictBuffer;
  1140.       I, LocalPos : Integer;
  1141.       Wptr : WordPtr;
  1142.  
  1143.     begin                    {EdInitLocalDict}
  1144.  
  1145.       {Clear the hash table}
  1146.       for I := 0 to LastHashBucket do
  1147.         EdSetPtrNil(RamDict[I]);
  1148.  
  1149.       {Get pointer to start of local dictionary string table}
  1150.       LocalPtr := EdInitLocalPtr;
  1151.       LocalPos := 1;
  1152.  
  1153.       {Add words to RAM dictionary as long as space remains}
  1154.       repeat
  1155.  
  1156.         {Point to next string from local buffer}
  1157.         Wptr := Addr(LocalPtr^[LocalPos]);
  1158.  
  1159.         {Add word to the local dictionary}
  1160.         if not(EdAddWord(RamDict, Wptr)) then
  1161.           {Out of heap space}
  1162.           Exit;
  1163.  
  1164.         LocalPos := LocalPos+Succ(LocalPtr^[LocalPos]);
  1165.  
  1166.       until (LocalPtr^[LocalPos] = 0);
  1167.  
  1168.     end;                     {EdInitLocalDict}
  1169.  
  1170.     procedure EdDisposeLocalDict(var RamDict : RamDictTable);
  1171.       {-Release heap space used by local dictionary}
  1172.     var
  1173.       I : Integer;
  1174.       P, N : RamDictPtr;
  1175.  
  1176.     begin                    {EdDisposeLocalDict}
  1177.       for I := 0 to LastHashBucket do begin
  1178.         P := RamDict[I];
  1179.         while EdPtrNotNil(P) do begin
  1180.           N := P^.Next;
  1181.           if Seg(P^.Word^) <> CSeg then
  1182.             {Deallocate space for words just added to local dictionary}
  1183.             FreeMem(P^.Word, Succ(Length(P^.Word^)));
  1184.           {Deallocate space for the hash table record}
  1185.           FreeMem(P, SizeOf(RamDictRec));
  1186.           P := N;
  1187.         end;
  1188.       end;
  1189.     end;                     {EdDisposeLocalDict}
  1190.  
  1191.   begin                      {EdSpellingCheck}
  1192.  
  1193.     {Assure Lightning is installed}
  1194.     if not(EdLightningPresent) then begin
  1195.       EdErrormsg(57);
  1196.       Exit;
  1197.     end;
  1198.  
  1199.     {Clear menus from screen}
  1200.     if EdPtrNotNil(CurrMenu) then
  1201.       EdEraseMenus;
  1202.  
  1203.     {Set up screen}
  1204.     SaveCursor := SolidCursor;
  1205.     SolidCursor := False;
  1206.     EdSetCursor(CursorOff);
  1207.     EdEraseMenuHelp;
  1208.     EdWritePromptLine(EdGetMessage(258));
  1209.  
  1210.     {Initialize and dedicate Lightning for this program}
  1211.     EdReserveLightning(True, AuxName);
  1212.  
  1213.     {Initialize fast local dictionary}
  1214.     EdInitLocalDict(RamDict);
  1215.  
  1216.     {One last chance to get out in case of mistake}
  1217.     if EdKeyInterrupt then
  1218.       Abortcmd := True
  1219.     else begin
  1220.       {Get the start and stop points in the text stream}
  1221.       EdInitEndPoints(CheckTo);
  1222.       {Check the text}
  1223.       EdCheckWords(CheckTo);
  1224.     end;
  1225.  
  1226.     {Restore Lightning for interactive use}
  1227.     EdReserveLightning(False, AuxName);
  1228.  
  1229.     {Remove the local dictionary from memory}
  1230.     EdDisposeLocalDict(RamDict);
  1231.  
  1232.     {Restore screen}
  1233.     SolidCursor := SaveCursor;
  1234.     EdSetCursor(CursorType);
  1235.     EdSetCursorOff;
  1236.  
  1237.     if not(Abortcmd) then begin
  1238.       {Display completion status and wait for keypress}
  1239.       EdWritePromptLine(EdGetMessage(246));
  1240.       EdUpdateCursor;
  1241.       EdUpdateScreen;
  1242.       EdWaitforKey;
  1243.     end;
  1244.  
  1245.   end;                       {EdSpellingCheck}
  1246.  
  1247.  
  1248. end.
  1249.