diff options
Diffstat (limited to 'ispell.c')
-rw-r--r-- | ispell.c | 2217 |
1 files changed, 2217 insertions, 0 deletions
diff --git a/ispell.c b/ispell.c new file mode 100644 index 0000000..1066cb6 --- /dev/null +++ b/ispell.c @@ -0,0 +1,2217 @@ +/* + * Copyright (c) 1999 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of the XFree86 Project shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from the + * XFree86 Project. + * + * Author: Paulo César Pereira de Andrade + */ + +/* $XFree86: xc/programs/xedit/ispell.c,v 1.19 2002/10/19 20:04:20 herrb Exp $ */ + +#include "xedit.h" +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <ctype.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <X11/Xaw/Toggle.h> +#include <X11/Xaw/MenuButton.h> +#include <X11/Xaw/SmeBSB.h> +#include <X11/Xaw/SimpleMenu.h> + +#define RECEIVE 1 +#define SEND 2 + +#define CHECK 0 +#define ADD 1 +#define REMOVE 2 + +#define ASIS 1 +#define UNCAP 2 + +/* + * Types + */ +#define UNDO_DEPTH 16 +typedef struct _ispell_undo { + char *undo_str; + int undo_count; + XawTextPosition undo_pos; + Boolean repeat; /* two (misspelled?) words together */ + Boolean terse; + int format; /* remember text formatting style */ + struct _ispell_undo *next, *prev; +} ispell_undo; + +typedef struct _ispell_dict { + Widget sme; + char *wchars; + struct _ispell_dict *next; +} ispell_dict; + +#define TEXT 0 +#define HTML 1 +struct _ispell_format { + char *name; + int value; + Widget sme; +}; + +static struct _ispell_format ispell_format[] = { + {"text", TEXT}, + {"html", HTML}, +}; + +struct _ispell { + Widget shell, form, mispelled, repeated, word, replacement, text, + suggestions, viewport, list, commands, replace, status, + replaceAll, undo, ignore, ignoreAll, add, addUncap, suspend, + cancel, check, look, terse, options, dict, dictMenu, + format, formatMenu; + + Widget ascii, source; + XtInputId id; + int pid, ifd[2], ofd[2]; + XawTextPosition left, right; + char *item; + Bool lock; + Bool repeat; + Bool checkit; + int stat; + char *buf; + int bufsiz; + int buflen; + char sendbuf[1024]; + char sentbuf[1024]; + + int undo_depth; + ispell_undo *undo_head, *undo_base; + char *undo_for; + + char *wchars; + char *cmd; + char *skip; + char *command; + Boolean terse_mode, undo_terse_mode; + char *guess_label, *miss_label, *root_label, *none_label, *eof_label, + *compound_label, *ok_label, *repeat_label, *working_label, *look_label; + char *look_cmd; + char *words_file; + + char *dictionary; + char *dict_list; + ispell_dict *dict_info; + + int format_mode; /* to undo correctly */ + char *formatting; + struct _ispell_format *format_info; +}; + +typedef struct _ReplaceList { + char *word; + char *replace; + struct _ReplaceList *next; +} ReplaceList; + +typedef struct _IgnoreList { + char *word; + int add; + struct _IgnoreList *next; +} IgnoreList; + +/* + * Prototypes + */ +static void AddIspell(Widget, XtPointer, XtPointer); +static void ChangeDictionaryIspell(Widget, XtPointer, XtPointer); +static void ChangeFormatIspell(Widget, XtPointer, XtPointer); +static void CheckIspell(Widget, XtPointer, XtPointer); +static void IgnoreIspell(Widget, XtPointer, XtPointer); +static Bool InitIspell(void); +static void IspellCheckUndo(void); +static int IspellConvertHtmlAmp(char*); +static Bool IspellDoIgnoredWord(char*, int, int); +static Bool IspellIgnoredWord(char*, int, int); +static void IspellInputCallback(XtPointer, int*, XtInputId*); +static void IspellKillUndoBuffer(void); +static Bool IspellReceive(void); +static char *IspellReplacedWord(char*, char*); +static int IspellSend(void); +static void IspellSetSelection(XawTextPosition, XawTextPosition); +static void IspellSetRepeated(Bool); +static void IspellSetSensitive(Bool); +static void IspellSetStatus(char*); +static void IspellSetTerseMode(Bool); +static Bool IspellStartProcess(void); +static Bool IspellEndProcess(Bool, Bool); +static void LookIspell(Widget, XtPointer, XtPointer); +static void PopdownIspell(Widget, XtPointer, XtPointer); +static void ReplaceIspell(Widget, XtPointer, XtPointer); +static void RevertIspell(Widget, XtPointer, XtPointer); +static void SelectIspell(Widget, XtPointer, XtPointer); +static void ToggleTerseIspell(Widget, XtPointer, XtPointer); +#ifndef SIGNALRETURNSINT +static void timeout_signal(int); +static void (*old_timeout)(int); +#else +static int timeout_signal(int); +static int (*old_timeout)(int); +#endif +static void UndoIspell(Widget, XtPointer, XtPointer); + +Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*); + +/* + * Initialization + */ +static struct _ispell ispell; + +#define RSTRTBLSZ 23 +#define ISTRTBLSZ 71 +static ReplaceList *replace_list[RSTRTBLSZ]; +static IgnoreList *ignore_list[ISTRTBLSZ]; + +#ifndef XtCStatus +#define XtCStatus "Status" +#endif + +#define Offset(field) XtOffsetOf(struct _ispell, field) +static XtResource resources[] = { + {"wordChars", "Chars", XtRString, sizeof(char*), + Offset(wchars), XtRString, ""}, + {"ispellCommand", "CommandLine", XtRString, sizeof(char*), + Offset(cmd), XtRString, "/usr/local/bin/ispell"}, + {"terseMode", "Terse", XtRBoolean, sizeof(Boolean), + Offset(terse_mode), XtRImmediate, (XtPointer)False}, + {"guessLabel", XtCStatus, XtRString, sizeof(String), + Offset(guess_label), XtRString, "Guess"}, + {"missLabel", XtCStatus, XtRString, sizeof(String), + Offset(miss_label), XtRString, "Miss"}, + {"rootLabel", XtCStatus, XtRString, sizeof(String), + Offset(root_label), XtRString, "Root:"}, + {"noneLabel", XtCStatus, XtRString, sizeof(String), + Offset(none_label), XtRString, "None"}, + {"compoundLabel", XtCStatus, XtRString, sizeof(String), + Offset(compound_label), XtRString, "Compound"}, + {"okLabel", XtCStatus, XtRString, sizeof(String), + Offset(ok_label), XtRString, "Ok"}, + {"eofLabel", XtCStatus, XtRString, sizeof(String), + Offset(eof_label), XtRString, "End Of File"}, + {"repeatLabel", XtCStatus, XtRString, sizeof(String), + Offset(repeat_label), XtRString, "Repeat"}, + {"workingLabel", XtCStatus, XtRString, sizeof(String), + Offset(working_label), XtRString, "..."}, + {"lookLabel", XtCStatus, XtRString, sizeof(String), + Offset(look_label), XtRString, "Look"}, + {"lookCommand", "CommandLine", XtRString, sizeof(char*), + Offset(look_cmd), XtRString, "/usr/bin/egrep -i"}, + {"wordsFile", "Words", XtRString, sizeof(char*), + Offset(words_file), XtRString, "/usr/share/dict/words"}, + {"dictionary", "Dictionary", XtRString, sizeof(char*), + Offset(dictionary), XtRString, "american"}, + {"dictionaries", "Dictionary", XtRString, sizeof(char*), + Offset(dict_list), XtRString, "american americanmed+ english"}, + {"formatting", "TextFormat", XtRString, sizeof(char*), + Offset(formatting), XtRString, "text"}, +}; +#undef Offset + +#ifdef NO_LIBC_I18N +static int +ToLower(int ch) +{ + char buf[2]; + + *buf = ch; + XmuNCopyISOLatin1Lowered(buf, buf, sizeof(buf)); + + return (*buf); +} + +static int +ToUpper(int ch) +{ + char buf[2]; + + *buf = ch; + XmuNCopyISOLatin1Uppered(buf, buf, sizeof(buf)); + + return (*buf); +} + +static int +IsLower(int ch) +{ + char upbuf[2]; + char lobuf[2]; + + *upbuf = *lobuf = ch; + XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf)); + XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf)); + + return (*lobuf != *upbuf && ch == *lobuf); +} + +static int +IsUpper(int ch) +{ + char upbuf[2]; + char lobuf[2]; + + *upbuf = *lobuf = ch; + XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf)); + XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf)); + + return (*lobuf != *upbuf && ch == *upbuf); +} +#else +#define ToLower tolower +#define ToUpper toupper +#define IsLower islower +#define IsUpper isupper +#endif + +/* + * Implementation + */ +#ifdef STDERR_FILENO +# define WRITES(s) write(STDERR_FILENO, s, strlen(s)) +#else +# define WRITES(s) write(fileno(stderr), s, strlen(s)) +#endif + +/*ARGSUSED*/ +#ifndef SIGNALRETURNSINT +static void +timeout_signal(int unused) +{ + int olderrno = errno; + + WRITES("Warning: Timeout waiting ispell process to die.\n"); + kill(ispell.pid, SIGTERM); + errno = olderrno; +} +#else +static int +timeout_signal(int unused) +{ + int olderrno = errno; + + WRITES("Warning: Timeout waiting ispell process to die.\n"); + kill(ispell.pid, SIGTERM); + + errno = olderrno; + return (0); +} +#endif + +static void +IspellSetSelection(XawTextPosition left, XawTextPosition right) +{ + /* Try to make sure the selected word is completely visible */ + XawTextSetInsertionPoint(ispell.ascii, right); + XawTextSetInsertionPoint(ispell.ascii, left); + XawTextSetSelection(ispell.ascii, left, right); +} + +static void +IspellSetStatus(char *label) +{ + Arg args[1]; + + XtSetArg(args[0], XtNlabel, label); + XtSetValues(ispell.status, args, 1); +} + +static void +IspellSetRepeated(Bool state) +{ + static char *mispelled, *repeated; + Arg args[1]; + + if (mispelled == NULL) { + XtSetArg(args[0], XtNlabel, &mispelled); + XtGetValues(ispell.mispelled, args, 1); + mispelled = XtNewString(mispelled); + } + if (repeated == NULL) { + XtSetArg(args[0], XtNlabel, &repeated); + XtGetValues(ispell.repeated, args, 1); + repeated = XtNewString(repeated); + } + XtSetSensitive(ispell.replaceAll, !state); + XtSetSensitive(ispell.ignoreAll, !state); + XtSetSensitive(ispell.add, !state); + XtSetSensitive(ispell.addUncap, !state); + if (!state) { + XtSetArg(args[0], XtNlabel, mispelled); + XtSetValues(ispell.mispelled, args, 1); + } + else { + XtSetArg(args[0], XtNlabel, repeated); + XtSetValues(ispell.mispelled, args, 1); + } +} + +static void +IspellSetSensitive(Bool state) +{ + XtSetSensitive(ispell.replace, state); + XtSetSensitive(ispell.replaceAll, state); + XtSetSensitive(ispell.ignore, state); + XtSetSensitive(ispell.ignoreAll, state); + XtSetSensitive(ispell.add, state); + XtSetSensitive(ispell.addUncap, state); +} + +static void +IspellSetTerseMode(Bool mode) +{ + Arg args[1]; + + XtSetArg(args[0], XtNstate, ispell.terse_mode = mode); + XtSetValues(ispell.terse, args, 1); + write(ispell.ofd[1], mode ? "!\n" : "%\n", 2); +} + +static void +IspellCheckUndo(void) +{ + ispell_undo *undo = XtNew(ispell_undo); + + if (ispell.undo_for && strcmp(ispell.undo_for, ispell.dictionary)) { + XeditPrintf("Undo: Dictionary changed. Previous undo information lost.\n"); + IspellKillUndoBuffer(); + Feep(); + } + + undo->next = NULL; + undo->repeat = False; + undo->terse = ispell.undo_terse_mode; + undo->format = ispell.format_mode; + if ((undo->prev = ispell.undo_head) != NULL) + undo->prev->next = undo; + else + undo->prev = NULL; + ++ispell.undo_depth; + if (!ispell.undo_base) { + ispell.undo_base = undo; + XtSetSensitive(ispell.undo, True); + } + else if (ispell.undo_depth > UNDO_DEPTH) { + ispell_undo *tmp; + + if (ispell.undo_base->undo_str) + XtFree(ispell.undo_base->undo_str); + tmp = ispell.undo_base->next; + XtFree((char*)ispell.undo_base); + tmp->prev = NULL; + ispell.undo_base = tmp; + ispell.undo_depth = UNDO_DEPTH; + } + ispell.undo_head = undo; +} + +static char * +IspellReplacedWord(char *word, char *replace) +{ + ReplaceList *list; + int ii = 0; + char *pp = word; + + while (*pp) + ii = (ii << 1) ^ *pp++; + if (ii < 0) + ii = -ii; + ii %= RSTRTBLSZ; + for (list = replace_list[ii]; list; list = list->next) + if (strcmp(list->word, word) == 0) { + if (replace) { + XtFree(list->replace); + list->replace = XtNewString(replace); + } + return (list->replace); + } + + if (!replace) + return (NULL); + + list = XtNew(ReplaceList); + list->word = XtNewString(word); + list->replace = XtNewString(replace); + list->next = replace_list[ii]; + replace_list[ii] = list; + + return (list->replace); +} + +static Bool +IspellDoIgnoredWord(char *word, int cmd, int add) +{ + IgnoreList *list, *prev; + int ii = 0; + char *pp = word; + + while (*pp) + ii = (ii << 1) ^ *pp++; + if (ii < 0) + ii = -ii; + ii %= ISTRTBLSZ; + for (prev = list = ignore_list[ii]; list; prev = list, list = list->next) + if (strcmp(list->word, word) == 0) { + if (cmd == REMOVE) { + XtFree(list->word); + prev->next = list->next; + XtFree((char*)list); + if (prev == list) + ignore_list[ii] = NULL; + return (True); + } + return (cmd == CHECK); + } + + if (cmd != ADD) + return (False); + + list = XtNew(IgnoreList); + list->word = XtNewString(word); + list->add = add; + list->next = ignore_list[ii]; + ignore_list[ii] = list; + + return (True); +} + +static Bool +IspellIgnoredWord(char *word, int cmd, int add) +{ + if (add != UNCAP && IspellDoIgnoredWord(word, cmd, add)) + return (True); + + /* add/remove uncapped word to/of list, + * or cheks for correct capitalization */ + if (add == UNCAP || cmd == CHECK) { + unsigned char *str = (unsigned char*)word; + unsigned char string[1024]; + Bool upper, status; + int i; + + status = True; + upper = IsUpper(*str); + *string = upper ? ToLower(*str) : *str; + if (*str) + str++; + if (IsLower(*str)) + upper = False; + for (i = 1; *str && i < sizeof(string) - 1; i++, str++) { + if (upper && IsLower(*str)) + status = False; + else if (!upper && IsUpper(*str)) + status = False; + string[i] = ToLower(*str); + } + string[i] = '\0'; + + if ((cmd != CHECK || status) && + IspellDoIgnoredWord((char*)string, cmd, add)) + return (True); + } + + return (False); +} + +/*ARGSUSED*/ +static Bool +IspellReceive(void) +{ + int i, len, old_len; + Arg args[2]; + char *str, *end, **list, **old_list; + char *tmp, word[1024]; + int j; + + if (ispell.lock || ispell.stat != RECEIVE) + return (False); + + while (1) { /* read the entire line */ + if (ispell.buflen >= ispell.bufsiz - 1) + ispell.buf = XtRealloc(ispell.buf, ispell.bufsiz += BUFSIZ); + if ((len = read(ispell.ifd[0], &ispell.buf[ispell.buflen], + ispell.bufsiz - ispell.buflen - 1)) <= 0) + break; + ispell.buflen += len; + } + if (ispell.buflen <= 0) + return (False); + len = 0; + i = ispell.buflen - 1; + while (i >= 0 && ispell.buf[i] == '\n') { + ++len; + --i; + } + if (len < 2 - ((ispell.terse_mode && i == -1) || ispell.buf[0] == '@')) + return (False); + ispell.buf[ispell.buflen - len] = '\0'; + ispell.buflen = 0; + + if ((tmp = strchr(ispell.sendbuf, '\n')) != NULL) + *tmp = '\0'; + + switch (ispell.buf[0]) { + case '&': /* MISS */ + case '?': /* GUESS */ + str = strchr(&ispell.buf[2], ' '); + if (!ispell.checkit) { + *str = '\0'; + XtSetArg(args[0], XtNlabel, &ispell.buf[2]); + XtSetValues(ispell.word, args, 1); + } + ++str; + list = NULL; + str = strchr(str, ':') + 1; + for (i = 0; ; i++) { + end = strchr(str, ','); + if (end) *end = '\0'; + if ((i % 16) == 0) + list = (char**)XtRealloc((char*)list, (i + 16) * sizeof(char*)); + tmp = word; + for (j = 1; j < sizeof(word) && str[j]; j++) { + if (str[j] == '+') + continue; + else if (str[j] == '-' && str[j+1] != '-' && str[j-1] != '-') { + char *p, string[256]; + int k, l; + + for (l = 0, k = j + 1; str[k] != '+' && str[k] != '-' + && str[k] && l < sizeof(string) - 1; k++, l++) + string[l] = str[k]; + string[l] = '\0'; + *tmp = '\0'; + if (l && (p = strstr(word, string)) != NULL) { + char *sav = p; + + while ((p = strstr(p + l, string)) != NULL) + sav = p; + p = sav; + if (strcmp(p, string) == 0) { + tmp = p; + j = k - 1; + } + else + *tmp++ = '-'; + } + else + *tmp++ = '-'; + } + else + *tmp++ = str[j]; + } + *tmp = '\0'; + list[i] = XtNewString(word); + + if (end) str = end + 1; + else break; + } + len = i + 1; + + XtSetArg(args[0], XtNlist, &old_list); + XtSetArg(args[1], XtNnumberStrings, &old_len); + XtGetValues(ispell.list, args, 2); + + ispell.item = NULL; + if ((str = IspellReplacedWord(&ispell.buf[2], NULL)) != NULL) + for (i = 0; i < len; i++) { + if (strcmp(list[i], str) == 0) { + ispell.item = list[i]; + break; + } + } + else + ispell.item = list[i = 0]; + if (!ispell.item) { + list = (char**)XtRealloc((char*)list, (len + 1) * sizeof(char*)); + ispell.item = list[i] = XtNewString(str); + ++len; + } + + XtSetArg(args[0], XtNlist, list); + XtSetArg(args[1], XtNnumberStrings, len); + XtSetValues(ispell.list, args, 2); + + XtSetSensitive(ispell.list, True); + if (!ispell.checkit) + XawListHighlight(ispell.list, i); + + if (old_len > 1 || (XtName(ispell.list) != old_list[0])) { + while (--old_len > -1) + XtFree(old_list[old_len]); + XtFree((char*)old_list); + } + + if (!ispell.checkit) { + XtSetArg(args[0], XtNstring, ispell.item); + XtSetValues(ispell.text, args, 1); + IspellSetSelection(ispell.left, ispell.right); + if (ispell.repeat) + IspellSetRepeated(ispell.repeat = False); + } + + IspellSetStatus(ispell.buf[0] == '?' ? + ispell.guess_label : ispell.miss_label); + ispell.undo_terse_mode = ispell.terse_mode; + ispell.format_mode = ispell.format_info->value; + ispell.lock = True; + break; + case '#': /* NONE */ + case '-': /* COMPOUND */ + case '+': /* ROOT */ + check_label: + str = &ispell.sendbuf[1]; + if (!ispell.checkit) { + XtSetArg(args[0], XtNlabel, str); + XtSetValues(ispell.word, args, 1); + } + + XtSetArg(args[0], XtNlist, &old_list); + XtSetArg(args[1], XtNnumberStrings, &old_len); + XtGetValues(ispell.list, args, 2); + ispell.item = NULL; + + list = (char**)XtMalloc(sizeof(char**)); + if ((tmp = IspellReplacedWord(str, NULL)) != NULL) + str = tmp; + if (tmp == NULL && ispell.buf[0] == '#') + list[0] = XtNewString(""); + else + list[0] = XtNewString(str); + + XtSetArg(args[0], XtNlist, list); + XtSetArg(args[1], XtNnumberStrings, 1); + XtSetValues(ispell.list, args, 2); + + if (tmp == NULL && ispell.buf[0] == '#') { + XawListUnhighlight(ispell.list); + XtSetSensitive(ispell.list, False); + } + else { + XtSetSensitive(ispell.list, True); + if (!ispell.checkit) + XawListHighlight(ispell.list, 0); + } + if (old_len > 1 || (XtName(ispell.list) != old_list[0])) { + while (--old_len > -1) + XtFree(old_list[old_len]); + XtFree((char*)old_list); + } + + if (!ispell.checkit) { + XtSetArg(args[0], XtNstring, str); + XtSetValues(ispell.text, args, 1); + IspellSetSelection(ispell.left, ispell.right); + if (ispell.repeat) + IspellSetRepeated(ispell.repeat = False); + } + + ispell.undo_terse_mode = ispell.terse_mode; + ispell.format_mode = ispell.format_info->value; + ispell.lock = True; + if (ispell.buf[0] == '+') { + if ((tmp = strchr(&ispell.buf[2], '\n')) != NULL) + *tmp = '\0'; + XmuSnprintf(word, sizeof(word), "%s %s", + ispell.root_label, &ispell.buf[2]); + IspellSetStatus(word); + } + else + IspellSetStatus(ispell.buf[0] == '#' ? ispell.none_label : + ispell.buf[0] == '-' ? ispell.compound_label : + ispell.ok_label); + break; + case '*': /* OK */ + case '\0': /* when running in terse mode */ + if (!ispell.checkit) + (void)IspellIgnoredWord(&ispell.sendbuf[1], ADD, 0); + else + goto check_label; + ispell.lock = False; + break; + case '@': /* Ispell banner */ + /* it only happens when the dictionary is changed */ + if (!ispell.repeat) { + XawTextPosition left, right; + + ispell.stat = SEND; + while (IspellSend() == 0) + ; + /* word chars may have changed */ + XawTextGetSelectionPos(ispell.ascii, &left, &right); + if (left != ispell.left || right != ispell.right) { + XtSetArg(args[0], XtNstring, &ispell.sendbuf[1]); + XtSetValues(ispell.text, args, 1); + IspellSetSelection(ispell.left, ispell.right); + } + ispell.checkit = True; + } + else { + IspellSetStatus(ispell.repeat_label); + ispell.undo_terse_mode = ispell.terse_mode; + ispell.format_mode = ispell.format_info->value; + ispell.lock = True; + return (True); + } + break; + default: + fprintf(stderr, "Unknown ispell command '%c'\n", ispell.buf[0]); + return (False); + } + + if (!ispell.lock && !ispell.checkit) { + ispell.stat = SEND; + while (IspellSend() == 0) + ; + } + + return (True); +} + +static int +IspellConvertHtmlAmp(char *buf) +{ + int len, ch = '?'; + + /* this function is static, so I can do it */ + *strchr(++buf, ';') = '\0'; + + len = strlen(buf); + if (len == 0) + return ('&'); + if (len > 1) { + if (strcasecmp(&buf[1], "lt") == 0) + ch = '<'; + else if (strcasecmp(&buf[1], "gt") == 0) + ch = '>'; + else if (strcasecmp(&buf[1], "nbsp") == 0) + ch = ' '; + else if (strcasecmp(&buf[1], "amp") == 0) + ch = '&'; + else if (strcasecmp(&buf[1], "quot") == 0) + ch = '"'; + else if (*buf == '#') { + char *tmp; + + if (len == 1); + return ('?'); + ch = strtol(&buf[1], &tmp, 10); + if (*tmp) + fprintf(stderr, "Warning: bad html interpreting '&#' mark.\n"); + } + else if (strcmp(&buf[1], "acute") == 0) { + switch (*buf) { + case 'a': ch = 'á'; break; + case 'e': ch = 'é'; break; + case 'i': ch = 'í'; break; + case 'o': ch = 'ó'; break; + case 'u': ch = 'ú'; break; + case 'A': ch = 'Á'; break; + case 'E': ch = 'É'; break; + case 'I': ch = 'Í'; break; + case 'O': ch = 'Ó'; break; + case 'U': ch = 'Ú'; break; + } + } + else if (strcmp(&buf[1], "grave") == 0) { + switch (*buf) { + case 'a': ch = 'à'; break; + case 'e': ch = 'è'; break; + case 'i': ch = 'ì'; break; + case 'o': ch = 'ò'; break; + case 'u': ch = 'ù'; break; + case 'A': ch = 'À'; break; + case 'E': ch = 'È'; break; + case 'I': ch = 'Ì'; break; + case 'O': ch = 'Ò'; break; + case 'U': ch = 'Ù'; break; + } + } + else if (strcmp(&buf[1], "tilde") == 0) { + switch (*buf) { + case 'a': ch = 'ã'; break; + case 'o': ch = 'õ'; break; + case 'n': ch = 'ñ'; break; + case 'A': ch = 'ã'; break; + case 'O': ch = 'Õ'; break; + case 'N': ch = 'Ñ'; break; + } + } + else if (strcmp(&buf[1], "circ") == 0) { + switch (*buf) { + case 'a': ch = 'â'; break; + case 'e': ch = 'ê'; break; + case 'i': ch = 'î'; break; + case 'o': ch = 'ô'; break; + case 'u': ch = 'û'; break; + case 'A': ch = 'Â'; break; + case 'E': ch = 'Ê'; break; + case 'I': ch = 'Î'; break; + case 'O': ch = 'Ô'; break; + case 'U': ch = 'Û'; break; + } + } + else if (strcmp(&buf[1], "cedil") == 0) { + switch (*buf) { + case 'c': ch = 'ç'; break; + case 'C': ch = 'Ç'; break; + } + } + /* add more cases here */ + } + + return (ch); +} + +/*ARGSUSED*/ +static int +IspellSend(void) +{ + XawTextPosition position, old_left, pos; + XawTextBlock block; + int i, len, spaces, nls; + Bool nl, html, inside_html; + char ampbuf[32]; + int amplen; + + if (ispell.lock || ispell.stat != SEND) + return (-1); + + len = 1; + ispell.sendbuf[0] = '^'; /* don't evaluate following characters as commands */ + + spaces = nls = 0; + + html = ispell.format_info->value == HTML; + inside_html = False; + amplen = 0; + + /* skip non word characters */ + pos = position = ispell.right; + nl = False; + while (1) { + Bool done = False; + char mb[sizeof(wchar_t)]; + + retry_html_space: + position = XawTextSourceRead(ispell.source, position, + &block, BUFSIZ); + if (block.length == 0) { /* end of file */ + ispell.stat = 0; + ispell.lock = True; + XawTextSetInsertionPoint(ispell.ascii, ispell.right); + XawTextUnsetSelection(ispell.ascii); + IspellSetSensitive(False); + IspellSetStatus(ispell.eof_label); + return (-1); + } + for (i = 0; i < block.length; i++) { + if (international) + wctomb(mb, ((wchar_t*)block.ptr)[i]); + else + *mb = block.ptr[i]; + if (amplen) { + if (amplen + 2 >= sizeof(ampbuf)) { + if (!ispell.terse_mode) + fprintf(stderr, "Warning: error interpreting '&' mark.\n"); + amplen = 0; + position = pos + 1; + goto retry_html_space; + } + else if ((ampbuf[amplen++] = *mb) == ';') { + int ch; + + ampbuf[amplen] = '\0'; + ch = IspellConvertHtmlAmp(ampbuf); + amplen = 0; + if (isalpha(ch) || + (ch && strchr(ispell.wchars, ch))) { + /* interpret it again */ + ispell.right = pos; + i = 0; + done = True; + break; + } + else if ((ch == '\n' || isspace(ch)) && spaces >= 0) + ++spaces; + else + spaces = -1; + } + } + else if (html && *mb == '&') { + ampbuf[amplen++] = *mb; + pos = block.firstPos + i; + continue; + } + else if ((!html || !inside_html) && (isalpha(*mb) || + (*mb && strchr(ispell.wchars, *mb)))) { + done = True; + break; + } + else if (!html && *mb == '\n') { + nl = True; + if (++nls > 1 && (!html || !inside_html)) + spaces = -1; + else if (spaces >= 0) + ++spaces; + } + else if (nl) { + nl = False; + if (*mb && strchr(ispell.skip, *mb)) { + position = ispell.right = + XawTextSourceScan(ispell.source, ispell.right + i, + XawstEOL, XawsdRight, 1, False); + i = 0; + break; + } + else if (spaces >= 0 && isspace(*mb)) + ++spaces; + else + spaces = -1; + } + else if (html && inside_html) { + if (*mb == '>') + inside_html = False; + } + else if (html && *mb == '<') + inside_html = True; + else if (spaces >= 0 && (isspace(*mb) || (html && *mb == '\n'))) + ++spaces; + else + spaces = -1; + } + + ispell.right += i; + if (done) + break; + } + + old_left = ispell.left; + + /* read a word */ + position = ispell.left = ispell.right; + while (1) { + Bool done = False; + char mb[sizeof(wchar_t)]; + + retry_html_word: + position = XawTextSourceRead(ispell.source, position, + &block, BUFSIZ); + if (block.length == 0 && len == 1) { /* end of file */ + ispell.stat = 0; + ispell.lock = True; + XawTextSetInsertionPoint(ispell.ascii, ispell.right); + XawTextUnsetSelection(ispell.ascii); + IspellSetSensitive(False); + IspellSetStatus(ispell.eof_label); + return (-1); + } + for (i = 0; i < block.length; i++) { + if (international) + wctomb(mb, ((wchar_t*)block.ptr)[i]); + else + *mb = block.ptr[i]; + if (amplen) { + if (amplen + 2 >= sizeof(ampbuf)) { + if (!ispell.terse_mode) + fprintf(stderr, "Warning: error interpreting '&' mark.\n"); + amplen = 0; + position = pos + 1; + if (strchr(ispell.wchars, '&')) { + if (len + 1 >= sizeof(ispell.sendbuf) - 1) { + done = True; + fprintf(stderr, "Warning: word is too large!\n"); + break; + } + ispell.sendbuf[len++] = '&'; + goto retry_html_word; + } + else { + ispell.right = position; + i = 0; + done = True; + break; + } + } + else if ((ampbuf[amplen++] = *mb) == ';') { + int ch; + + ampbuf[amplen] = '\0'; + ch = IspellConvertHtmlAmp(ampbuf); + amplen = 0; + if (!isalpha(ch) && + (!ch || !strchr(ispell.wchars, ch))) { + ispell.right = pos; + i = 0; + done = True; + break; + } + *mb = ch; + } + else + continue; + } + else if (html && *mb == '&') { + ampbuf[amplen++] = *mb; + pos = block.firstPos + i; + continue; + } + else if (!isalpha(*mb) && (!*mb || !strchr(ispell.wchars, *mb))) { + done = True; + break; + } + ispell.sendbuf[len] = *mb; + if (++len >= sizeof(ispell.sendbuf) - 1) { + done = True; + fprintf(stderr, "Warning: word is too large!\n"); + break; + } + } + ispell.right += i; + if (done || block.length == 0) + break; + } + + ispell.sendbuf[len] = '\0'; + + if (spaces > 0 && spaces <= 32 && strcmp(ispell.sendbuf, ispell.sentbuf) == 0) { + Arg args[2]; + int old_len; + char **list, **old_list; + char label[sizeof(ispell.sendbuf) + sizeof(ispell.sentbuf) + 32]; + + strcpy(label, &ispell.sendbuf[1]); + for (i = 0; i < spaces; i++) + label[len + i - 1] = ' '; + strcpy(&label[len + i - 1], &ispell.sendbuf[1]); + XtSetArg(args[0], XtNlabel, label); + XtSetValues(ispell.word, args, 1); + + XtSetArg(args[0], XtNstring, &ispell.sendbuf[1]); + XtSetValues(ispell.text, args, 1); + + XtSetArg(args[0], XtNlist, &old_list); + XtSetArg(args[1], XtNnumberStrings, &old_len); + XtGetValues(ispell.list, args, 2); + list = (char**)XtMalloc(sizeof(char**)); + list[0] = XtNewString(&ispell.sendbuf[1]); + XtSetArg(args[0], XtNlist, list); + XtSetArg(args[1], XtNnumberStrings, 1); + XtSetValues(ispell.list, args, 2); + XtSetSensitive(ispell.list, True); + XawListHighlight(ispell.list, 0); + if (old_len > 1 || (XtName(ispell.list) != old_list[0])) { + while (--old_len > -1) + XtFree(old_list[old_len]); + XtFree((char*)old_list); + } + + IspellSetRepeated(True); + IspellSetSelection(old_left, ispell.right); + IspellSetStatus(ispell.repeat_label); + ispell.repeat = ispell.lock = True; + + return (1); + } + strcpy(ispell.sentbuf, ispell.sendbuf); + + if (len <= 2 || IspellIgnoredWord(&ispell.sendbuf[1], CHECK, 0)) + return (0); + + ispell.sendbuf[len++] = '\n'; + + write(ispell.ofd[1], ispell.sendbuf, len); + + ispell.stat = RECEIVE; + + return (1); +} + +/*ARGSUSED*/ +static void +IspellInputCallback(XtPointer closure, int *source, XtInputId *id) +{ + if (ispell.right < 0) { + int len; + char buf[1024]; + + ispell.right = XawTextGetInsertionPoint(ispell.ascii); + ispell.right = XawTextSourceScan(ispell.source, ispell.right, + XawstEOL, XawsdLeft, 1, True); + len = read(ispell.ifd[0], buf, sizeof(buf)); + if (strncmp(buf, "@(#)", 4) == 0) { + Arg args[1]; + + buf[len - 1] = '\0'; + XtSetArg(args[0], XtNtitle, &buf[5]); + XtSetValues(ispell.shell, args, 1); + } + else + fprintf(stderr, "Error: is ispell talking with me?\n"); + IspellSetTerseMode(ispell.terse_mode); + while (IspellSend() == 0) + ; + } + else if (ispell.source) + IspellReceive(); +} + +/*ARGSUSED*/ +void +IspellCallback(Widget w, XtPointer client_data, XtPointer call_data) +{ + Cardinal zero = 0; + + IspellAction(textwindow, NULL, NULL, &zero); +} + +/*ARGSUSED*/ +void +IspellAction(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + Arg args[3]; + Cardinal num_args; + char **strs, **list; + int n_strs; + Bool first_time = InitIspell(); + + if (*num_params == 1 && (params[0][0] == 'e' || params[0][0] == 'E')) { + PopdownIspell(w, (XtPointer)True, NULL); + return; + } + + if (!XtIsSubclass(w, textWidgetClass) || ispell.source) { + Feep(); + return; + } + + ispell.source = XawTextGetSource(ispell.ascii = w); + + if (first_time) { + /* let the user choose the better position for the ispell window */ + Dimension width, height, b_width; + Position x, y, max_x, max_y; + + x = y = -1; + if (event) { + switch (event->type) { + case ButtonPress: + case ButtonRelease: + x = event->xbutton.x_root; + y = event->xbutton.y_root; + break; + case KeyPress: + case KeyRelease: + x = event->xkey.x_root; + y = event->xkey.y_root; + break; + } + } + if (x < 0 || y < 0) { + Window r, c; + int rx, ry, wx, wy; + unsigned mask; + + XQueryPointer(XtDisplay(ispell.shell), XtWindow(ispell.shell), + &r, &c, &rx, &ry, &wx, &wy, &mask); + x = rx; + y = ry; + } + + num_args = 0; + XtSetArg(args[num_args], XtNwidth, &width); num_args++; + XtSetArg(args[num_args], XtNheight, &height); num_args++; + XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++; + XtGetValues(ispell.shell, args, num_args); + + width += b_width << 1; + height += b_width << 1; + + x -= (Position)(width >> 1); + if (x < 0) + x = 0; + if (x > (max_x = (Position)(XtScreen(w)->width - width))) + x = max_x; + + y -= (Position)(height >> 1); + if (y < 0) + y = 0; + if (y > (max_y = (Position)(XtScreen(w)->height - height))) + y = max_y; + + num_args = 0; + XtSetArg(args[num_args], XtNx, x); num_args++; + XtSetArg(args[num_args], XtNy, y); num_args++; + XtSetValues(ispell.shell, args, num_args); + } + + if (ispell.repeat) + IspellSetRepeated(False); + ispell.lock = ispell.repeat = ispell.checkit = False; + ispell.stat = SEND; + + IspellSetSensitive(True); + XtSetSensitive(ispell.undo, False); + + XtSetArg(args[0], XtNlabel, ""); + XtSetValues(ispell.word, args, 1); + + XtSetArg(args[0], XtNstring, ""); + XtSetValues(ispell.text, args, 1); + + XtSetArg(args[0], XtNlist, &strs); + XtSetArg(args[1], XtNnumberStrings, &n_strs); + XtGetValues(ispell.list, args, 2); + + list = (char**)XtMalloc(sizeof(char**)); + list[0] = XtNewString(""); + XtSetArg(args[0], XtNlist, list); + XtSetArg(args[1], XtNnumberStrings, 1); + XtSetValues(ispell.list, args, 2); + + if (n_strs > 1 || (XtName(ispell.list) != strs[0])) { + while (--n_strs > -1) + XtFree(strs[n_strs]); + XtFree((char*)strs); + } + + IspellSetStatus(ispell.working_label); + + if (!ispell.pid) + (void)IspellStartProcess(); + else { + ispell.right = XawTextGetInsertionPoint(ispell.ascii); + ispell.right = XawTextSourceScan(ispell.source, ispell.right, + XawstEOL, XawsdLeft, 1, True); + while (IspellSend() == 0) + ; + } + + XtPopup(ispell.shell, XtGrabExclusive); + XtSetKeyboardFocus(ispell.shell, ispell.text); +} + +static Bool +IspellStartProcess(void) +{ + if (!ispell.pid) { + int len; + char *command; + + ispell.source = XawTextGetSource(ispell.ascii); + + len = strlen(ispell.cmd) + strlen(ispell.dictionary) + + strlen(ispell.wchars) + 16; + command = XtMalloc(len); + XmuSnprintf(command, len, "%s -a -d '%s' -w '%s'", + ispell.cmd, ispell.dictionary, ispell.wchars); + + pipe(ispell.ifd); + pipe(ispell.ofd); + if ((ispell.pid = fork()) == 0) { + close(0); + close(1); + dup2(ispell.ofd[0], 0); + dup2(ispell.ifd[1], 1); + close(ispell.ofd[0]); + close(ispell.ofd[1]); + close(ispell.ifd[0]); + close(ispell.ifd[1]); + execl("/bin/sh", "sh", "-c", command, (void *)NULL); + exit(-127); + } + else if (ispell.pid < 0) { + fprintf(stderr, "Cannot fork\n"); + exit(1); + } + ispell.buf = XtMalloc(ispell.bufsiz = BUFSIZ); + ispell.right = -1; + ispell.id = XtAppAddInput(XtWidgetToApplicationContext(ispell.shell), + ispell.ifd[0], (XtPointer)XtInputReadMask, + IspellInputCallback, NULL); + fcntl(ispell.ifd[0], F_SETFL, O_NONBLOCK); + } + else + return (False); + + return (True); +} + +/*ARGSUSED*/ +static void +PopdownIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + (void)IspellEndProcess((Bool)(long)client_data, True); + XtPopdown(ispell.shell); + *ispell.sentbuf = '\0'; +} + +static Bool +IspellEndProcess(Bool killit, Bool killundo) +{ + ispell.source = NULL; + + if (ispell.pid) { + IgnoreList *il, *pil, *nil; + int i; + + /* insert added words in private dictionary */ + for (i = 0; i < ISTRTBLSZ; i++) { + pil = il = ignore_list[i]; + while (il) { + if (il->add) { + nil = il->next; + if (il == pil) + ignore_list[i] = nil; + else + pil->next = nil; + if (il->add == UNCAP) + write(ispell.ofd[1], "&", 1); + else + write(ispell.ofd[1], "*", 1); + write(ispell.ofd[1], il->word, strlen(il->word)); + write(ispell.ofd[1], "\n", 1); + XtFree(il->word); + XtFree((char*)il); + il = nil; + } + else + il = il->next; + pil = il; + } + } + write(ispell.ofd[1], "#\n", 2); /* save dictionary */ + + if (killit) { + ReplaceList *rl, *prl; + + XtRemoveInput(ispell.id); + + close(ispell.ofd[0]); + close(ispell.ofd[1]); + close(ispell.ifd[0]); + close(ispell.ifd[1]); + + /* if something goes wrong, we don't want to block here forever */ + old_timeout = signal(SIGALRM, timeout_signal); + alarm(10); + waitpid(ispell.pid, NULL, 0); + alarm(0); + signal(SIGALRM, old_timeout); + + ispell.pid = 0; + if (ispell.buf) + XtFree(ispell.buf); + ispell.buf = NULL; + + for (i = 0; i < RSTRTBLSZ; i++) { + prl = rl = replace_list[i]; + while (prl) { + rl = rl->next; + XtFree(prl->word); + XtFree(prl->replace); + XtFree((char*)prl); + prl = rl; + } + replace_list[i] = NULL; + } + for (i = 0; i < ISTRTBLSZ; i++) { + pil = il = ignore_list[i]; + while (pil) { + il = il->next; + XtFree(pil->word); + XtFree((char*)pil); + pil = il; + } + ignore_list[i] = NULL; + } + } + + if (killundo) + IspellKillUndoBuffer(); + } + else + return (False); + + return (True); +} + +static void +IspellKillUndoBuffer(void) +{ + ispell_undo *undo, *pundo; + + undo = pundo = ispell.undo_base; + while (undo) { + undo = undo->next; + if (pundo->undo_str) + XtFree(pundo->undo_str); + XtFree((char*)pundo); + pundo = undo; + } + ispell.undo_base = ispell.undo_head = NULL; + ispell.undo_for = NULL; + ispell.undo_depth = 0; + XtSetSensitive(ispell.undo, False); +} + +/*ARGSUSED*/ +static void +RevertIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + Arg args[1]; + char *string, *repstr = NULL; + + XtSetArg(args[0], XtNlabel, &string); + XtGetValues(ispell.word, args, 1); + if ((repstr = strchr(string, ' ')) != NULL) { + string = repstr = XtNewString(string); + *strchr(repstr, ' ') = '\0'; + } + XtSetArg(args[0], XtNstring, string); + XtSetValues(ispell.text, args, 1); + if (repstr) + XtFree(repstr); +} + +/*ARGSUSED*/ +static void +SelectIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + XawListReturnStruct *info = (XawListReturnStruct *)call_data; + Arg args[1]; + + XtSetArg(args[0], XtNstring, ispell.item = info->string); + XtSetValues(ispell.text, args, 1); +} + +/*ARGSUSED*/ +void +ReplaceIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + XawTextPosition pos = XawTextGetInsertionPoint(ispell.ascii); + XawTextBlock check, search, replace; + Arg args[1]; + char *text; + + if (!ispell.lock) + return; + + XtSetArg(args[0], XtNlabel, &text); + XtGetValues(ispell.word, args, 1); + search.ptr = text; + search.format = XawFmt8Bit; + search.firstPos = 0; + search.length = ispell.right - pos; + + XtSetArg(args[0], XtNstring, &text); + XtGetValues(ispell.text, args, 1); + replace.ptr = text; + replace.format = XawFmt8Bit; + replace.firstPos = 0; + replace.length = strlen(text); + + if (strcmp(search.ptr, replace.ptr) != 0 && + XawTextReplace(ispell.ascii, pos, pos + search.length, + &replace) == XawEditDone) { + ispell.right += replace.length - search.length; + IspellCheckUndo(); + ispell.undo_head->undo_str = NULL; + ispell.undo_head->undo_pos = pos; + ispell.undo_head->undo_count = 1; + + if (ispell.repeat) { + ispell.undo_head->repeat = 2; /* To recognize later it was replaced */ + ispell.undo_head->undo_count = ispell.right; + ispell.undo_head->undo_str = XtNewString(search.ptr); + } + if (client_data && !ispell.repeat) { + XawTextDisableRedisplay(ispell.ascii); + pos = ispell.right; + while ((pos = XawTextSourceSearch(ispell.source, pos, XawsdRight, &search)) + != XawTextSearchError) { + Bool do_replace = True; + char mb[sizeof(wchar_t)]; + + if (XawTextSourceRead(ispell.source, pos - 1, &check, 1) > 0) { + if (international) + wctomb(mb, *(wchar_t*)check.ptr); + else + *mb = *check.ptr; + do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb); + } + if (do_replace && + XawTextSourceRead(ispell.source, pos + search.length, &check, 1) > 0) { + if (international) + wctomb(mb, *(wchar_t*)check.ptr); + else + *mb = *check.ptr; + do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb); + } + if (do_replace) { + XawTextReplace(ispell.ascii, pos, pos + search.length, &replace); + ++ispell.undo_head->undo_count; + } + pos += search.length; + } + XawTextEnableRedisplay(ispell.ascii); + } + (void)IspellReplacedWord(search.ptr, replace.ptr); + + strncpy(&ispell.sentbuf[1], replace.ptr, sizeof(ispell.sentbuf) - 2); + ispell.sentbuf[sizeof(ispell.sentbuf) - 1] = '\0'; + } + else + Feep(); + + if (ispell.repeat) + ispell.right = ispell.left = XawTextGetInsertionPoint(ispell.ascii); + else if (!ispell.terse_mode || !ispell.item || + strcmp(ispell.item, replace.ptr)) + ispell.right = ispell.left; /* check it again! */ + + ispell.lock = ispell.checkit = False; + + ispell.stat = SEND; + IspellSetStatus(ispell.working_label); + while (IspellSend() == 0) + ; +} + +/*ARGSUSED*/ +void +IgnoreIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + Arg args[1]; + char *text; + + if (!ispell.lock) + return; + + XtSetArg(args[0], XtNlabel, &text); + XtGetValues(ispell.word, args, 1); + + IspellCheckUndo(); + + if ((ispell.undo_head->repeat = ispell.repeat) != False) { + ispell.undo_head->undo_count = ispell.right; + ispell.undo_head->undo_str = XtNewString(text); + } + else + ispell.undo_head->undo_count = 0; + + ispell.undo_head->undo_pos = XawTextGetInsertionPoint(ispell.ascii); + + if (!ispell.repeat) { + if (client_data) { + IspellIgnoredWord(text, ADD, 0); + ispell.undo_head->undo_str = XtNewString(text); + } + else + ispell.undo_head->undo_str = NULL; + } + + ispell.lock = ispell.checkit = False; + + ispell.stat = SEND; + IspellSetStatus(ispell.working_label); + while (IspellSend() == 0) + ; +} + +/*ARGSUSED*/ +void +AddIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + Arg args[1]; + char *text; + int cmd = (long)client_data; + + if (!ispell.lock || ispell.repeat) + return; + + XtSetArg(args[0], XtNlabel, &text); + XtGetValues(ispell.word, args, 1); + + IspellCheckUndo(); + ispell.undo_head->undo_str = XtNewString(text); + ispell.undo_head->undo_pos = XawTextGetInsertionPoint(ispell.ascii); + ispell.undo_head->undo_count = -cmd; + + (void)IspellIgnoredWord(text, ADD, cmd); + + ispell.lock = ispell.checkit = False; + ispell.stat = SEND; + IspellSetStatus(ispell.working_label); + while (IspellSend() == 0) + ; +} + +/*ARGSUSED*/ +static void +UndoIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + Bool enable_redisplay = False; + ispell_undo *undo = ispell.undo_head; + + if ((!ispell.lock && ispell.stat) || !undo) + return; + + if (ispell.undo_for && strcmp(ispell.undo_for, ispell.dictionary)) { + XeditPrintf("Undo: Dictionary changed. Undo information was lost.\n"); + IspellKillUndoBuffer(); + Feep(); + return; + } + + if (undo->terse != ispell.terse_mode) + IspellSetTerseMode(undo->terse); + + if (undo->format != ispell.format_info->value) { + struct _ispell_format *fmt = &ispell_format[undo->format]; + ChangeFormatIspell(fmt->sme, (XtPointer)fmt, NULL); + } + + if (undo->undo_count > 0 && !undo->repeat) { + XawTextPosition tmp; + + enable_redisplay = undo->undo_count > 1; + if (enable_redisplay) + XawTextDisableRedisplay(ispell.ascii); + while (undo->undo_count--) + if (!_XawTextSrcUndo((TextSrcObject)ispell.source, &tmp)) { + Feep(); + break; + } + } + else if (undo->undo_count < 0) { + if (undo->undo_str) + (void)IspellIgnoredWord(undo->undo_str, REMOVE, -undo->undo_count); + } + else if (undo->undo_str) { + if (!undo->repeat) + IspellIgnoredWord(undo->undo_str, REMOVE, 0); + } + + XawTextSetInsertionPoint(ispell.ascii, + ispell.right = ispell.left = undo->undo_pos); + if (enable_redisplay) + XawTextEnableRedisplay(ispell.ascii); + + /* need to do it because may be two misspelled words together */ + if (undo->repeat) { + char **list, **old_list; + int old_len; + Arg args[2]; + + if (undo->repeat > 1) { + XawTextDisableRedisplay(ispell.ascii); + if (!_XawTextSrcUndo((TextSrcObject)ispell.source, &ispell.right)) + Feep(); + XawTextEnableRedisplay(ispell.ascii); + } + else + ispell.right = (XawTextPosition)undo->undo_count; + IspellSetRepeated(ispell.repeat = True); + XtSetArg(args[0], XtNlabel, undo->undo_str); + XtSetValues(ispell.word, args, 1); + XmuSnprintf(ispell.sentbuf, sizeof(ispell.sentbuf), "^%s", + strrchr(undo->undo_str, ' ') + 1); + strcpy(ispell.sendbuf, ispell.sentbuf); + XtSetArg(args[0], XtNstring, &ispell.sentbuf[1]); + XtSetValues(ispell.text, args, 1); + + XtSetArg(args[0], XtNlist, &old_list); + XtSetArg(args[1], XtNnumberStrings, &old_len); + XtGetValues(ispell.list, args, 2); + + list = (char **)XtMalloc(sizeof(char*)); + list[0] = XtNewString(&ispell.sentbuf[1]); + XtSetArg(args[0], XtNlist, list); + XtSetArg(args[1], XtNnumberStrings, 1); + XtSetValues(ispell.list, args, 2); + XtSetSensitive(ispell.list, True); + XawListHighlight(ispell.list, 0); + + if (old_len > 1 || (XtName(ispell.list) != old_list[0])) { + while (--old_len > -1) + XtFree(old_list[old_len]); + XtFree((char*)old_list); + } + + IspellSetSelection(ispell.left, ispell.right); + IspellSetStatus(ispell.repeat_label); + ispell.lock = True; + ispell.checkit = False; + } + else if (ispell.repeat) { + *ispell.sentbuf = '\0'; + IspellSetRepeated(ispell.repeat = False); + } + + if (undo->prev) + undo->prev->next = NULL; + ispell.undo_head = undo->prev; + if (undo == ispell.undo_base) { + ispell.undo_base = NULL; + ispell.undo_for = NULL; + XtSetSensitive(ispell.undo, False); + } + if (undo->undo_str) + XtFree(undo->undo_str); + XtFree((char*)undo); + --ispell.undo_depth; + + if (!ispell.stat || ispell.checkit) + IspellSetSensitive(True); + + if (!ispell.repeat) { + ispell.lock = ispell.checkit = False; + ispell.stat = SEND; + IspellSetStatus(ispell.working_label); + while (IspellSend() == 0) + ; + } +} + +/*ARGSUSED*/ +static void +CheckIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + Arg args[1]; + char *text, *str, string[1024]; + int i, len; + + if (!ispell.lock) + return; + + XtSetArg(args[0], XtNstring, &text); + XtGetValues(ispell.text, args, 1); + + /* Check only a word at a time */ + len = 0; + str = text; + while (*str) { + if (isalpha(*str) || strchr(ispell.wchars, *str)) + break; + ++str; + ++len; + } + i = 0; + while (*str) { + if (isalpha(*str) || strchr(ispell.wchars, *str)) + string[i++] = *str++; + else + break; + } + string[i] = '\0'; + + if (strcmp(text, string)) { + XawTextPosition pos = XawTextGetInsertionPoint(ispell.text) - len; + + XtSetArg(args[0], XtNstring, string); + XtSetValues(ispell.text, args, 1); + XawTextSetInsertionPoint(ispell.text, pos); + Feep(); + } + + if (i == 0) { + Feep(); + return; + } + + len = XmuSnprintf(ispell.sendbuf, sizeof(ispell.sendbuf), "^%s\n", string); + + ispell.sendbuf[sizeof(ispell.sendbuf) - 1] = '\n'; + + write(ispell.ofd[1], ispell.sendbuf, len); + + ispell.lock = False; + ispell.checkit = True; + ispell.stat = RECEIVE; +} + +/*ARGSUSED*/ +static void +LookIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + int len, old_len; + FILE *fd; + Arg args[2]; + char *text, *str, **list, **old_list, command[1024], buffer[1024]; + Bool sensitive = True; + + if (!ispell.lock) + return; + + XtSetArg(args[0], XtNstring, &text); + XtGetValues(ispell.text, args, 1); + + if (!*text) { + Feep(); + return; + } + + if (strlen(ispell.look_cmd) + strlen(text) + strlen(ispell.words_file) + 8 + > sizeof(command) - 1) { + fprintf(stderr, "Command line too large\n"); + return; + } + + XmuSnprintf(command, sizeof(command), "%s '^%s.*$' %s", + ispell.look_cmd, text, ispell.words_file); + + if ((fd = popen(command, "r")) == NULL) { + fprintf(stderr, "Cannot popen '%s'\n", ispell.look_cmd); + return; + } + + list = NULL; + len = 0; + +#define MAX_LOOK_RESULTS 256 + while (fgets(buffer, sizeof(buffer), fd) != NULL) { + if ((str = strchr(buffer, '\n')) == NULL) { + fprintf(stderr, "String is too large\n"); + break; + } + *str = '\0'; + if ((len % 16) == 0) + list = (char**)XtRealloc((char*)list, sizeof(char*) * (len + 16)); + list[len] = XtNewString(buffer); + if (++len >= MAX_LOOK_RESULTS) { + Feep(); + break; + } + } +#undef MAX_LOOK_RESULTS + + XtSetArg(args[0], XtNlist, &old_list); + XtSetArg(args[1], XtNnumberStrings, &old_len); + XtGetValues(ispell.list, args, 2); + + if (len == 0) { + list = (char**)XtMalloc(sizeof(char*)); + list[0] = XtNewString(""); + len = 1; + sensitive = False; + } + + XtSetArg(args[0], XtNlist, list); + XtSetArg(args[1], XtNnumberStrings, len); + XtSetValues(ispell.list, args, 2); + + XtSetSensitive(ispell.list, sensitive); + IspellSetStatus(sensitive ? ispell.look_label : ispell.none_label); + + if (old_len > 1 || (XtName(ispell.list) != old_list[0])) { + while (--old_len > -1) + XtFree(old_list[old_len]); + XtFree((char*)old_list); + } + + pclose(fd); +} + +/*ARGSUSED*/ +static void +ToggleTerseIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + if (!ispell.lock) + return; + + ispell.terse_mode = !ispell.terse_mode; + write(ispell.ofd[1], ispell.terse_mode ? "!\n" : "%\n", 2); +} + +/*ARGSUSED*/ +static void +ChangeDictionaryIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + ispell_dict *tmp, *dic = (ispell_dict*)client_data; + XawTextPosition pos = XawTextGetInsertionPoint(ispell.ascii); + XawTextPosition right = ispell.right; + Arg args[1]; + + if (strcmp(XtName(dic->sme), ispell.dictionary) == 0) + return; + + if (!ispell.lock) { + Feep(); + return; + } + + for (tmp = ispell.dict_info; tmp; tmp = tmp->next) + if (strcmp(XtName(tmp->sme), ispell.dictionary) == 0) { + XtSetArg(args[0], XtNleftBitmap, None); + XtSetValues(tmp->sme, args, 1); + } + + if (ispell.undo_base && !ispell.undo_for) + ispell.undo_for = ispell.dictionary; + + XtSetArg(args[0], XtNleftBitmap, flist.pixmap); + XtSetValues(dic->sme, args, 1); + ispell.dictionary = XtName(dic->sme); + ispell.wchars = dic->wchars; + XtSetArg(args[0], XtNlabel, XtName(dic->sme)); + XtSetValues(ispell.dict, args, 1); + + IspellSetStatus(ispell.working_label); + + (void)IspellEndProcess(True, False); + ispell.lock = ispell.checkit = False; + (void)IspellStartProcess(); + + ispell.stat = RECEIVE; + + /* restart at the same selected word */ + if (ispell.repeat == False) + ispell.left = ispell.right = pos; + else + ispell.right = right; +} + +/*ARGSUSED*/ +static void +ChangeFormatIspell(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct _ispell_format *fmt = (struct _ispell_format*)client_data; + Arg args[1]; + + if (strcmp(fmt->name, ispell.formatting) == 0) + return; + + if (!ispell.lock) { + Feep(); + return; + } + + XtSetArg(args[0], XtNleftBitmap, None); + XtSetValues(ispell.format_info->sme, args, 1); + + XtSetArg(args[0], XtNleftBitmap, flist.pixmap); + XtSetValues(fmt->sme, args, 1); + ispell.formatting = fmt->name; + ispell.format_info = fmt; + XtSetArg(args[0], XtNlabel, fmt->name); + XtSetValues(ispell.format, args, 1); +} + +static Bool +InitIspell(void) +{ + Atom delete_window; + char *str, *list; + XtResource dict_res; + int i; + static XtResource text_res[] = { + {"skipLines", "Skip", XtRString, sizeof(char*), + XtOffsetOf(struct _ispell, skip), XtRString, "#"}, + }; + + if (ispell.shell) + return (False); + + ispell.shell = XtCreatePopupShell("ispell", transientShellWidgetClass, + topwindow, NULL, 0); + + XtGetApplicationResources(ispell.shell, (XtPointer)&ispell, resources, + XtNumber(resources), NULL, 0); + + ispell.form = XtCreateManagedWidget("form", formWidgetClass, + ispell.shell, NULL, 0); + ispell.mispelled = XtCreateManagedWidget("mispelled", labelWidgetClass, + ispell.form, NULL, 0); + ispell.repeated = XtCreateWidget("repeated", labelWidgetClass, + ispell.form, NULL, 0); + ispell.word = XtCreateManagedWidget("word", commandWidgetClass, + ispell.form, NULL, 0); + XtAddCallback(ispell.word, XtNcallback, RevertIspell, NULL); + ispell.replacement = XtCreateManagedWidget("replacement", labelWidgetClass, + ispell.form, NULL, 0); + ispell.text = XtVaCreateManagedWidget("text", asciiTextWidgetClass, + ispell.form, + XtNeditType, XawtextEdit, + NULL, 0); + ispell.suggestions = XtCreateManagedWidget("suggestions", labelWidgetClass, + ispell.form, NULL, 0); + ispell.viewport = XtCreateManagedWidget("viewport", viewportWidgetClass, + ispell.form, NULL, 0); + ispell.list = XtCreateManagedWidget("list", listWidgetClass, + ispell.viewport, NULL, 0); + XtAddCallback(ispell.list, XtNcallback, SelectIspell, NULL); + ispell.commands = XtCreateManagedWidget("commands", formWidgetClass, + ispell.form, NULL, 0); + ispell.check = XtCreateManagedWidget("check", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.check, XtNcallback, CheckIspell, NULL); + ispell.look = XtCreateManagedWidget("look", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.look, XtNcallback, LookIspell, NULL); + ispell.undo = XtCreateManagedWidget("undo", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.undo, XtNcallback, UndoIspell, NULL); + ispell.replace = XtCreateManagedWidget("replace", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.replace, XtNcallback, ReplaceIspell, (XtPointer)False); + ispell.replaceAll = XtCreateManagedWidget("replaceAll", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.replaceAll, XtNcallback, ReplaceIspell, (XtPointer)True); + ispell.ignore = XtCreateManagedWidget("ignore", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.ignore, XtNcallback, IgnoreIspell, (XtPointer)False); + ispell.ignoreAll = XtCreateManagedWidget("ignoreAll", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.ignoreAll, XtNcallback, IgnoreIspell, (XtPointer)True); + ispell.add = XtCreateManagedWidget("add", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.add, XtNcallback, AddIspell, (XtPointer)ASIS); + ispell.addUncap = XtCreateManagedWidget("addUncap", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.addUncap, XtNcallback, AddIspell, (XtPointer)UNCAP); + ispell.suspend = XtCreateManagedWidget("suspend", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.suspend, XtNcallback, PopdownIspell, (XtPointer)False); + ispell.cancel = XtCreateManagedWidget("cancel", commandWidgetClass, + ispell.commands, NULL, 0); + XtAddCallback(ispell.cancel, XtNcallback, PopdownIspell, (XtPointer)True); + ispell.terse = XtVaCreateManagedWidget("terse", toggleWidgetClass, + ispell.commands, + XtNstate, ispell.terse_mode, + NULL, 0); + XtAddCallback(ispell.terse, XtNcallback, ToggleTerseIspell, NULL); + ispell.status = XtCreateManagedWidget("status", labelWidgetClass, + ispell.form, NULL, 0); + ispell.options = XtCreateManagedWidget("options", formWidgetClass, + ispell.form, NULL, 0); + ispell.dict = XtVaCreateManagedWidget("dict", menuButtonWidgetClass, + ispell.options, + XtNmenuName, "dictionaries", + NULL, 0); + ispell.dictMenu = XtCreatePopupShell("dictionaries", simpleMenuWidgetClass, + ispell.options, NULL, 0); + XtRealizeWidget(ispell.dictMenu); + + ispell.format = XtVaCreateManagedWidget("format", menuButtonWidgetClass, + ispell.options, + XtNmenuName, "formats", + NULL, 0); + ispell.formatMenu = XtCreatePopupShell("formats", simpleMenuWidgetClass, + ispell.options, NULL, 0); + XtRealizeWidget(ispell.formatMenu); + + XtRealizeWidget(ispell.shell); + + for (i = 0; i < sizeof(ispell_format) / sizeof(ispell_format[0]); i++) { + struct _ispell_format *fmt = &ispell_format[i]; + + fmt->sme = XtCreateManagedWidget(fmt->name, smeBSBObjectClass, + ispell.formatMenu, NULL, 0); + XtAddCallback(fmt->sme, XtNcallback, ChangeFormatIspell, (XtPointer)fmt); + + if (strcmp(fmt->name, ispell.formatting) == 0) { + Arg args[1]; + + XtSetArg(args[0], XtNlabel, ispell.formatting); + XtSetValues(ispell.format, args, 1); + XtSetArg(args[0], XtNleftBitmap, flist.pixmap); + XtSetValues(fmt->sme, args, 1); + ispell.format_info = fmt; + } + } + if (ispell.format_info == NULL) { + Arg args[1]; + char msg[256]; + + ispell.format_info = &ispell_format[TEXT]; + + XmuSnprintf(msg, sizeof(msg), + "Unrecognized formatting type \"%s\", will use \"%s\"", + ispell.formatting, ispell.format_info->name); + XtAppWarning(XtWidgetToApplicationContext(ispell.shell), msg); + ispell.formatting = ispell.format_info->name; + + XtSetArg(args[0], XtNlabel, ispell.format_info->name); + XtSetValues(ispell.format, args, 1); + XtSetArg(args[0], XtNleftBitmap, flist.pixmap); + XtSetValues(ispell.format_info->sme, args, 1); + } + XtGetApplicationResources(ispell_format[TEXT].sme, (XtPointer)&ispell, + text_res, XtNumber(text_res), NULL, 0); + + dict_res.resource_name = "wordChars"; + dict_res.resource_class = "Chars"; + dict_res.resource_type = XtRString; + dict_res.resource_size = sizeof(char*); + dict_res.resource_offset = XtOffsetOf(ispell_dict, wchars); + dict_res.default_type = XtRString; + dict_res.default_addr = ""; + + list = XtNewString(ispell.dict_list); + for (str = strtok(list, " \t,"); str; str = strtok(NULL, " \t,")) { + ispell_dict *dic = XtNew(ispell_dict); + + dic->sme = XtCreateManagedWidget(str, smeBSBObjectClass, + ispell.dictMenu, NULL, 0); + XtGetApplicationResources(dic->sme, (XtPointer)dic, &dict_res, + 1, NULL, 0); + XtAddCallback(dic->sme, XtNcallback, ChangeDictionaryIspell, + (XtPointer)dic); + dic->next = NULL; + if (!ispell.dict_info) + ispell.dict_info = dic; + else { + ispell_dict *tmp = ispell.dict_info; + + for (; tmp->next; tmp = tmp->next) + ; + tmp->next = dic; + } + if (strcmp(str, ispell.dictionary) == 0) { + Arg args[1]; + + XtSetArg(args[0], XtNleftBitmap, flist.pixmap); + XtSetValues(dic->sme, args, 1); + XtSetArg(args[0], XtNlabel, str); + XtSetValues(ispell.dict, args, 1); + ispell.wchars = dic->wchars; + } + } + XtFree(list); + + delete_window = XInternAtom(XtDisplay(ispell.shell), "WM_DELETE_WINDOW", False); + XSetWMProtocols(XtDisplay(ispell.shell), XtWindow(ispell.shell), &delete_window, 1); + + return (True); +} |