home *** CD-ROM | disk | FTP | other *** search
/ Dream 41 / Amiga_Dream_41.iso / Amiga / Programmation / envWRD44.lha / envWRD44 / source / words / funcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-03-18  |  50.5 KB  |  2,013 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  ApiLib ⌐1997 Dietmar Eilert
  4.  
  5.  ISpell interface for GoldED 4, ⌐1996 Dietmar Eilert.
  6.  
  7.  Dice:
  8.  
  9.  DMAKE
  10.  
  11.  -------------------------------------------------------------------------------
  12.  
  13. */
  14.  
  15. #include "defs.h"
  16.  
  17. /// "prototypes"
  18.  
  19. // library functions
  20.  
  21. Prototype LibCall struct APIClient *APIMountClient(__A0 struct APIMessage *, __A1 char *);
  22. Prototype LibCall void              APICloseClient(__A0 struct APIClient  *, __A1 struct APIMessage *);
  23. Prototype LibCall void              APIBriefClient(__A0 struct APIClient  *, __A1 struct APIMessage *);
  24. Prototype LibCall void              APIFree       (__A0 struct APIClient  *, __A1 struct APIOrder   *);
  25.  
  26. // private functions
  27.  
  28. Prototype struct FileInfoBlock     *FileInfo(UBYTE *, struct FileInfoBlock *);
  29. Prototype void                      Beep(UWORD);
  30. Prototype struct Node              *SearchNode(struct List *, UWORD);
  31. Prototype UWORD                     ComputeX(struct TextFont *, UWORD);
  32. Prototype UWORD                     ComputeY(struct TextFont *, UWORD);
  33. Prototype void                      GetVScreenSize(struct Screen *, UWORD *, UWORD *);
  34. Prototype struct MsgPort           *RunISpell(void);
  35. Prototype struct Window            *OpenMessageWin(UBYTE *, BOOL);
  36. Prototype struct Window            *Message       (UBYTE *, BOOL, struct Window *);
  37. Prototype BOOL                      ExistPath(UBYTE *);
  38. Prototype UBYTE                    *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *, UBYTE *);
  39. Prototype LONG                      CommandSpell     (struct APIClient *, struct APIMessage *, ULONG *);
  40. Prototype void                      IgnoreWordCurrent(struct APIClient *, struct APIMessage *, BOOL);
  41. Prototype BOOL                      CheckWord        (struct APIClient *, struct APIMessage *, UBYTE *, UWORD, UBYTE *);
  42. Prototype void                      CheckWordCurrent (struct APIClient *, struct APIMessage *, BOOL);
  43. Prototype void                      LearnWord        (struct APIClient *, struct APIMessage *, UBYTE *, UWORD, BOOL);
  44. Prototype void                      LearnWordCurrent (struct APIClient *, struct APIMessage *, BOOL);
  45. Prototype void                      Dispatch         (struct APIClient *, struct APIMessage *);
  46. Prototype void                      ShowSpell        (struct APIClient *, struct APIMessage *, struct List *, UBYTE *, UBYTE *);
  47. Prototype void                      Replace          (struct APIClient *, struct APIMessage *, UWORD, UWORD, UBYTE *);
  48. Prototype BOOL                      DoExecute        (UBYTE *, BOOL, UBYTE *, UBYTE *, ULONG, WORD);
  49. Prototype struct MsgPort           *WaitForISpell(void);
  50. Prototype void                      StopISpell(void);
  51. Prototype void                      Uncapitalize(UBYTE *);
  52.  
  53. // private defines
  54.  
  55. #define UNDEFINED  ((UWORD)~0)
  56.  
  57. // buffer handles are allocated to store text-local data
  58.  
  59. struct BufferHandle {
  60.  
  61.     struct APIClient        APIClient;               // API handle
  62.     struct APIOrder         APIOrder;                // API order
  63.     struct APIModifyRequest APIModifyRequest;        // API modification request
  64.     UBYTE                   APIData[4096];           // API buffer
  65.     UBYTE                   Buffer [4096];           // private ARexx buffer
  66.     struct MsgPort         *Port;                    // private ARexx message port
  67. };
  68.  
  69. // AutoConfig semaphore coordinates ISpell usage
  70.  
  71. struct AutoConfig {
  72.  
  73.     struct SignalSemaphore Semaphore;
  74.     UWORD                  Users;                    // ISpell users
  75.     ULONG                  Words;                    // new words counter
  76. };
  77.  
  78. ///
  79. /// "globals"
  80.  
  81. struct AutoConfig      *AutoConfig;
  82. struct SignalSemaphore  ISpellSemaphore;
  83. BOOL                    IsLetter  [256];             // valid letters
  84. BOOL                    IsBoundary[256];             // valid letter OR space (depends on context)
  85. BOOL                    IsTrigger [256];             // trigger characters
  86. BOOL                    Online;                      // online spelll checking ?
  87. UWORD                   Volume;                      // beep volume
  88.  
  89. ///
  90. /// "library functions"
  91.  
  92. LibCall struct APIClient *
  93. APIMountClient(__A0 struct APIMessage *apiMsg, __A1 char *args)
  94. {
  95.     struct BufferHandle *bufferHandle = NULL;
  96.  
  97.     // start ISpell (if not yet running)
  98.  
  99.     if (RunISpell()) {
  100.  
  101.         // allocate local data for this editor window (extended APIClient structure)
  102.  
  103.         if (bufferHandle = (struct BufferHandle *)AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  104.  
  105.             if (bufferHandle->Port = CreateMsgPort()) {
  106.  
  107.                 static UBYTE *apiCommands[] = { "SPELL PREV/S,NEXT/S,ADD/S,CURRENT/S,ASK/S", NULL };
  108.  
  109.                 // describe our features
  110.  
  111.                 bufferHandle->APIClient.api_APIVersion = API_INTERFACE_VERSION;
  112.                 bufferHandle->APIClient.api_Version    = 2;
  113.                 bufferHandle->APIClient.api_Name       = "Words";
  114.                 bufferHandle->APIClient.api_Info       = "ISpell interface for GoldED";
  115.                 bufferHandle->APIClient.api_Commands   = NULL;
  116.                 bufferHandle->APIClient.api_Serial     = 0;
  117.                 bufferHandle->APIClient.api_Classes    = API_CLASS_SYSTEM | API_CLASS_COMMAND;
  118.                 bufferHandle->APIClient.api_Area       = NULL;
  119.  
  120.                 if (Online)
  121.                     bufferHandle->APIClient.api_Classes |= API_CLASS_KEY;
  122.             }
  123.         }
  124.     }
  125.  
  126.     return((struct APIClient *)bufferHandle);
  127. }
  128.  
  129.  
  130. LibCall void
  131. APICloseClient(__A0 struct APIClient *handle, __A1 struct APIMessage *apiMsg)
  132. {
  133.     struct BufferHandle *bufferHandle = (struct BufferHandle *)handle;
  134.  
  135.     // free local data (allocated for a specific editor window)
  136.  
  137.     if (bufferHandle->Port)
  138.  
  139.         DeleteMsgPort(bufferHandle->Port);
  140.  
  141.     FreeVec(bufferHandle);
  142.  
  143.     StopISpell();
  144. }
  145.  
  146.  
  147. LibCall void
  148. APIBriefClient(__A0 struct APIClient *handle, __A1 struct APIMessage *apiMsg)
  149. {
  150.     struct APIMessage *msg;
  151.  
  152.     // handle host's command notify
  153.  
  154.     for (msg = apiMsg; msg; msg = msg->api_Next) {
  155.  
  156.         if (msg->api_State == API_STATE_NOTIFY) {
  157.  
  158.             // process message sent by GoldED
  159.  
  160.             switch (msg->api_Class) {
  161.  
  162.                 // did user press a key ?
  163.  
  164.                 case API_CLASS_KEY:
  165.  
  166.                     struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  167.  
  168.                     switch (msg->api_Action) {
  169.  
  170.                         case API_ACTION_VANILLAKEY:
  171.  
  172.                             // trigger key used ?
  173.  
  174.                             if (Online) {
  175.  
  176.                                 if (IsTrigger[(UWORD)apiMsg->api_Data]) {
  177.  
  178.                                     // spellcheck last word
  179.  
  180.                                     CheckWordCurrent(handle, apiMsg, FALSE);
  181.                                 }
  182.                             }
  183.  
  184.                             break;
  185.                     }
  186.  
  187.                     break;
  188.  
  189.                 // did user enter a command ?
  190.  
  191.                 case API_CLASS_COMMAND:
  192.  
  193.                     switch (msg->api_Action) {
  194.  
  195.                         case API_ACTION_COMMAND:
  196.  
  197.                             Dispatch(handle, msg);
  198.  
  199.                             break;
  200.  
  201.                         default:
  202.  
  203.                             msg->api_Error = API_ERROR_UNKNOWN;
  204.                     }
  205.  
  206.                     break;
  207.  
  208.                 default:
  209.  
  210.                     msg->api_Error = API_ERROR_UNKNOWN;
  211.             }
  212.         }
  213.     }
  214. }
  215.  
  216.  
  217. LibCall void
  218. APIFree(__A0 struct APIClient *handle, __A1 struct APIOrder *apiOrder)
  219. {
  220.     ;
  221. }
  222.  
  223. ///
  224. /// "dispatcher"
  225.  
  226. /* --------------------------------- Dispatch ----------------------------------
  227.  
  228.  Handle command sent by GoldED. This API client adds commands (currently only
  229.  the SPELL command) to GoldED's command set. This function is called if GoldED
  230.  detects an command that needs to be handled by an API client (e.g. us).
  231.  
  232. */
  233.  
  234. void
  235. Dispatch(handle, apiMsg)
  236.  
  237. struct APIClient  *handle;
  238. struct APIMessage *apiMsg;
  239. {
  240.     struct RDArgs *rdArgs;
  241.  
  242.     if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {
  243.  
  244.         // table of supported commands, handlers & template strings
  245.  
  246.         static struct parser { char *command; LONG (*handler)(struct APIClient *, struct APIMessage *, ULONG *); char *template; } parser[] = {
  247.  
  248.             "SPELL", (APTR)CommandSpell, "CURRENT/S,ADD/S,IGNORE/S,NOCASE/S,SAVE/S",
  249.              NULL
  250.         };
  251.  
  252.         ULONG argArray[10];
  253.         UBYTE buffer  [80];
  254.         ULONG n;
  255.  
  256.         memset(argArray, 0, sizeof(argArray));
  257.  
  258.         // make LF-terminated copy of command string (required by dos/readArgs):
  259.  
  260.         strncpy(buffer, apiMsg->api_Command, sizeof(buffer));
  261.  
  262.         strcat(buffer, "\12");
  263.  
  264.         for (n = 0; parser[n].command; ++n) {
  265.  
  266.             if (memcmp(buffer, parser[n].command, strlen(parser[n].command)) == 0) {
  267.  
  268.                 UBYTE *arguments = buffer + strlen(parser[n].command);
  269.  
  270.                 // tell host that message has been consumed by us (hide it from other API clients)
  271.  
  272.                 apiMsg->api_State = API_STATE_CONSUMED;
  273.  
  274.                 rdArgs->RDA_Source.CS_Buffer = arguments;
  275.                 rdArgs->RDA_Source.CS_Length = strlen(arguments);
  276.                 rdArgs->RDA_Source.CS_CurChr = 0;
  277.                 rdArgs->RDA_DAList           = 0;
  278.                 rdArgs->RDA_Buffer           = NULL;
  279.  
  280.                 // arguments expected ?
  281.  
  282.                 if (parser[n].template) {
  283.  
  284.                     if (ReadArgs(parser[n].template, argArray, rdArgs)) {
  285.  
  286.                         apiMsg->api_RC = (*parser[n].handler)(handle, apiMsg, argArray);
  287.  
  288.                         FreeArgs(rdArgs);
  289.                     }
  290.                     else {
  291.  
  292.                         static UBYTE error[80 + 1];
  293.  
  294.                         apiMsg->api_RC           = RC_WARN;
  295.                         apiMsg->api_CommandError = error;
  296.  
  297.                         Fault(IoErr(), "IoErr()", error, 80);
  298.                     }
  299.                 }
  300.                 else
  301.                     apiMsg->api_RC = (*parser[n].handler)(handle, apiMsg, argArray);
  302.             }
  303.         }
  304.  
  305.         FreeDosObject(DOS_RDARGS, rdArgs);
  306.     }
  307. }
  308.  
  309.  
  310. /* -------------------------------- CommandSpell -------------------------------
  311.  
  312.  SPELL command handler
  313.  
  314.  template: CURRENT/S,ADD/S,IGNORE/S,NOCASE/S,SAVE/S
  315.  
  316. */
  317.  
  318. LONG
  319. CommandSpell(handle, apiMsg, argArray)
  320.  
  321. struct APIClient  *handle;
  322. struct APIMessage *apiMsg;
  323. ULONG             *argArray;
  324. {
  325.     if (argArray[0])                                 // CURRENT/S
  326.  
  327.         CheckWordCurrent(handle, apiMsg, TRUE);
  328.  
  329.     if (argArray[1])                                 // ADD/S
  330.  
  331.         LearnWordCurrent(handle, apiMsg, argArray[3]);
  332.  
  333.     if (argArray[2])                                 // IGNORE/S
  334.  
  335.         IgnoreWordCurrent(handle, apiMsg, argArray[3]);
  336.  
  337.     if (argArray[4]) {                               // SAVE/S
  338.  
  339.         struct AutoConfig *autoConfig;
  340.  
  341.         Forbid();
  342.  
  343.         if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  344.  
  345.             ObtainSemaphore(&autoConfig->Semaphore);
  346.  
  347.             // save user dictionary
  348.  
  349.             SendRexxCommand("IRexxSpell", "ADD A", ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer);
  350.  
  351.             // reset global new words counter
  352.  
  353.             autoConfig->Words = 0;
  354.  
  355.             ReleaseSemaphore(&autoConfig->Semaphore);
  356.         }
  357.         else
  358.             Message("Communication error (semaphore not found)", TRUE, NULL);
  359.  
  360.         Permit();
  361.     }
  362.  
  363.     return(RC_OK);
  364. }
  365.  
  366. ///
  367. /// "misc"
  368.  
  369. /* --------------------------------- Uncapitalize ------------------------------
  370.  
  371.  Uncapitalize text if first character is uppercase and all other characters are
  372.  lowercase. Writes to <text>.
  373.  
  374. */
  375.  
  376. void
  377. Uncapitalize(text)
  378.  
  379. UBYTE *text;
  380. {
  381.     if (isupper(*text)) {
  382.  
  383.         UBYTE *next;
  384.  
  385.         for (next = text + 1; *next; ++next) {
  386.  
  387.             if (isupper(*next))
  388.  
  389.                 break;
  390.         }
  391.  
  392.         if (*next == 0)
  393.  
  394.             *text = tolower(*text);
  395.     }
  396. }
  397.  
  398.  
  399. /* --------------------------------- FileInfo ----------------------------------
  400.  
  401.  Return file information or NULL (writes to <fib>)
  402.  
  403. */
  404.  
  405. struct FileInfoBlock *
  406. FileInfo(name, fib)
  407.  
  408. struct FileInfoBlock *fib;
  409. UBYTE                *name;
  410. {
  411.     BPTR handle;
  412.  
  413.     if (handle = Lock(name, ACCESS_READ)) {
  414.  
  415.         if (Examine(handle, fib)) {
  416.  
  417.             UnLock(handle);
  418.  
  419.             return(fib);
  420.         }
  421.         else
  422.             UnLock(handle);
  423.     }
  424.  
  425.     return(NULL);
  426. }
  427.  
  428.  
  429. /* --------------------------------- DoExecute ---------------------------------
  430.  
  431.  Run DOS command
  432.  
  433. */
  434.  
  435. BOOL
  436. DoExecute(cmd, async, output, directory, stack, prio)
  437.  
  438. UBYTE *cmd, *output, *directory;
  439. ULONG  stack;
  440. WORD   prio;
  441. BOOL   async;
  442. {
  443.     BPTR newCurrentDir;
  444.     BOOL success;
  445.  
  446.     success = FALSE;
  447.  
  448.     if (newCurrentDir = Lock(directory, SHARED_LOCK)) {
  449.  
  450.         BPTR inHandle, outHandle, oldCurrentDir;
  451.  
  452.         oldCurrentDir = CurrentDir(newCurrentDir);
  453.  
  454.         if (outHandle = Open(output, MODE_NEWFILE)) {
  455.  
  456.             struct MsgPort *oldct, *newct;
  457.  
  458.             if (IsInteractive(outHandle)) {
  459.  
  460.                 newct = ((struct FileHandle *)BADDR(outHandle))->fh_Type;
  461.  
  462.                 oldct = SetConsoleTask(newct);
  463.  
  464.                 inHandle = Open("CONSOLE:", MODE_OLDFILE);
  465.  
  466.                 SetConsoleTask(oldct);
  467.             }
  468.             else {
  469.  
  470.                 newct = NULL;
  471.  
  472.                 inHandle = Open("NIL:", MODE_OLDFILE);
  473.             }
  474.  
  475.             if (inHandle) {
  476.  
  477.                 struct TagItem tagItems[] = {
  478.  
  479.                     SYS_Output,     (ULONG)NULL,
  480.                     SYS_Input,      (ULONG)NULL,
  481.                     NP_ConsoleTask, (ULONG)NULL,
  482.                     SYS_Asynch,     (ULONG)FALSE,
  483.                     NP_StackSize,   (ULONG)8196,
  484.                     NP_Priority,    (ULONG)0,
  485.                     SYS_UserShell,  (ULONG)TRUE,
  486.                     TAG_DONE
  487.                 };
  488.  
  489.                 tagItems[0].ti_Data = (ULONG)outHandle;
  490.                 tagItems[1].ti_Data = (ULONG)inHandle;
  491.                 tagItems[2].ti_Data = (ULONG)newct;
  492.                 tagItems[3].ti_Data = (ULONG)async;
  493.                 tagItems[4].ti_Data = (ULONG)stack;
  494.                 tagItems[5].ti_Data = (ULONG)prio;
  495.  
  496.                 if (SystemTagList(cmd, tagItems) != -1L)
  497.  
  498.                     success = TRUE;
  499.  
  500.                 if (!(async && success))
  501.  
  502.                     Close(inHandle);
  503.             }
  504.  
  505.             if (!(async && success))
  506.  
  507.                 Close(outHandle);
  508.         }
  509.  
  510.         CurrentDir(oldCurrentDir);
  511.  
  512.         UnLock(newCurrentDir);
  513.     }
  514.  
  515.     return(success);
  516. }
  517.  
  518.  
  519. /* --------------------------------- ExistPath -----------------------------------
  520.  
  521.  Check whether file/directory exists.
  522.  
  523. */
  524.  
  525. BOOL
  526. ExistPath(path)
  527.  
  528. UBYTE *path;
  529. {
  530.     BPTR lock;
  531.  
  532.     if (lock = Lock(path, ACCESS_READ))
  533.         UnLock(lock);
  534.  
  535.     if (lock)
  536.         return(TRUE);
  537.     else
  538.         return(FALSE);
  539. }
  540.  
  541.  
  542. /* ---------------------------------- SearchNode --------------------------------
  543.  
  544.  Return pointer to node if ordinal number (0, ...) is known.
  545.  
  546. */
  547.  
  548. struct Node *
  549. SearchNode(list, ordinal)
  550.  
  551. struct List *list;
  552. UWORD  ordinal;
  553. {
  554.     struct Node *node;
  555.  
  556.     for (node = list->lh_Head; node->ln_Succ; --ordinal, node = node->ln_Succ)
  557.  
  558.         if (ordinal == 0)
  559.  
  560.             return(node);
  561.  
  562.     return(NULL);
  563. }
  564.  
  565.  
  566. /* ----------------------------------- Beep ------------------------------------
  567.  
  568.  Short audible beep (volumn: 0...63)
  569.  
  570. */
  571.  
  572. void
  573. Beep(volume)
  574.  
  575. UWORD volume;
  576. {
  577.     if (volume) {
  578.  
  579.         struct IOAudio *audioIO;
  580.  
  581.         if (audioIO = (struct IOAudio *)AllocVec(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) {
  582.  
  583.             struct MsgPort *audioMP;
  584.  
  585.             if (audioMP = CreateMsgPort()) {
  586.  
  587.                 UBYTE whichannel[] = { 1, 2, 4, 8 };
  588.  
  589.                 audioIO->ioa_Request.io_Message.mn_ReplyPort   = audioMP;
  590.                 audioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
  591.                 audioIO->ioa_Request.io_Command                = ADCMD_ALLOCATE;
  592.                 audioIO->ioa_Request.io_Flags                  = ADIOF_NOWAIT;
  593.                 audioIO->ioa_AllocKey                          = 0;
  594.                 audioIO->ioa_Data                              = whichannel;
  595.                 audioIO->ioa_Length                            = sizeof(whichannel);
  596.  
  597.                 if (!OpenDevice("audio.device", 0, (struct IORequest *)audioIO, 0)) {
  598.  
  599.                     __chip const static UBYTE waveptr[2] = {127, -127};
  600.  
  601.                     audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
  602.                     audioIO->ioa_Request.io_Command              = CMD_WRITE;
  603.                     audioIO->ioa_Request.io_Flags                = ADIOF_PERVOL;
  604.  
  605.                     audioIO->ioa_Data   = waveptr;
  606.                     audioIO->ioa_Length = 2;
  607.                     audioIO->ioa_Period = 1015;
  608.                     audioIO->ioa_Volume = volume;
  609.                     audioIO->ioa_Cycles = 60;
  610.  
  611.                     BeginIO((struct IORequest *)audioIO );
  612.  
  613.                     WaitPort(audioMP);
  614.                     GetMsg  (audioMP);
  615.  
  616.                     CloseDevice((struct IORequest *)audioIO);
  617.                 }
  618.  
  619.                 DeleteMsgPort(audioMP);
  620.             }
  621.  
  622.             FreeVec(audioIO);
  623.         }
  624.     }
  625. }  
  626.  
  627.  
  628. /* ----------------------------------- Replace ---------------------------------
  629.  
  630.  Replace word of length <wordLen> at position <pos> within current line by
  631.  string <replacement> (0-terminated)
  632.  
  633. */
  634.  
  635. void
  636. Replace(handle, apiMsg, column, wordLen, replacement)
  637.  
  638. struct APIClient   *handle;
  639. struct APIMessage  *apiMsg;
  640. UBYTE              *replacement;
  641. UWORD               column;
  642. UWORD               wordLen;
  643. {
  644.     struct APIOrder         *apiOrder;
  645.     struct APIModifyRequest *apiModifyRequest;
  646.     UBYTE                   *apiBuffer;
  647.     struct EditConfig       *config;
  648.     UBYTE                   *dest;
  649.     UWORD                    rplcLen;
  650.  
  651.     config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  652.  
  653.     // allocate API command structures
  654.  
  655.     apiModifyRequest = &((struct BufferHandle *)handle)->APIModifyRequest;
  656.     apiOrder         = &((struct BufferHandle *)handle)->APIOrder;
  657.     apiBuffer        =  ((struct BufferHandle *)handle)->APIData;
  658.  
  659.     // copy current line to buffer
  660.  
  661.     movmem(config->CurrentBuffer, apiBuffer, config->CurrentLen);
  662.  
  663.     // replace old word by <replacement>
  664.  
  665.     dest = apiBuffer + column;
  666.  
  667.     rplcLen = strlen(replacement);
  668.  
  669.     if (rplcLen != wordLen)
  670.  
  671.         movmem(dest + wordLen, dest + rplcLen, config->CurrentLen - column - wordLen);
  672.  
  673.     movmem(replacement, dest, rplcLen);
  674.  
  675.     // create API command for GoldED
  676.  
  677.     apiModifyRequest->mr_Line   = config->Line;
  678.     apiModifyRequest->mr_Column = column;
  679.     apiModifyRequest->mr_Size   = config->CurrentLen + (rplcLen - wordLen);
  680.     apiModifyRequest->mr_Data   = apiBuffer;
  681.  
  682.     apiOrder->api_Data = apiModifyRequest;
  683.     apiOrder->api_Next = NULL;
  684.  
  685.     // notify GoldED
  686.  
  687.     apiMsg->api_Order = apiOrder;
  688. }
  689.  
  690. ///
  691. /// "gui"
  692.  
  693. /* ------------------------------ GetVScreenSize -------------------------------
  694.  
  695.  Calculate visible screen rectangle
  696.  
  697. */
  698.  
  699. void
  700. GetVScreenSize(screen, displayW, displayH)
  701.  
  702. struct Screen *screen;
  703. UWORD         *displayW;
  704. UWORD         *displayH;
  705. {
  706.     struct Rectangle clip;
  707.  
  708.     // read screen size
  709.  
  710.     if (QueryOverscan(GetVPModeID(&screen->ViewPort), &clip, OSCAN_TEXT)) {
  711.  
  712.         *displayW = clip.MaxX - clip.MinX + 1;
  713.         *displayH = clip.MaxY - clip.MinY + 1;
  714.     }
  715.     else {
  716.  
  717.         *displayW = screen->Width;
  718.         *displayH = screen->Height;
  719.     }
  720. }
  721.  
  722.  
  723. /* --------------------------------- ComputeX ----------------------------------
  724.  
  725.  Scale width <value> to match font
  726.  
  727. */
  728.  
  729. UWORD
  730. ComputeX(font, value)
  731.  
  732. struct TextFont *font;
  733. UWORD            value;
  734. {
  735.     return((font->tf_XSize * value) / 8);
  736. }
  737.  
  738.  
  739. /* --------------------------------- ComputeY ----------------------------------
  740.  
  741.  Resize height <value> to match font
  742.  
  743. */
  744.  
  745. UWORD
  746. ComputeY(font, value)
  747.  
  748. struct TextFont *font;
  749. UWORD            value;
  750. {
  751.     return((font->tf_YSize * value) / 8);
  752. }
  753.  
  754.  
  755. /* -------------------------------- OpenMessageWin -----------------------------
  756.  
  757.  Open info window. Window is closed automatically after a short delay time if
  758.  <sync> is TRUE.
  759.  
  760. */
  761.  
  762. struct Window *
  763. OpenMessageWin(text, sync)
  764.  
  765. UBYTE *text;
  766. BOOL   sync;
  767. {
  768.     struct TextFont *font;
  769.     struct Screen   *screen;
  770.     struct Window   *win;
  771.     ULONG            lock;
  772.  
  773.     win  = NULL;
  774.     font = NULL;
  775.  
  776.     // critical stuff (barely legal - don't do this at home, kids ;-)
  777.  
  778.     lock = LockIBase(0);
  779.  
  780.     // find frontmost screen
  781.  
  782.     screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen;
  783.  
  784.     // keep system frozen
  785.  
  786.     Forbid();
  787.  
  788.     // enable rendering functions
  789.  
  790.     UnlockIBase(lock);
  791.  
  792.     if (screen) {
  793.  
  794.         struct TextAttr textAttr = { "topaz.font", 8, 0, FPF_DESIGNED };
  795.  
  796.         WORD len, width, height, fontW, fontH, x, y, displayW, displayH, xOffset, yOffset;
  797.  
  798.         GetVScreenSize(screen, &displayW, &displayH);
  799.  
  800.         xOffset = -screen->ViewPort.DxOffset;
  801.         yOffset = -screen->ViewPort.DyOffset;
  802.  
  803.         textAttr = *screen->Font;
  804.  
  805.         if (font = OpenDiskFont(&textAttr)) {
  806.  
  807.             fontW = font->tf_XSize;
  808.             fontH = font->tf_YSize;
  809.  
  810.             CloseFont(font);
  811.         }
  812.         else
  813.             fontW = fontH = textAttr.ta_YSize;
  814.  
  815.         len = strlen(text);
  816.  
  817.         if (len < 30)
  818.             len = 30;
  819.  
  820.         width  = 20 + fontW * len;
  821.         height = 20 + fontH;
  822.  
  823.         // center window on screen
  824.  
  825.         if (displayW) {
  826.  
  827.             x = ((displayW - width )>>1) + xOffset;
  828.             y = ((displayH - height)>>1) + yOffset;
  829.         }
  830.         else
  831.             x = y = 0;
  832.  
  833.         win = OpenWindowTags(NULL,
  834.  
  835.             WA_PubScreen,     screen,
  836.             WA_Left,          x,
  837.             WA_Top,           y,
  838.             WA_InnerWidth,    width,
  839.             WA_InnerHeight,   height,
  840.             WA_Title,        "Words",
  841.             WA_ScreenTitle,  "Words",
  842.             WA_Flags,         WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_RMBTRAP,
  843.             WA_GimmeZeroZero, TRUE,
  844.             TAG_DONE
  845.         );
  846.  
  847.         Permit();
  848.  
  849.         // render window
  850.  
  851.         if (win) {
  852.  
  853.             if (font)
  854.                 SetFont(win->RPort, font);
  855.  
  856.             win = Message(text, sync, win);
  857.         }
  858.     }
  859.     else
  860.         Permit();
  861.  
  862.     return(win);
  863. }
  864.  
  865.  
  866. /* ---------------------------------- Message ----------------------------------
  867.  
  868.  Show message (assume that window size is sufficient)
  869.  
  870. */
  871.  
  872. struct Window *
  873. Message(text, sync, win)
  874.  
  875. struct Window *win;
  876. UBYTE         *text;
  877. BOOL           sync;
  878. {
  879.     if (win == NULL)
  880.  
  881.         win = OpenMessageWin(text, sync);
  882.  
  883.     else {
  884.  
  885.         struct RastPort *rast = win->RPort;
  886.  
  887.         SetAPen(rast, 0);
  888.         SetRast(rast, 0);
  889.         SetAPen(rast, 1);
  890.  
  891.         Move(rast, 10, 10 + rast->TxBaseline);
  892.  
  893.         Text(rast, text, strlen(text));
  894.  
  895.         if (sync) {
  896.  
  897.             Delay(150);
  898.  
  899.             CloseWindow(win);
  900.  
  901.             win = NULL;
  902.         }
  903.     }
  904.  
  905.     return(win);
  906. }
  907.  
  908.  
  909. /* --------------------------------- ShowSpell ---------------------------------
  910.  
  911.  Show ISpell suggestions. Writes user selection to suggestion.
  912.  
  913. */
  914.  
  915. void
  916. ShowSpell(handle, apiMsg, list, title, suggestion)
  917.  
  918. struct APIClient  *handle;
  919. struct APIMessage *apiMsg;
  920. struct List       *list;
  921. UBYTE             *title;
  922. UBYTE             *suggestion;
  923. {
  924.     struct Node *node;
  925.     UWORD        suggestions;
  926.  
  927.     *suggestion = 0;
  928.  
  929.     for (suggestions = 0, node = list->lh_Head; node->ln_Succ; node = node->ln_Succ)
  930.  
  931.         ++suggestions;
  932.  
  933.     if (suggestions) {
  934.  
  935.         struct Screen *screen;
  936.         struct Window *parent;
  937.  
  938.         screen = apiMsg->api_Instance->api_Screen;
  939.         parent = apiMsg->api_Instance->api_Window;
  940.  
  941.         if (screen && parent) {
  942.  
  943.             struct TextFont *font;
  944.  
  945.             if (font = OpenDiskFont(screen->Font)) {
  946.  
  947.                 APTR visualInfo;
  948.  
  949.                 if (visualInfo = GetVisualInfoA(screen, NULL)) {
  950.  
  951.                     struct Window    *win;
  952.                     struct NewGadget  newGad;
  953.  
  954.                     if (suggestions < 3)
  955.                         suggestions = 3;
  956.  
  957.                     newGad.ng_LeftEdge   = 5;
  958.                     newGad.ng_TopEdge    = 5;
  959.                     newGad.ng_Width      = ComputeX(font, 300);
  960.                     newGad.ng_Height     = ComputeY(font, suggestions * 8) + 4;
  961.                     newGad.ng_GadgetText = NULL;
  962.                     newGad.ng_TextAttr   = screen->Font;
  963.                     newGad.ng_GadgetID   = 0;
  964.                     newGad.ng_Flags      = 0;
  965.                     newGad.ng_VisualInfo = visualInfo;
  966.                     newGad.ng_UserData   = 0;
  967.  
  968.                     win = OpenWindowTags(NULL,
  969.  
  970.                         WA_PubScreen,   screen,
  971.                         WA_Left,        parent->LeftEdge + parent->BorderLeft,
  972.                         WA_Top,         parent->TopEdge  + parent->BorderTop,
  973.                         WA_InnerWidth,  newGad.ng_Width  + 10,
  974.                         WA_InnerHeight, newGad.ng_Height + 10,
  975.                         WA_IDCMP,       LISTVIEWIDCMP | IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_CLOSEWINDOW,
  976.                         WA_Flags,       WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_DEPTHGADGET | WFLG_DRAGBAR,
  977.                         WA_Title,       title,
  978.                         TAG_DONE
  979.                     );
  980.  
  981.                     if (win) {
  982.  
  983.                         struct Gadget *context, *glist;
  984.  
  985.                         glist = NULL;
  986.  
  987.                         newGad.ng_LeftEdge += win->BorderLeft;
  988.                         newGad.ng_TopEdge  += win->BorderTop;
  989.  
  990.                         if (context = CreateContext(&glist)) {
  991.  
  992.                             struct Gadget *gad = CreateGadget(LISTVIEW_KIND, context, &newGad, GTLV_ShowSelected, NULL, GTLV_Labels, list, GTLV_Selected, 0, TAG_DONE);
  993.  
  994.                             if (gad) {
  995.  
  996.                                 struct IntuiMessage *msg;
  997.                                 struct Node         *node;
  998.                                 UWORD                active;
  999.                                 BOOL                 isDoubleClick;
  1000.                                 ULONG                lastSeconds;
  1001.                                 ULONG                lastMicros;
  1002.                                 ULONG                lastSelection;
  1003.                                 ULONG                class;
  1004.  
  1005.                                 AddGList(win, glist, UNDEFINED, UNDEFINED, NULL);
  1006.  
  1007.                                 RefreshGList(glist, win, NULL, UNDEFINED);
  1008.  
  1009.                                 GT_RefreshWindow(win, NULL);
  1010.  
  1011.                                 active = 0;
  1012.  
  1013.                                 do {
  1014.  
  1015.                                     isDoubleClick = FALSE;
  1016.  
  1017.                                     while (!(msg = GT_GetIMsg(win->UserPort)))
  1018.  
  1019.                                         WaitPort(win->UserPort);
  1020.  
  1021.                                     switch (class = msg->Class) {
  1022.  
  1023.                                         case RAWKEY:
  1024.  
  1025.                                             WORD step = (msg->Code == CURSORUP) ? -1 : 1;
  1026.  
  1027.                                             if (node = SearchNode(list, active + step)) {
  1028.  
  1029.                                                 active += step;
  1030.  
  1031.                                                 GT_SetGadgetAttrs(gad, win, NULL, GTLV_Selected, active, GTLV_MakeVisible, active, TAG_DONE);
  1032.                                             }
  1033.  
  1034.                                             break;
  1035.  
  1036.                                         case IDCMP_REFRESHWINDOW:
  1037.  
  1038.                                             BeginRefresh(win);
  1039.  
  1040.                                             EndRefresh(win, TRUE);
  1041.  
  1042.                                             break;
  1043.  
  1044.                                         case IDCMP_VANILLAKEY:
  1045.  
  1046.                                             if (msg->Code == 27)
  1047.                                                 class  = CLOSEWINDOW;
  1048.  
  1049.                                             if (msg->Code == 13)
  1050.                                                 isDoubleClick = TRUE;
  1051.  
  1052.                                             break;
  1053.  
  1054.                                         case IDCMP_GADGETUP:
  1055.  
  1056.                                             active = msg->Code;
  1057.  
  1058.                                             if (active == lastSelection)
  1059.  
  1060.                                                 isDoubleClick = DoubleClick(lastSeconds, lastMicros, msg->Seconds, msg->Micros);
  1061.  
  1062.                                             lastSelection = msg->Code;
  1063.                                             lastSeconds   = msg->Seconds;
  1064.                                             lastMicros    = msg->Micros;
  1065.  
  1066.                                             break;
  1067.                                     }
  1068.  
  1069.                                     GT_ReplyIMsg(msg);
  1070.  
  1071.                                 } while ((class != CLOSEWINDOW) && (isDoubleClick == FALSE));
  1072.  
  1073.                                 if (class != CLOSEWINDOW) {
  1074.  
  1075.                                     if (node = SearchNode(list, active))
  1076.  
  1077.                                         strcpy(suggestion, node->ln_Name);
  1078.                                 }
  1079.                             }
  1080.                         }
  1081.  
  1082.                         CloseWindow(win);
  1083.                     }
  1084.  
  1085.                     FreeVisualInfo(visualInfo);
  1086.                 }
  1087.             }
  1088.         }
  1089.     }
  1090. }
  1091.  
  1092. ///
  1093. /// "ispell"
  1094.  
  1095. /* --------------------------------- RunISpell ---------------------------------
  1096.  
  1097.  Run ISpell (if not yet running); increase user count
  1098.  
  1099. */
  1100.  
  1101. struct MsgPort *
  1102. RunISpell()
  1103. {
  1104.     struct MsgPort    *port;
  1105.     struct AutoConfig *autoConfig;
  1106.  
  1107.     // serialize access within this library
  1108.  
  1109.     ObtainSemaphore(&ISpellSemaphore);
  1110.  
  1111.     Forbid();
  1112.  
  1113.     // ISpell running/starting already ?
  1114.  
  1115.     if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  1116.  
  1117.         ObtainSemaphore(&autoConfig->Semaphore);
  1118.  
  1119.         ++autoConfig->Users;
  1120.  
  1121.         ReleaseSemaphore(&autoConfig->Semaphore);
  1122.  
  1123.         port = WaitForISpell();
  1124.  
  1125.         if (port == NULL)
  1126.  
  1127.             Message("STARTUP FAILURE", TRUE, NULL);
  1128.     }
  1129.     else {
  1130.  
  1131.         port = FindPort("IRexxSpell");
  1132.  
  1133.         // run ISpell on our own ?
  1134.  
  1135.         if (port == NULL) {
  1136.  
  1137.             struct AutoConfig *autoConfig;
  1138.  
  1139.             if (autoConfig = (struct AutoConfig *)AllocVec(sizeof(struct AutoConfig), MEMF_PUBLIC | MEMF_CLEAR)) {
  1140.  
  1141.                 static UBYTE hashfile[255], language[255], personal[255], options[255], command[255], home[255];
  1142.  
  1143.                 __aligned struct FileInfoBlock fib = { 0 };
  1144.  
  1145.                 autoConfig->Semaphore.ss_Link.ln_Name = "WORDS";
  1146.                 autoConfig->Semaphore.ss_Link.ln_Pri  = 0;
  1147.                 autoConfig->Users                     = 1;
  1148.  
  1149.                 AddSemaphore   (&autoConfig->Semaphore);
  1150.                 ObtainSemaphore(&autoConfig->Semaphore);
  1151.  
  1152.                 // set default language
  1153.  
  1154.                 strcpy(language, "english");
  1155.  
  1156.                 // set default options
  1157.  
  1158.                 strcpy(options,  "-W 1");
  1159.  
  1160.                 // did user provide special startup arguments for ISpell ?
  1161.  
  1162.                 if (GetVar("WORDS.prefs", command, sizeof(command), GVF_GLOBAL_ONLY) != -1) {
  1163.  
  1164.                     if (*command) {
  1165.  
  1166.                         struct RDArgs *rdArgs, *args;
  1167.  
  1168.                         if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {
  1169.  
  1170.                             ULONG argArray[] = { 0, 0, 0, 0, 0 };
  1171.  
  1172.                             // make LF-terminated copy of command string (required by dos/readArgs):
  1173.  
  1174.                             strcat(command, "\12");
  1175.  
  1176.                             rdArgs->RDA_Source.CS_Buffer = command;
  1177.                             rdArgs->RDA_Source.CS_Length = strlen(command);
  1178.                             rdArgs->RDA_Source.CS_CurChr = 0;
  1179.                             rdArgs->RDA_DAList           = 0;
  1180.                             rdArgs->RDA_Buffer           = NULL;
  1181.  
  1182.                             if (args = ReadArgs("LANGUAGE/K,QUICKCHECK/K,BOUNDARYCHARS/K,BEEP/N,ARGS/F", argArray, rdArgs)) {
  1183.  
  1184.                                 if (argArray[0])             // LANGUAGE/K
  1185.                                     strcpy(language, (UBYTE *)argArray[0]);
  1186.  
  1187.                                 if (argArray[4])             // ARGS/F
  1188.                                     strcpy(options,  (UBYTE *)argArray[4]);
  1189.  
  1190.                                 FreeArgs(args);
  1191.                             }
  1192.                             else
  1193.                                 Message("Syntax error", TRUE, NULL);
  1194.  
  1195.                             FreeDosObject(DOS_RDARGS, rdArgs);
  1196.                         }
  1197.                     }
  1198.                 }
  1199.  
  1200.                 // personal dictionary file (for new words)
  1201.  
  1202.                 sprintf(personal, "/ispell/lib/.ispell_%s", language);
  1203.  
  1204.                 // standard dictionary file
  1205.  
  1206.                 sprintf(hashfile, "ispell:lib/%s.hash", language);
  1207.  
  1208.                 // dictionary available ?
  1209.  
  1210.                 if (FileInfo(hashfile, &fib)) {
  1211.  
  1212.                     ULONG avail = AvailMem(MEMF_LARGEST);
  1213.  
  1214.                     if (fib.fib_Size < avail) {
  1215.  
  1216.                         struct Window *win = Message("Starting ISpell...", FALSE, NULL);
  1217.  
  1218.                         // $HOME required by ISpell 3.1.18
  1219.  
  1220.                         if (GetVar("HOME", home, sizeof(home), GVF_GLOBAL_ONLY) == -1)
  1221.  
  1222.                             SetVar("HOME", "sys:", -1, GVF_GLOBAL_ONLY);
  1223.  
  1224.                         // run ISpell in host mode
  1225.  
  1226.                         if (*options)
  1227.                             sprintf(command, "ispell:bin/ispell -d%s -p%s -r %s", hashfile, personal, options);
  1228.                         else
  1229.                             sprintf(command, "ispell:bin/ispell -d%s -p%s -r", hashfile, personal);
  1230.  
  1231.                         // wait for completion of ISpell startup
  1232.  
  1233.                         if (DoExecute(command, TRUE, "NIL:", "ispell:", 32768, 1))
  1234.  
  1235.                             port = WaitForISpell();
  1236.  
  1237.                         if (port == NULL)
  1238.  
  1239.                             Message("STARTUP FAILURE", TRUE, win);
  1240.  
  1241.                         else if (win)
  1242.  
  1243.                             CloseWindow(win);
  1244.                     }
  1245.                     else {
  1246.  
  1247.                         if (fib.fib_Size <= AvailMem(MEMF_ANY))
  1248.                             Message("Out of RAM (fragmentation error)", TRUE, NULL);
  1249.                         else
  1250.                             Message("Out of RAM", TRUE, NULL);
  1251.                     }
  1252.                 }
  1253.                 else
  1254.                     Message("Dictionary not found", TRUE, NULL);
  1255.  
  1256.                 ReleaseSemaphore(&autoConfig->Semaphore);
  1257.  
  1258.                 if (port == NULL)
  1259.  
  1260.                     StopISpell();
  1261.             }
  1262.         }
  1263.     }
  1264.  
  1265.     Permit();
  1266.  
  1267.     ReleaseSemaphore(&ISpellSemaphore);
  1268.  
  1269.     return(port);
  1270. }
  1271.  
  1272.  
  1273. /* ------------------------------- WaitForISpell -------------------------------
  1274.  
  1275.  Wait for ISpell
  1276.  
  1277. */
  1278.  
  1279. struct MsgPort *
  1280. WaitForISpell()
  1281. {
  1282.     struct MsgPort *port;
  1283.     UWORD           try;
  1284.  
  1285.     for (try = 50; try-- && (port == NULL); Delay(10)) {
  1286.  
  1287.         Forbid();
  1288.  
  1289.         port = FindPort("IRexxSpell");
  1290.  
  1291.         Permit();
  1292.     }
  1293.  
  1294.     return(port);
  1295. }
  1296.  
  1297.  
  1298. /* -------------------------------- StopISpell ---------------------------------
  1299.  
  1300.  Decrement ISpell user count. Unload ISpell if unused.
  1301.  
  1302. */
  1303.  
  1304. void
  1305. StopISpell()
  1306. {
  1307.     struct AutoConfig *autoConfig;
  1308.  
  1309.     Forbid();
  1310.  
  1311.     if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  1312.  
  1313.         ObtainSemaphore(&autoConfig->Semaphore);
  1314.  
  1315.         if (autoConfig->Users)
  1316.  
  1317.             --autoConfig->Users;
  1318.  
  1319.         ReleaseSemaphore(&autoConfig->Semaphore);
  1320.  
  1321.         if (autoConfig->Users == 0) {
  1322.  
  1323.             struct MsgPort *port;
  1324.  
  1325.             RemSemaphore    (&autoConfig->Semaphore);
  1326.             ObtainSemaphore (&autoConfig->Semaphore);
  1327.             ReleaseSemaphore(&autoConfig->Semaphore);
  1328.  
  1329.             if (port = CreateMsgPort()) {
  1330.  
  1331.                 if (autoConfig->Words) {
  1332.  
  1333.                     struct Screen *screen;
  1334.                     ULONG          lock;
  1335.  
  1336.                     // critical stuff (barely legal - don't do this at home, kids ;-)
  1337.  
  1338.                     lock = LockIBase(0);
  1339.  
  1340.                     // find frontmost screen
  1341.  
  1342.                     screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen;
  1343.  
  1344.                     // keep system frozen
  1345.  
  1346.                     Forbid();
  1347.  
  1348.                     // enable rendering functions
  1349.  
  1350.                     UnlockIBase(lock);
  1351.  
  1352.                     if (rtEZRequestTags("Save new words to user dictionary ?", "_SAVE|_cancel", NULL, NULL, RT_Underscore, '_', RTEZ_ReqTitle, "Words", RT_Screen, screen, TAG_DONE) == 1)
  1353.  
  1354.                         SendRexxCommand("IRexxSpell", "ADD A", port, NULL);
  1355.  
  1356.                     Permit();
  1357.                 }
  1358.  
  1359.                 SendRexxCommand("IRexxSpell", "EXIT", port, NULL);
  1360.  
  1361.                 DeleteMsgPort(port);
  1362.             }
  1363.  
  1364.             FreeVec(autoConfig);
  1365.         }
  1366.     }
  1367.  
  1368.     Permit();
  1369. }
  1370.  
  1371. ///
  1372. /// "arexx"
  1373.  
  1374. /* ---------------------------------- SendRexxCommand -------------------------
  1375.  
  1376.  Send ARexx message & wait for answer. Return pointer to result or NULL.
  1377.  
  1378. */
  1379.  
  1380. UBYTE *
  1381. SendRexxCommand(port, cmd, replyPort, buffer)
  1382.  
  1383. struct MsgPort *replyPort;
  1384. UBYTE          *cmd, *port, *buffer;
  1385. {
  1386.     struct MsgPort *rexxport;
  1387.  
  1388.     Forbid();
  1389.  
  1390.     if (rexxport = FindPort(port)) {
  1391.  
  1392.         struct RexxMsg *rexxMsg, *answer;
  1393.  
  1394.         if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {
  1395.  
  1396.             if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {
  1397.  
  1398.                 static ULONG result;
  1399.  
  1400.                 rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;
  1401.  
  1402.                 PutMsg(rexxport, &rexxMsg->rm_Node);
  1403.  
  1404.                 do {
  1405.  
  1406.                     WaitPort(replyPort);
  1407.  
  1408.                     if (answer = (struct RexxMsg *)GetMsg(replyPort))
  1409.                         result = answer->rm_Result1;
  1410.  
  1411.                 } while (!answer);
  1412.  
  1413.                 Permit();
  1414.  
  1415.                 if (answer->rm_Result1 == RC_OK) {
  1416.  
  1417.                     if (answer->rm_Result2) {
  1418.  
  1419.                         if (buffer)
  1420.                             strcpy(buffer, (char *)answer->rm_Result2);
  1421.  
  1422.                         DeleteArgstring((char *)answer->rm_Result2);
  1423.                     }
  1424.                 }
  1425.  
  1426.                 DeleteArgstring((char *)ARG0(answer));
  1427.  
  1428.                 DeleteRexxMsg(answer);
  1429.  
  1430.                 return(&result);
  1431.             }
  1432.         }
  1433.     }
  1434.  
  1435.     Permit();
  1436.  
  1437.     return(NULL);
  1438. }
  1439.  
  1440. ///
  1441. /// "spellchecking"
  1442.  
  1443. /* ---------------------------- CheckWordCurrent -------------------------------
  1444.  
  1445.  Check word under or before cursor.
  1446.  
  1447. */
  1448.  
  1449. void
  1450. CheckWordCurrent(handle, apiMsg, suggestions)
  1451.  
  1452. struct APIClient  *handle;
  1453. struct APIMessage *apiMsg;
  1454. BOOL               suggestions;
  1455. {
  1456.     struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  1457.  
  1458.     UBYTE *text, *last;
  1459.     UWORD  column;
  1460.  
  1461.     text = config->CurrentBuffer + config->Column;
  1462.     last = config->CurrentBuffer + config->CurrentLen;
  1463.  
  1464.     // find a character of the last word (ignore two white space characters if necessary)
  1465.  
  1466.     column = config->Column;
  1467.  
  1468.     if (column && (IsLetter[*text] == FALSE)) {
  1469.  
  1470.         --column;
  1471.         --text;
  1472.     }
  1473.  
  1474.     if (column && (IsLetter[*text] == FALSE)) {
  1475.  
  1476.         --column;
  1477.         --text;
  1478.     }
  1479.  
  1480.     // any character found ?
  1481.  
  1482.     if (IsLetter[*text]) {
  1483.  
  1484.         UWORD  wordLen;
  1485.         UBYTE *check;
  1486.  
  1487.         // find beginning of word
  1488.  
  1489.         while (column) {
  1490.  
  1491.             if (IsLetter[*(text - 1)]) {
  1492.  
  1493.                 --text;
  1494.                 --column;
  1495.             }
  1496.             else
  1497.                 break;
  1498.         }
  1499.  
  1500.         // find end of word
  1501.  
  1502.         for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) {
  1503.  
  1504.             // end of word detected ?
  1505.  
  1506.             if (IsLetter[*check] == FALSE) {
  1507.  
  1508.                 // verify end of word (boundary characters can be space OR letter)
  1509.  
  1510.                 if (IsBoundary[*check]) {
  1511.  
  1512.                     if ((check + 1) < last) {
  1513.  
  1514.                         if (IsLetter[*(check + 1)] == FALSE)
  1515.  
  1516.                             break;
  1517.                     }
  1518.                     else
  1519.                         break;
  1520.                 }
  1521.                 else
  1522.                     break;
  1523.             }
  1524.         }
  1525.  
  1526.         // check the word ?
  1527.  
  1528.         if ((wordLen > 1) && (wordLen <= 100)) {
  1529.  
  1530.             if (suggestions) {
  1531.  
  1532.                 UBYTE replacement[128];
  1533.  
  1534.                 if (CheckWord(handle, apiMsg, text, wordLen, replacement) == FALSE) {
  1535.  
  1536.                     if (*replacement)
  1537.  
  1538.                         Replace(handle, apiMsg, column, wordLen, replacement);
  1539.                 }
  1540.             }
  1541.             else if (CheckWord(handle, apiMsg, text, wordLen, NULL) == FALSE)
  1542.  
  1543.                 Beep(Volume);
  1544.         }
  1545.     }
  1546. }
  1547.  
  1548.  
  1549. /* --------------------------------- CheckWord ---------------------------------
  1550.  
  1551.  Check <word>. Handles approx. 100 characters. Return TRUE if ok. Writes
  1552.  replacement to <suggestion> (unless NULL).
  1553.  
  1554. */
  1555.  
  1556. BOOL
  1557. CheckWord(handle, apiMsg, word, len, suggestion)
  1558.  
  1559. struct APIClient  *handle;
  1560. struct APIMessage *apiMsg;
  1561. UBYTE             *word;
  1562. UWORD              len;
  1563. UBYTE             *suggestion;
  1564. {
  1565.     UBYTE command[128];
  1566.  
  1567.     BOOL  ok;
  1568.  
  1569.     ok = TRUE;
  1570.  
  1571.     if (suggestion)
  1572.  
  1573.         *suggestion = 0;
  1574.  
  1575.     // build command line for ISpell
  1576.  
  1577.      movmem(word, command, len);
  1578.  
  1579.      command[len] = 0;
  1580.  
  1581.     // suggestion requested ?
  1582.  
  1583.     if (suggestion) {
  1584.  
  1585.         UBYTE *result = ((struct BufferHandle *)handle)->Buffer;
  1586.  
  1587.         // send CHECK command to recieve detailed information (suggestions)
  1588.  
  1589.         strins(command, "CHECK ");
  1590.  
  1591.         if (SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer)) {
  1592.  
  1593.             if ((result[0] == 'o') && (result[1] == 'k')) {
  1594.  
  1595.                 ok = TRUE;
  1596.  
  1597.                 DisplayBeep(NULL);
  1598.             }
  1599.             else {
  1600.  
  1601.                 switch (result[0]) {
  1602.  
  1603.                     case '&':                        // spelling error, near misses available
  1604.  
  1605.                         UBYTE *end;
  1606.  
  1607.                         ok = FALSE;
  1608.  
  1609.                         if (end = strchr(result, ':')) {
  1610.  
  1611.                             // convert ISpell result string (suggestions) to exec list
  1612.  
  1613.                             struct List  list;
  1614.                             struct Node *node;
  1615.                             struct Node *next;
  1616.                             UWORD        count;
  1617.  
  1618.                             NewList(&list);
  1619.  
  1620.                             count = 0;
  1621.  
  1622.                             while (*end) {
  1623.  
  1624.                                 UBYTE *start;
  1625.  
  1626.                                 do {
  1627.  
  1628.                                     *end++ = 0;
  1629.  
  1630.                                 } while (*end && ((*end == ',') || (*end == ' ')));
  1631.  
  1632.                                 start = end;
  1633.  
  1634.                                 while (*end && (*end != 32) && (*end != ','))
  1635.  
  1636.                                     ++end;
  1637.  
  1638.                                 if (node = (struct Node *)AllocVec(sizeof(struct Node), MEMF_ANY | MEMF_CLEAR)) {
  1639.  
  1640.                                     // node name: we point directly into the result string
  1641.  
  1642.                                     node->ln_Name = start;
  1643.  
  1644.                                     AddTail(&list, node);
  1645.  
  1646.                                     ++count;
  1647.                                 }
  1648.                             }
  1649.  
  1650.                             if (count) {
  1651.  
  1652.                                 UBYTE title[128];
  1653.  
  1654.                                 // build window title
  1655.  
  1656.                                 movmem(word, title, len);
  1657.  
  1658.                                 title[len] = 0;
  1659.  
  1660.                                 // show suggestions
  1661.  
  1662.                                 ShowSpell(handle, apiMsg, &list, title, suggestion);
  1663.  
  1664.                                 // free list of suggestions
  1665.  
  1666.                                 for (node = list.lh_Head; next = node->ln_Succ; node = next)
  1667.  
  1668.                                     FreeVec(node);
  1669.                             }
  1670.                             else
  1671.                                 DisplayBeep(NULL);
  1672.                         }
  1673.                         else
  1674.                             DisplayBeep(NULL);
  1675.  
  1676.                         break;
  1677.  
  1678.                     case '?':                        // spelling error, no near misses
  1679.  
  1680.                         ok = FALSE;
  1681.  
  1682.                         DisplayBeep(NULL);
  1683.  
  1684.                         break;
  1685.  
  1686.                     case '*':                        // valid word
  1687.                     case '+':                        // valid after affix removal
  1688.                     case '-':                        // valid compound
  1689.  
  1690.                         ok = TRUE;
  1691.  
  1692.                         break;
  1693.  
  1694.                     default:
  1695.  
  1696.                         ok = FALSE;
  1697.  
  1698.                         DisplayBeep(0);
  1699.                 }
  1700.             }
  1701.         }
  1702.     }
  1703.     else {
  1704.  
  1705.         UBYTE *result = ((struct BufferHandle *)handle)->Buffer;
  1706.  
  1707.         strins(command, "QUICKCHECK ");
  1708.  
  1709.         if (SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer)) {
  1710.  
  1711.             if ((result[0] == 'o') && (result[1] == 'k'))
  1712.                 ok = TRUE;
  1713.             else
  1714.                 ok = FALSE;
  1715.         }
  1716.     }
  1717.  
  1718.     return(ok);
  1719. }
  1720.  
  1721.  
  1722. /* --------------------------------- LearnWord ---------------------------------
  1723.  
  1724.  Learn word <word>. Handles approx. 100 characters. Uncapitalize word if <smart>
  1725.  is TRUE.
  1726.  
  1727. */
  1728.  
  1729. void
  1730. LearnWord(handle, apiMsg, new, len, smart)
  1731.  
  1732. struct APIClient  *handle;
  1733. struct APIMessage *apiMsg;
  1734. UBYTE             *new;
  1735. UWORD              len;
  1736. BOOL               smart;
  1737. {
  1738.     struct AutoConfig *autoConfig;
  1739.  
  1740.     Forbid();
  1741.  
  1742.     if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  1743.  
  1744.         UBYTE command[128];
  1745.  
  1746.         ObtainSemaphore(&autoConfig->Semaphore);
  1747.  
  1748.         ++autoConfig->Words;
  1749.  
  1750.         ReleaseSemaphore(&autoConfig->Semaphore);
  1751.  
  1752.         movmem(new, command, len);
  1753.  
  1754.         command[len] = 0;
  1755.  
  1756.         if (smart)
  1757.             Uncapitalize(command);
  1758.  
  1759.         strins(command, "QUICKADD ");
  1760.  
  1761.         SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer);
  1762.     }
  1763.     else
  1764.         Message("Communication error (semaphore not found)", TRUE, NULL);
  1765.  
  1766.     Permit();
  1767. }
  1768.  
  1769.  
  1770. /* ------------------------------ LearnWordCurrent -----------------------------
  1771.  
  1772.  Learn word under cursor. Uncapitalize word if <smart> is TRUE.
  1773.  
  1774. */
  1775.  
  1776. void
  1777. LearnWordCurrent(handle, apiMsg, smart)
  1778.  
  1779. struct APIClient  *handle;
  1780. struct APIMessage *apiMsg;
  1781. BOOL               smart;
  1782. {
  1783.     struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  1784.  
  1785.     UBYTE *text, *last;
  1786.     UWORD  column;
  1787.  
  1788.     text = config->CurrentBuffer + config->Column;
  1789.     last = config->CurrentBuffer + config->CurrentLen;
  1790.  
  1791.     // find a character of the last word (ignore two white space characters if necessary)
  1792.  
  1793.     column = config->Column;
  1794.  
  1795.     if (column && (IsLetter[*text] == FALSE)) {
  1796.  
  1797.         --column;
  1798.         --text;
  1799.     }
  1800.  
  1801.     if (column && (IsLetter[*text] == FALSE)) {
  1802.  
  1803.         --column;
  1804.         --text;
  1805.     }
  1806.  
  1807.     // any character found ?
  1808.  
  1809.     if (IsLetter[*text]) {
  1810.  
  1811.         UWORD  wordLen;
  1812.         UBYTE *check;
  1813.  
  1814.         // find beginning of word
  1815.  
  1816.         while (column) {
  1817.  
  1818.             if (IsLetter[*(text - 1)]) {
  1819.  
  1820.                 --text;
  1821.                 --column;
  1822.             }
  1823.             else
  1824.                 break;
  1825.         }
  1826.  
  1827.         // find end of word
  1828.  
  1829.         for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) {
  1830.  
  1831.             // end of word detected ?
  1832.  
  1833.             if (IsLetter[*check] == FALSE) {
  1834.  
  1835.                 // verify end of word (boundary characters can be space OR letter)
  1836.  
  1837.                 if (IsBoundary[*check]) {
  1838.  
  1839.                     if ((check + 1) < last) {
  1840.  
  1841.                         if (IsLetter[*(check + 1)] == FALSE)
  1842.  
  1843.                             break;
  1844.                     }
  1845.                     else
  1846.                         break;
  1847.                 }
  1848.                 else
  1849.                     break;
  1850.             }
  1851.         }
  1852.  
  1853.         // learn the word ?
  1854.  
  1855.         if ((wordLen > 1) && (wordLen <= 100))
  1856.  
  1857.             LearnWord(handle, apiMsg, text, wordLen, smart);
  1858.     }
  1859. }
  1860.  
  1861.  
  1862. /* ------------------------------ IgnoreWordCurrent ----------------------------
  1863.  
  1864.  Ignore word under cursor. Uncapitalize word if <smart> is TRUE.
  1865.  
  1866. */
  1867.  
  1868. void
  1869. IgnoreWordCurrent(handle, apiMsg, smart)
  1870.  
  1871. struct APIClient  *handle;
  1872. struct APIMessage *apiMsg;
  1873. BOOL               smart;
  1874. {
  1875.     struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  1876.  
  1877.     UBYTE *text, *last;
  1878.     UWORD  column;
  1879.  
  1880.     text = config->CurrentBuffer + config->Column;
  1881.     last = config->CurrentBuffer + config->CurrentLen;
  1882.  
  1883.     // find a character of the last word (ignore two white space characters if necessary)
  1884.  
  1885.     column = config->Column;
  1886.  
  1887.     if (column && (IsLetter[*text] == FALSE)) {
  1888.  
  1889.         --column;
  1890.         --text;
  1891.     }
  1892.  
  1893.     if (column && (IsLetter[*text] == FALSE)) {
  1894.  
  1895.         --column;
  1896.         --text;
  1897.     }
  1898.  
  1899.     // any character found ?
  1900.  
  1901.     if (IsLetter[*text]) {
  1902.  
  1903.         UWORD  wordLen;
  1904.         UBYTE *check;
  1905.  
  1906.         // find beginning of word
  1907.  
  1908.         while (column) {
  1909.  
  1910.             if (IsLetter[*(text - 1)]) {
  1911.  
  1912.                 --text;
  1913.                 --column;
  1914.             }
  1915.             else
  1916.                 break;
  1917.         }
  1918.  
  1919.         // find end of word
  1920.  
  1921.         for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) {
  1922.  
  1923.             // end of word detected ?
  1924.  
  1925.             if (IsLetter[*check] == FALSE) {
  1926.  
  1927.                 // verify end of word (boundary characters can be space OR letter)
  1928.  
  1929.                 if (IsBoundary[*check]) {
  1930.  
  1931.                     if ((check + 1) < last) {
  1932.  
  1933.                         if (IsLetter[*(check + 1)] == FALSE)
  1934.  
  1935.                             break;
  1936.                     }
  1937.                     else
  1938.                         break;
  1939.                 }
  1940.                 else
  1941.                     break;
  1942.             }
  1943.         }
  1944.  
  1945.         // ignore the word ?
  1946.  
  1947.         if ((wordLen > 1) && (wordLen <= 100)) {
  1948.  
  1949.             UBYTE command[128];
  1950.  
  1951.             movmem(text, command, wordLen);
  1952.  
  1953.             command[wordLen] = 0;
  1954.  
  1955.             if (smart)
  1956.                 Uncapitalize(command);
  1957.  
  1958.             strins(command, "ACCEPT ");
  1959.  
  1960.             SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer);
  1961.         }
  1962.     }
  1963. }
  1964.  
  1965. ///
  1966. /// "dice"
  1967.  
  1968. #ifdef _DCC
  1969.  
  1970. /* ------------------------------- CreateGadget --------------------------------
  1971.  
  1972.  Stub function (missing in linker lib of DICE 2.0.54r)
  1973.  
  1974. */
  1975.  
  1976. struct Gadget *
  1977. CreateGadget(ULONG kind, struct Gadget *previous, struct NewGadget *newgad, ULONG firstTag, ...)
  1978. {
  1979.     return(CreateGadgetA(kind, previous, newgad, (struct TagItem *)&firstTag));
  1980. }
  1981.  
  1982. /* ----------------------------- GT_SetGadgetAttrs -----------------------------
  1983.  
  1984.  Stub function (missing in linker lib of DICE 2.0.54r)
  1985.  
  1986. */
  1987.  
  1988. void
  1989. GT_SetGadgetAttrs(struct Gadget *gad, struct Window *win, struct Requester *req, ULONG firstTag, ...)
  1990. {
  1991.     GT_SetGadgetAttrsA(gad, win, req, (struct TagItem *)&firstTag);
  1992. }
  1993.  
  1994. /* ------------------------------- DrawBevelBox --------------------------------
  1995.  
  1996.  Stub function (missing in linker lib of DICE 2.0.54r)
  1997.  
  1998. */
  1999.  
  2000. void
  2001. DrawBevelBox(rport, left, top, width, height, firsttag, ...)
  2002.  
  2003. struct RastPort *rport;
  2004. LONG             left, top, width, height;
  2005. Tag              firsttag;
  2006. {
  2007.     DrawBevelBoxA(rport, left, top, width, height, (struct TagItem *)&firsttag);
  2008. }
  2009.  
  2010. #endif
  2011.  
  2012. ///
  2013.