home *** CD-ROM | disk | FTP | other *** search
Wrap
/* ----------------------------------------------------------------------------- ApiLib ⌐1997 Dietmar Eilert ISpell interface for GoldED 4, ⌐1996 Dietmar Eilert. Dice: DMAKE ------------------------------------------------------------------------------- */ #include "defs.h" /// "prototypes" // library functions Prototype LibCall struct APIClient *APIMountClient(__A0 struct APIMessage *, __A1 char *); Prototype LibCall void APICloseClient(__A0 struct APIClient *, __A1 struct APIMessage *); Prototype LibCall void APIBriefClient(__A0 struct APIClient *, __A1 struct APIMessage *); Prototype LibCall void APIFree (__A0 struct APIClient *, __A1 struct APIOrder *); // private functions Prototype struct FileInfoBlock *FileInfo(UBYTE *, struct FileInfoBlock *); Prototype void Beep(UWORD); Prototype struct Node *SearchNode(struct List *, UWORD); Prototype UWORD ComputeX(struct TextFont *, UWORD); Prototype UWORD ComputeY(struct TextFont *, UWORD); Prototype void GetVScreenSize(struct Screen *, UWORD *, UWORD *); Prototype struct MsgPort *RunISpell(void); Prototype struct Window *OpenMessageWin(UBYTE *, BOOL); Prototype struct Window *Message (UBYTE *, BOOL, struct Window *); Prototype BOOL ExistPath(UBYTE *); Prototype UBYTE *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *, UBYTE *); Prototype LONG CommandSpell (struct APIClient *, struct APIMessage *, ULONG *); Prototype void IgnoreWordCurrent(struct APIClient *, struct APIMessage *, BOOL); Prototype BOOL CheckWord (struct APIClient *, struct APIMessage *, UBYTE *, UWORD, UBYTE *); Prototype void CheckWordCurrent (struct APIClient *, struct APIMessage *, BOOL); Prototype void LearnWord (struct APIClient *, struct APIMessage *, UBYTE *, UWORD, BOOL); Prototype void LearnWordCurrent (struct APIClient *, struct APIMessage *, BOOL); Prototype void Dispatch (struct APIClient *, struct APIMessage *); Prototype void ShowSpell (struct APIClient *, struct APIMessage *, struct List *, UBYTE *, UBYTE *); Prototype void Replace (struct APIClient *, struct APIMessage *, UWORD, UWORD, UBYTE *); Prototype BOOL DoExecute (UBYTE *, BOOL, UBYTE *, UBYTE *, ULONG, WORD); Prototype struct MsgPort *WaitForISpell(void); Prototype void StopISpell(void); Prototype void Uncapitalize(UBYTE *); // private defines #define UNDEFINED ((UWORD)~0) // buffer handles are allocated to store text-local data struct BufferHandle { struct APIClient APIClient; // API handle struct APIOrder APIOrder; // API order struct APIModifyRequest APIModifyRequest; // API modification request UBYTE APIData[4096]; // API buffer UBYTE Buffer [4096]; // private ARexx buffer struct MsgPort *Port; // private ARexx message port }; // AutoConfig semaphore coordinates ISpell usage struct AutoConfig { struct SignalSemaphore Semaphore; UWORD Users; // ISpell users ULONG Words; // new words counter }; /// /// "globals" struct AutoConfig *AutoConfig; struct SignalSemaphore ISpellSemaphore; BOOL IsLetter [256]; // valid letters BOOL IsBoundary[256]; // valid letter OR space (depends on context) BOOL IsTrigger [256]; // trigger characters BOOL Online; // online spelll checking ? UWORD Volume; // beep volume /// /// "library functions" LibCall struct APIClient * APIMountClient(__A0 struct APIMessage *apiMsg, __A1 char *args) { struct BufferHandle *bufferHandle = NULL; // start ISpell (if not yet running) if (RunISpell()) { // allocate local data for this editor window (extended APIClient structure) if (bufferHandle = (struct BufferHandle *)AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) { if (bufferHandle->Port = CreateMsgPort()) { static UBYTE *apiCommands[] = { "SPELL PREV/S,NEXT/S,ADD/S,CURRENT/S,ASK/S", NULL }; // describe our features bufferHandle->APIClient.api_APIVersion = API_INTERFACE_VERSION; bufferHandle->APIClient.api_Version = 2; bufferHandle->APIClient.api_Name = "Words"; bufferHandle->APIClient.api_Info = "ISpell interface for GoldED"; bufferHandle->APIClient.api_Commands = NULL; bufferHandle->APIClient.api_Serial = 0; bufferHandle->APIClient.api_Classes = API_CLASS_SYSTEM | API_CLASS_COMMAND; bufferHandle->APIClient.api_Area = NULL; if (Online) bufferHandle->APIClient.api_Classes |= API_CLASS_KEY; } } } return((struct APIClient *)bufferHandle); } LibCall void APICloseClient(__A0 struct APIClient *handle, __A1 struct APIMessage *apiMsg) { struct BufferHandle *bufferHandle = (struct BufferHandle *)handle; // free local data (allocated for a specific editor window) if (bufferHandle->Port) DeleteMsgPort(bufferHandle->Port); FreeVec(bufferHandle); StopISpell(); } LibCall void APIBriefClient(__A0 struct APIClient *handle, __A1 struct APIMessage *apiMsg) { struct APIMessage *msg; // handle host's command notify for (msg = apiMsg; msg; msg = msg->api_Next) { if (msg->api_State == API_STATE_NOTIFY) { // process message sent by GoldED switch (msg->api_Class) { // did user press a key ? case API_CLASS_KEY: struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment; switch (msg->api_Action) { case API_ACTION_VANILLAKEY: // trigger key used ? if (Online) { if (IsTrigger[(UWORD)apiMsg->api_Data]) { // spellcheck last word CheckWordCurrent(handle, apiMsg, FALSE); } } break; } break; // did user enter a command ? case API_CLASS_COMMAND: switch (msg->api_Action) { case API_ACTION_COMMAND: Dispatch(handle, msg); break; default: msg->api_Error = API_ERROR_UNKNOWN; } break; default: msg->api_Error = API_ERROR_UNKNOWN; } } } } LibCall void APIFree(__A0 struct APIClient *handle, __A1 struct APIOrder *apiOrder) { ; } /// /// "dispatcher" /* --------------------------------- Dispatch ---------------------------------- Handle command sent by GoldED. This API client adds commands (currently only the SPELL command) to GoldED's command set. This function is called if GoldED detects an command that needs to be handled by an API client (e.g. us). */ void Dispatch(handle, apiMsg) struct APIClient *handle; struct APIMessage *apiMsg; { struct RDArgs *rdArgs; if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) { // table of supported commands, handlers & template strings static struct parser { char *command; LONG (*handler)(struct APIClient *, struct APIMessage *, ULONG *); char *template; } parser[] = { "SPELL", (APTR)CommandSpell, "CURRENT/S,ADD/S,IGNORE/S,NOCASE/S,SAVE/S", NULL }; ULONG argArray[10]; UBYTE buffer [80]; ULONG n; memset(argArray, 0, sizeof(argArray)); // make LF-terminated copy of command string (required by dos/readArgs): strncpy(buffer, apiMsg->api_Command, sizeof(buffer)); strcat(buffer, "\12"); for (n = 0; parser[n].command; ++n) { if (memcmp(buffer, parser[n].command, strlen(parser[n].command)) == 0) { UBYTE *arguments = buffer + strlen(parser[n].command); // tell host that message has been consumed by us (hide it from other API clients) apiMsg->api_State = API_STATE_CONSUMED; rdArgs->RDA_Source.CS_Buffer = arguments; rdArgs->RDA_Source.CS_Length = strlen(arguments); rdArgs->RDA_Source.CS_CurChr = 0; rdArgs->RDA_DAList = 0; rdArgs->RDA_Buffer = NULL; // arguments expected ? if (parser[n].template) { if (ReadArgs(parser[n].template, argArray, rdArgs)) { apiMsg->api_RC = (*parser[n].handler)(handle, apiMsg, argArray); FreeArgs(rdArgs); } else { static UBYTE error[80 + 1]; apiMsg->api_RC = RC_WARN; apiMsg->api_CommandError = error; Fault(IoErr(), "IoErr()", error, 80); } } else apiMsg->api_RC = (*parser[n].handler)(handle, apiMsg, argArray); } } FreeDosObject(DOS_RDARGS, rdArgs); } } /* -------------------------------- CommandSpell ------------------------------- SPELL command handler template: CURRENT/S,ADD/S,IGNORE/S,NOCASE/S,SAVE/S */ LONG CommandSpell(handle, apiMsg, argArray) struct APIClient *handle; struct APIMessage *apiMsg; ULONG *argArray; { if (argArray[0]) // CURRENT/S CheckWordCurrent(handle, apiMsg, TRUE); if (argArray[1]) // ADD/S LearnWordCurrent(handle, apiMsg, argArray[3]); if (argArray[2]) // IGNORE/S IgnoreWordCurrent(handle, apiMsg, argArray[3]); if (argArray[4]) { // SAVE/S struct AutoConfig *autoConfig; Forbid(); if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) { ObtainSemaphore(&autoConfig->Semaphore); // save user dictionary SendRexxCommand("IRexxSpell", "ADD A", ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer); // reset global new words counter autoConfig->Words = 0; ReleaseSemaphore(&autoConfig->Semaphore); } else Message("Communication error (semaphore not found)", TRUE, NULL); Permit(); } return(RC_OK); } /// /// "misc" /* --------------------------------- Uncapitalize ------------------------------ Uncapitalize text if first character is uppercase and all other characters are lowercase. Writes to <text>. */ void Uncapitalize(text) UBYTE *text; { if (isupper(*text)) { UBYTE *next; for (next = text + 1; *next; ++next) { if (isupper(*next)) break; } if (*next == 0) *text = tolower(*text); } } /* --------------------------------- FileInfo ---------------------------------- Return file information or NULL (writes to <fib>) */ struct FileInfoBlock * FileInfo(name, fib) struct FileInfoBlock *fib; UBYTE *name; { BPTR handle; if (handle = Lock(name, ACCESS_READ)) { if (Examine(handle, fib)) { UnLock(handle); return(fib); } else UnLock(handle); } return(NULL); } /* --------------------------------- DoExecute --------------------------------- Run DOS command */ BOOL DoExecute(cmd, async, output, directory, stack, prio) UBYTE *cmd, *output, *directory; ULONG stack; WORD prio; BOOL async; { BPTR newCurrentDir; BOOL success; success = FALSE; if (newCurrentDir = Lock(directory, SHARED_LOCK)) { BPTR inHandle, outHandle, oldCurrentDir; oldCurrentDir = CurrentDir(newCurrentDir); if (outHandle = Open(output, MODE_NEWFILE)) { struct MsgPort *oldct, *newct; if (IsInteractive(outHandle)) { newct = ((struct FileHandle *)BADDR(outHandle))->fh_Type; oldct = SetConsoleTask(newct); inHandle = Open("CONSOLE:", MODE_OLDFILE); SetConsoleTask(oldct); } else { newct = NULL; inHandle = Open("NIL:", MODE_OLDFILE); } if (inHandle) { struct TagItem tagItems[] = { SYS_Output, (ULONG)NULL, SYS_Input, (ULONG)NULL, NP_ConsoleTask, (ULONG)NULL, SYS_Asynch, (ULONG)FALSE, NP_StackSize, (ULONG)8196, NP_Priority, (ULONG)0, SYS_UserShell, (ULONG)TRUE, TAG_DONE }; tagItems[0].ti_Data = (ULONG)outHandle; tagItems[1].ti_Data = (ULONG)inHandle; tagItems[2].ti_Data = (ULONG)newct; tagItems[3].ti_Data = (ULONG)async; tagItems[4].ti_Data = (ULONG)stack; tagItems[5].ti_Data = (ULONG)prio; if (SystemTagList(cmd, tagItems) != -1L) success = TRUE; if (!(async && success)) Close(inHandle); } if (!(async && success)) Close(outHandle); } CurrentDir(oldCurrentDir); UnLock(newCurrentDir); } return(success); } /* --------------------------------- ExistPath ----------------------------------- Check whether file/directory exists. */ BOOL ExistPath(path) UBYTE *path; { BPTR lock; if (lock = Lock(path, ACCESS_READ)) UnLock(lock); if (lock) return(TRUE); else return(FALSE); } /* ---------------------------------- SearchNode -------------------------------- Return pointer to node if ordinal number (0, ...) is known. */ struct Node * SearchNode(list, ordinal) struct List *list; UWORD ordinal; { struct Node *node; for (node = list->lh_Head; node->ln_Succ; --ordinal, node = node->ln_Succ) if (ordinal == 0) return(node); return(NULL); } /* ----------------------------------- Beep ------------------------------------ Short audible beep (volumn: 0...63) */ void Beep(volume) UWORD volume; { if (volume) { struct IOAudio *audioIO; if (audioIO = (struct IOAudio *)AllocVec(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) { struct MsgPort *audioMP; if (audioMP = CreateMsgPort()) { UBYTE whichannel[] = { 1, 2, 4, 8 }; audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP; audioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0; audioIO->ioa_Request.io_Command = ADCMD_ALLOCATE; audioIO->ioa_Request.io_Flags = ADIOF_NOWAIT; audioIO->ioa_AllocKey = 0; audioIO->ioa_Data = whichannel; audioIO->ioa_Length = sizeof(whichannel); if (!OpenDevice("audio.device", 0, (struct IORequest *)audioIO, 0)) { __chip const static UBYTE waveptr[2] = {127, -127}; audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP; audioIO->ioa_Request.io_Command = CMD_WRITE; audioIO->ioa_Request.io_Flags = ADIOF_PERVOL; audioIO->ioa_Data = waveptr; audioIO->ioa_Length = 2; audioIO->ioa_Period = 1015; audioIO->ioa_Volume = volume; audioIO->ioa_Cycles = 60; BeginIO((struct IORequest *)audioIO ); WaitPort(audioMP); GetMsg (audioMP); CloseDevice((struct IORequest *)audioIO); } DeleteMsgPort(audioMP); } FreeVec(audioIO); } } } /* ----------------------------------- Replace --------------------------------- Replace word of length <wordLen> at position <pos> within current line by string <replacement> (0-terminated) */ void Replace(handle, apiMsg, column, wordLen, replacement) struct APIClient *handle; struct APIMessage *apiMsg; UBYTE *replacement; UWORD column; UWORD wordLen; { struct APIOrder *apiOrder; struct APIModifyRequest *apiModifyRequest; UBYTE *apiBuffer; struct EditConfig *config; UBYTE *dest; UWORD rplcLen; config = (struct EditConfig *)apiMsg->api_Instance->api_Environment; // allocate API command structures apiModifyRequest = &((struct BufferHandle *)handle)->APIModifyRequest; apiOrder = &((struct BufferHandle *)handle)->APIOrder; apiBuffer = ((struct BufferHandle *)handle)->APIData; // copy current line to buffer movmem(config->CurrentBuffer, apiBuffer, config->CurrentLen); // replace old word by <replacement> dest = apiBuffer + column; rplcLen = strlen(replacement); if (rplcLen != wordLen) movmem(dest + wordLen, dest + rplcLen, config->CurrentLen - column - wordLen); movmem(replacement, dest, rplcLen); // create API command for GoldED apiModifyRequest->mr_Line = config->Line; apiModifyRequest->mr_Column = column; apiModifyRequest->mr_Size = config->CurrentLen + (rplcLen - wordLen); apiModifyRequest->mr_Data = apiBuffer; apiOrder->api_Data = apiModifyRequest; apiOrder->api_Next = NULL; // notify GoldED apiMsg->api_Order = apiOrder; } /// /// "gui" /* ------------------------------ GetVScreenSize ------------------------------- Calculate visible screen rectangle */ void GetVScreenSize(screen, displayW, displayH) struct Screen *screen; UWORD *displayW; UWORD *displayH; { struct Rectangle clip; // read screen size if (QueryOverscan(GetVPModeID(&screen->ViewPort), &clip, OSCAN_TEXT)) { *displayW = clip.MaxX - clip.MinX + 1; *displayH = clip.MaxY - clip.MinY + 1; } else { *displayW = screen->Width; *displayH = screen->Height; } } /* --------------------------------- ComputeX ---------------------------------- Scale width <value> to match font */ UWORD ComputeX(font, value) struct TextFont *font; UWORD value; { return((font->tf_XSize * value) / 8); } /* --------------------------------- ComputeY ---------------------------------- Resize height <value> to match font */ UWORD ComputeY(font, value) struct TextFont *font; UWORD value; { return((font->tf_YSize * value) / 8); } /* -------------------------------- OpenMessageWin ----------------------------- Open info window. Window is closed automatically after a short delay time if <sync> is TRUE. */ struct Window * OpenMessageWin(text, sync) UBYTE *text; BOOL sync; { struct TextFont *font; struct Screen *screen; struct Window *win; ULONG lock; win = NULL; font = NULL; // critical stuff (barely legal - don't do this at home, kids ;-) lock = LockIBase(0); // find frontmost screen screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen; // keep system frozen Forbid(); // enable rendering functions UnlockIBase(lock); if (screen) { struct TextAttr textAttr = { "topaz.font", 8, 0, FPF_DESIGNED }; WORD len, width, height, fontW, fontH, x, y, displayW, displayH, xOffset, yOffset; GetVScreenSize(screen, &displayW, &displayH); xOffset = -screen->ViewPort.DxOffset; yOffset = -screen->ViewPort.DyOffset; textAttr = *screen->Font; if (font = OpenDiskFont(&textAttr)) { fontW = font->tf_XSize; fontH = font->tf_YSize; CloseFont(font); } else fontW = fontH = textAttr.ta_YSize; len = strlen(text); if (len < 30) len = 30; width = 20 + fontW * len; height = 20 + fontH; // center window on screen if (displayW) { x = ((displayW - width )>>1) + xOffset; y = ((displayH - height)>>1) + yOffset; } else x = y = 0; win = OpenWindowTags(NULL, WA_PubScreen, screen, WA_Left, x, WA_Top, y, WA_InnerWidth, width, WA_InnerHeight, height, WA_Title, "Words", WA_ScreenTitle, "Words", WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_RMBTRAP, WA_GimmeZeroZero, TRUE, TAG_DONE ); Permit(); // render window if (win) { if (font) SetFont(win->RPort, font); win = Message(text, sync, win); } } else Permit(); return(win); } /* ---------------------------------- Message ---------------------------------- Show message (assume that window size is sufficient) */ struct Window * Message(text, sync, win) struct Window *win; UBYTE *text; BOOL sync; { if (win == NULL) win = OpenMessageWin(text, sync); else { struct RastPort *rast = win->RPort; SetAPen(rast, 0); SetRast(rast, 0); SetAPen(rast, 1); Move(rast, 10, 10 + rast->TxBaseline); Text(rast, text, strlen(text)); if (sync) { Delay(150); CloseWindow(win); win = NULL; } } return(win); } /* --------------------------------- ShowSpell --------------------------------- Show ISpell suggestions. Writes user selection to suggestion. */ void ShowSpell(handle, apiMsg, list, title, suggestion) struct APIClient *handle; struct APIMessage *apiMsg; struct List *list; UBYTE *title; UBYTE *suggestion; { struct Node *node; UWORD suggestions; *suggestion = 0; for (suggestions = 0, node = list->lh_Head; node->ln_Succ; node = node->ln_Succ) ++suggestions; if (suggestions) { struct Screen *screen; struct Window *parent; screen = apiMsg->api_Instance->api_Screen; parent = apiMsg->api_Instance->api_Window; if (screen && parent) { struct TextFont *font; if (font = OpenDiskFont(screen->Font)) { APTR visualInfo; if (visualInfo = GetVisualInfoA(screen, NULL)) { struct Window *win; struct NewGadget newGad; if (suggestions < 3) suggestions = 3; newGad.ng_LeftEdge = 5; newGad.ng_TopEdge = 5; newGad.ng_Width = ComputeX(font, 300); newGad.ng_Height = ComputeY(font, suggestions * 8) + 4; newGad.ng_GadgetText = NULL; newGad.ng_TextAttr = screen->Font; newGad.ng_GadgetID = 0; newGad.ng_Flags = 0; newGad.ng_VisualInfo = visualInfo; newGad.ng_UserData = 0; win = OpenWindowTags(NULL, WA_PubScreen, screen, WA_Left, parent->LeftEdge + parent->BorderLeft, WA_Top, parent->TopEdge + parent->BorderTop, WA_InnerWidth, newGad.ng_Width + 10, WA_InnerHeight, newGad.ng_Height + 10, WA_IDCMP, LISTVIEWIDCMP | IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_CLOSEWINDOW, WA_Flags, WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_DEPTHGADGET | WFLG_DRAGBAR, WA_Title, title, TAG_DONE ); if (win) { struct Gadget *context, *glist; glist = NULL; newGad.ng_LeftEdge += win->BorderLeft; newGad.ng_TopEdge += win->BorderTop; if (context = CreateContext(&glist)) { struct Gadget *gad = CreateGadget(LISTVIEW_KIND, context, &newGad, GTLV_ShowSelected, NULL, GTLV_Labels, list, GTLV_Selected, 0, TAG_DONE); if (gad) { struct IntuiMessage *msg; struct Node *node; UWORD active; BOOL isDoubleClick; ULONG lastSeconds; ULONG lastMicros; ULONG lastSelection; ULONG class; AddGList(win, glist, UNDEFINED, UNDEFINED, NULL); RefreshGList(glist, win, NULL, UNDEFINED); GT_RefreshWindow(win, NULL); active = 0; do { isDoubleClick = FALSE; while (!(msg = GT_GetIMsg(win->UserPort))) WaitPort(win->UserPort); switch (class = msg->Class) { case RAWKEY: WORD step = (msg->Code == CURSORUP) ? -1 : 1; if (node = SearchNode(list, active + step)) { active += step; GT_SetGadgetAttrs(gad, win, NULL, GTLV_Selected, active, GTLV_MakeVisible, active, TAG_DONE); } break; case IDCMP_REFRESHWINDOW: BeginRefresh(win); EndRefresh(win, TRUE); break; case IDCMP_VANILLAKEY: if (msg->Code == 27) class = CLOSEWINDOW; if (msg->Code == 13) isDoubleClick = TRUE; break; case IDCMP_GADGETUP: active = msg->Code; if (active == lastSelection) isDoubleClick = DoubleClick(lastSeconds, lastMicros, msg->Seconds, msg->Micros); lastSelection = msg->Code; lastSeconds = msg->Seconds; lastMicros = msg->Micros; break; } GT_ReplyIMsg(msg); } while ((class != CLOSEWINDOW) && (isDoubleClick == FALSE)); if (class != CLOSEWINDOW) { if (node = SearchNode(list, active)) strcpy(suggestion, node->ln_Name); } } } CloseWindow(win); } FreeVisualInfo(visualInfo); } } } } } /// /// "ispell" /* --------------------------------- RunISpell --------------------------------- Run ISpell (if not yet running); increase user count */ struct MsgPort * RunISpell() { struct MsgPort *port; struct AutoConfig *autoConfig; // serialize access within this library ObtainSemaphore(&ISpellSemaphore); Forbid(); // ISpell running/starting already ? if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) { ObtainSemaphore(&autoConfig->Semaphore); ++autoConfig->Users; ReleaseSemaphore(&autoConfig->Semaphore); port = WaitForISpell(); if (port == NULL) Message("STARTUP FAILURE", TRUE, NULL); } else { port = FindPort("IRexxSpell"); // run ISpell on our own ? if (port == NULL) { struct AutoConfig *autoConfig; if (autoConfig = (struct AutoConfig *)AllocVec(sizeof(struct AutoConfig), MEMF_PUBLIC | MEMF_CLEAR)) { static UBYTE hashfile[255], language[255], personal[255], options[255], command[255], home[255]; __aligned struct FileInfoBlock fib = { 0 }; autoConfig->Semaphore.ss_Link.ln_Name = "WORDS"; autoConfig->Semaphore.ss_Link.ln_Pri = 0; autoConfig->Users = 1; AddSemaphore (&autoConfig->Semaphore); ObtainSemaphore(&autoConfig->Semaphore); // set default language strcpy(language, "english"); // set default options strcpy(options, "-W 1"); // did user provide special startup arguments for ISpell ? if (GetVar("WORDS.prefs", command, sizeof(command), GVF_GLOBAL_ONLY) != -1) { if (*command) { struct RDArgs *rdArgs, *args; if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) { ULONG argArray[] = { 0, 0, 0, 0, 0 }; // make LF-terminated copy of command string (required by dos/readArgs): strcat(command, "\12"); rdArgs->RDA_Source.CS_Buffer = command; rdArgs->RDA_Source.CS_Length = strlen(command); rdArgs->RDA_Source.CS_CurChr = 0; rdArgs->RDA_DAList = 0; rdArgs->RDA_Buffer = NULL; if (args = ReadArgs("LANGUAGE/K,QUICKCHECK/K,BOUNDARYCHARS/K,BEEP/N,ARGS/F", argArray, rdArgs)) { if (argArray[0]) // LANGUAGE/K strcpy(language, (UBYTE *)argArray[0]); if (argArray[4]) // ARGS/F strcpy(options, (UBYTE *)argArray[4]); FreeArgs(args); } else Message("Syntax error", TRUE, NULL); FreeDosObject(DOS_RDARGS, rdArgs); } } } // personal dictionary file (for new words) sprintf(personal, "/ispell/lib/.ispell_%s", language); // standard dictionary file sprintf(hashfile, "ispell:lib/%s.hash", language); // dictionary available ? if (FileInfo(hashfile, &fib)) { ULONG avail = AvailMem(MEMF_LARGEST); if (fib.fib_Size < avail) { struct Window *win = Message("Starting ISpell...", FALSE, NULL); // $HOME required by ISpell 3.1.18 if (GetVar("HOME", home, sizeof(home), GVF_GLOBAL_ONLY) == -1) SetVar("HOME", "sys:", -1, GVF_GLOBAL_ONLY); // run ISpell in host mode if (*options) sprintf(command, "ispell:bin/ispell -d%s -p%s -r %s", hashfile, personal, options); else sprintf(command, "ispell:bin/ispell -d%s -p%s -r", hashfile, personal); // wait for completion of ISpell startup if (DoExecute(command, TRUE, "NIL:", "ispell:", 32768, 1)) port = WaitForISpell(); if (port == NULL) Message("STARTUP FAILURE", TRUE, win); else if (win) CloseWindow(win); } else { if (fib.fib_Size <= AvailMem(MEMF_ANY)) Message("Out of RAM (fragmentation error)", TRUE, NULL); else Message("Out of RAM", TRUE, NULL); } } else Message("Dictionary not found", TRUE, NULL); ReleaseSemaphore(&autoConfig->Semaphore); if (port == NULL) StopISpell(); } } } Permit(); ReleaseSemaphore(&ISpellSemaphore); return(port); } /* ------------------------------- WaitForISpell ------------------------------- Wait for ISpell */ struct MsgPort * WaitForISpell() { struct MsgPort *port; UWORD try; for (try = 50; try-- && (port == NULL); Delay(10)) { Forbid(); port = FindPort("IRexxSpell"); Permit(); } return(port); } /* -------------------------------- StopISpell --------------------------------- Decrement ISpell user count. Unload ISpell if unused. */ void StopISpell() { struct AutoConfig *autoConfig; Forbid(); if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) { ObtainSemaphore(&autoConfig->Semaphore); if (autoConfig->Users) --autoConfig->Users; ReleaseSemaphore(&autoConfig->Semaphore); if (autoConfig->Users == 0) { struct MsgPort *port; RemSemaphore (&autoConfig->Semaphore); ObtainSemaphore (&autoConfig->Semaphore); ReleaseSemaphore(&autoConfig->Semaphore); if (port = CreateMsgPort()) { if (autoConfig->Words) { struct Screen *screen; ULONG lock; // critical stuff (barely legal - don't do this at home, kids ;-) lock = LockIBase(0); // find frontmost screen screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen; // keep system frozen Forbid(); // enable rendering functions UnlockIBase(lock); if (rtEZRequestTags("Save new words to user dictionary ?", "_SAVE|_cancel", NULL, NULL, RT_Underscore, '_', RTEZ_ReqTitle, "Words", RT_Screen, screen, TAG_DONE) == 1) SendRexxCommand("IRexxSpell", "ADD A", port, NULL); Permit(); } SendRexxCommand("IRexxSpell", "EXIT", port, NULL); DeleteMsgPort(port); } FreeVec(autoConfig); } } Permit(); } /// /// "arexx" /* ---------------------------------- SendRexxCommand ------------------------- Send ARexx message & wait for answer. Return pointer to result or NULL. */ UBYTE * SendRexxCommand(port, cmd, replyPort, buffer) struct MsgPort *replyPort; UBYTE *cmd, *port, *buffer; { struct MsgPort *rexxport; Forbid(); if (rexxport = FindPort(port)) { struct RexxMsg *rexxMsg, *answer; if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) { if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) { static ULONG result; rexxMsg->rm_Action = RXCOMM | RXFF_RESULT; PutMsg(rexxport, &rexxMsg->rm_Node); do { WaitPort(replyPort); if (answer = (struct RexxMsg *)GetMsg(replyPort)) result = answer->rm_Result1; } while (!answer); Permit(); if (answer->rm_Result1 == RC_OK) { if (answer->rm_Result2) { if (buffer) strcpy(buffer, (char *)answer->rm_Result2); DeleteArgstring((char *)answer->rm_Result2); } } DeleteArgstring((char *)ARG0(answer)); DeleteRexxMsg(answer); return(&result); } } } Permit(); return(NULL); } /// /// "spellchecking" /* ---------------------------- CheckWordCurrent ------------------------------- Check word under or before cursor. */ void CheckWordCurrent(handle, apiMsg, suggestions) struct APIClient *handle; struct APIMessage *apiMsg; BOOL suggestions; { struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment; UBYTE *text, *last; UWORD column; text = config->CurrentBuffer + config->Column; last = config->CurrentBuffer + config->CurrentLen; // find a character of the last word (ignore two white space characters if necessary) column = config->Column; if (column && (IsLetter[*text] == FALSE)) { --column; --text; } if (column && (IsLetter[*text] == FALSE)) { --column; --text; } // any character found ? if (IsLetter[*text]) { UWORD wordLen; UBYTE *check; // find beginning of word while (column) { if (IsLetter[*(text - 1)]) { --text; --column; } else break; } // find end of word for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) { // end of word detected ? if (IsLetter[*check] == FALSE) { // verify end of word (boundary characters can be space OR letter) if (IsBoundary[*check]) { if ((check + 1) < last) { if (IsLetter[*(check + 1)] == FALSE) break; } else break; } else break; } } // check the word ? if ((wordLen > 1) && (wordLen <= 100)) { if (suggestions) { UBYTE replacement[128]; if (CheckWord(handle, apiMsg, text, wordLen, replacement) == FALSE) { if (*replacement) Replace(handle, apiMsg, column, wordLen, replacement); } } else if (CheckWord(handle, apiMsg, text, wordLen, NULL) == FALSE) Beep(Volume); } } } /* --------------------------------- CheckWord --------------------------------- Check <word>. Handles approx. 100 characters. Return TRUE if ok. Writes replacement to <suggestion> (unless NULL). */ BOOL CheckWord(handle, apiMsg, word, len, suggestion) struct APIClient *handle; struct APIMessage *apiMsg; UBYTE *word; UWORD len; UBYTE *suggestion; { UBYTE command[128]; BOOL ok; ok = TRUE; if (suggestion) *suggestion = 0; // build command line for ISpell movmem(word, command, len); command[len] = 0; // suggestion requested ? if (suggestion) { UBYTE *result = ((struct BufferHandle *)handle)->Buffer; // send CHECK command to recieve detailed information (suggestions) strins(command, "CHECK "); if (SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer)) { if ((result[0] == 'o') && (result[1] == 'k')) { ok = TRUE; DisplayBeep(NULL); } else { switch (result[0]) { case '&': // spelling error, near misses available UBYTE *end; ok = FALSE; if (end = strchr(result, ':')) { // convert ISpell result string (suggestions) to exec list struct List list; struct Node *node; struct Node *next; UWORD count; NewList(&list); count = 0; while (*end) { UBYTE *start; do { *end++ = 0; } while (*end && ((*end == ',') || (*end == ' '))); start = end; while (*end && (*end != 32) && (*end != ',')) ++end; if (node = (struct Node *)AllocVec(sizeof(struct Node), MEMF_ANY | MEMF_CLEAR)) { // node name: we point directly into the result string node->ln_Name = start; AddTail(&list, node); ++count; } } if (count) { UBYTE title[128]; // build window title movmem(word, title, len); title[len] = 0; // show suggestions ShowSpell(handle, apiMsg, &list, title, suggestion); // free list of suggestions for (node = list.lh_Head; next = node->ln_Succ; node = next) FreeVec(node); } else DisplayBeep(NULL); } else DisplayBeep(NULL); break; case '?': // spelling error, no near misses ok = FALSE; DisplayBeep(NULL); break; case '*': // valid word case '+': // valid after affix removal case '-': // valid compound ok = TRUE; break; default: ok = FALSE; DisplayBeep(0); } } } } else { UBYTE *result = ((struct BufferHandle *)handle)->Buffer; strins(command, "QUICKCHECK "); if (SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer)) { if ((result[0] == 'o') && (result[1] == 'k')) ok = TRUE; else ok = FALSE; } } return(ok); } /* --------------------------------- LearnWord --------------------------------- Learn word <word>. Handles approx. 100 characters. Uncapitalize word if <smart> is TRUE. */ void LearnWord(handle, apiMsg, new, len, smart) struct APIClient *handle; struct APIMessage *apiMsg; UBYTE *new; UWORD len; BOOL smart; { struct AutoConfig *autoConfig; Forbid(); if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) { UBYTE command[128]; ObtainSemaphore(&autoConfig->Semaphore); ++autoConfig->Words; ReleaseSemaphore(&autoConfig->Semaphore); movmem(new, command, len); command[len] = 0; if (smart) Uncapitalize(command); strins(command, "QUICKADD "); SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer); } else Message("Communication error (semaphore not found)", TRUE, NULL); Permit(); } /* ------------------------------ LearnWordCurrent ----------------------------- Learn word under cursor. Uncapitalize word if <smart> is TRUE. */ void LearnWordCurrent(handle, apiMsg, smart) struct APIClient *handle; struct APIMessage *apiMsg; BOOL smart; { struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment; UBYTE *text, *last; UWORD column; text = config->CurrentBuffer + config->Column; last = config->CurrentBuffer + config->CurrentLen; // find a character of the last word (ignore two white space characters if necessary) column = config->Column; if (column && (IsLetter[*text] == FALSE)) { --column; --text; } if (column && (IsLetter[*text] == FALSE)) { --column; --text; } // any character found ? if (IsLetter[*text]) { UWORD wordLen; UBYTE *check; // find beginning of word while (column) { if (IsLetter[*(text - 1)]) { --text; --column; } else break; } // find end of word for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) { // end of word detected ? if (IsLetter[*check] == FALSE) { // verify end of word (boundary characters can be space OR letter) if (IsBoundary[*check]) { if ((check + 1) < last) { if (IsLetter[*(check + 1)] == FALSE) break; } else break; } else break; } } // learn the word ? if ((wordLen > 1) && (wordLen <= 100)) LearnWord(handle, apiMsg, text, wordLen, smart); } } /* ------------------------------ IgnoreWordCurrent ---------------------------- Ignore word under cursor. Uncapitalize word if <smart> is TRUE. */ void IgnoreWordCurrent(handle, apiMsg, smart) struct APIClient *handle; struct APIMessage *apiMsg; BOOL smart; { struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment; UBYTE *text, *last; UWORD column; text = config->CurrentBuffer + config->Column; last = config->CurrentBuffer + config->CurrentLen; // find a character of the last word (ignore two white space characters if necessary) column = config->Column; if (column && (IsLetter[*text] == FALSE)) { --column; --text; } if (column && (IsLetter[*text] == FALSE)) { --column; --text; } // any character found ? if (IsLetter[*text]) { UWORD wordLen; UBYTE *check; // find beginning of word while (column) { if (IsLetter[*(text - 1)]) { --text; --column; } else break; } // find end of word for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) { // end of word detected ? if (IsLetter[*check] == FALSE) { // verify end of word (boundary characters can be space OR letter) if (IsBoundary[*check]) { if ((check + 1) < last) { if (IsLetter[*(check + 1)] == FALSE) break; } else break; } else break; } } // ignore the word ? if ((wordLen > 1) && (wordLen <= 100)) { UBYTE command[128]; movmem(text, command, wordLen); command[wordLen] = 0; if (smart) Uncapitalize(command); strins(command, "ACCEPT "); SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer); } } } /// /// "dice" #ifdef _DCC /* ------------------------------- CreateGadget -------------------------------- Stub function (missing in linker lib of DICE 2.0.54r) */ struct Gadget * CreateGadget(ULONG kind, struct Gadget *previous, struct NewGadget *newgad, ULONG firstTag, ...) { return(CreateGadgetA(kind, previous, newgad, (struct TagItem *)&firstTag)); } /* ----------------------------- GT_SetGadgetAttrs ----------------------------- Stub function (missing in linker lib of DICE 2.0.54r) */ void GT_SetGadgetAttrs(struct Gadget *gad, struct Window *win, struct Requester *req, ULONG firstTag, ...) { GT_SetGadgetAttrsA(gad, win, req, (struct TagItem *)&firstTag); } /* ------------------------------- DrawBevelBox -------------------------------- Stub function (missing in linker lib of DICE 2.0.54r) */ void DrawBevelBox(rport, left, top, width, height, firsttag, ...) struct RastPort *rport; LONG left, top, width, height; Tag firsttag; { DrawBevelBoxA(rport, left, top, width, height, (struct TagItem *)&firsttag); } #endif ///