/* * 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 */ /* $XdotOrg: xc/programs/xedit/ispell.c,v 1.6 2004/12/04 00:43:13 kuhn Exp $ */ /* $XFree86: xc/programs/xedit/ispell.c,v 1.19 2002/10/19 20:04:20 herrb Exp $ */ #include "xedit.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 _ReplaceEntry ReplaceEntry; struct _ReplaceEntry { hash_key *word; ReplaceEntry*next; char *replace; }; typedef struct _IgnoreEntry IgnoreEntry; struct _IgnoreEntry { hash_key *word; IgnoreEntry *next; int add; }; /* * 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 IspellCheckProcess(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); static void timeout_signal(int); static void (*old_timeout)(int); static void UndoIspell(Widget, XtPointer, XtPointer); Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*); /* * Initialization */ static struct _ispell ispell; #define RSTRTBLSZ 23 #define ISTRTBLSZ 71 static hash_table *replace_hash; static hash_table *ignore_hash; #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*/ static void timeout_signal(int unused) { int olderrno = errno; WRITES("Warning: Timeout waiting ispell process to die.\n"); kill(ispell.pid, SIGTERM); errno = olderrno; } 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) { int word_len; hash_key *word_key; ReplaceEntry *entry; word_len = strlen(word); entry = (ReplaceEntry *)hash_check(replace_hash, word, word_len); if (entry == NULL) { word_key = XtNew(hash_key); word_key->value = XtNewString(word); word_key->length = word_len; entry = XtNew(ReplaceEntry); entry->word = word_key; entry->replace = NULL; entry->next = NULL; hash_put(replace_hash, (hash_entry *)entry); } if (replace) { XtFree(entry->replace); entry->replace = XtNewString(replace); } return (entry->replace); } static Bool IspellDoIgnoredWord(char *word, int cmd, int add) { int word_len; hash_key *word_key; IgnoreEntry *entry; word_len = strlen(word); entry = (IgnoreEntry *)hash_check(ignore_hash, word, word_len); if (entry == NULL) { if (cmd != ADD) return (False); word_key = XtNew(hash_key); word_key->value = XtNewString(word); word_key->length = word_len; entry = XtNew(IgnoreEntry); entry->word = word_key; entry->add = add; entry->next = NULL; hash_put(ignore_hash, (hash_entry *)entry); return (True); } else if (cmd == REMOVE) hash_rem(ignore_hash, (hash_entry *)entry); return (cmd == CHECK); } 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 = 0xe1; break; case 'e': ch = 0xe9; break; case 'i': ch = 0xed; break; case 'o': ch = 0xf3; break; case 'u': ch = 0xfa; break; case 'A': ch = 0xc1; break; case 'E': ch = 0xc9; break; case 'I': ch = 0xcd; break; case 'O': ch = 0xd3; break; case 'U': ch = 0xda; break; } } else if (strcmp(&buf[1], "grave") == 0) { switch (*buf) { case 'a': ch = 0xe0; break; case 'e': ch = 0xe8; break; case 'i': ch = 0xec; break; case 'o': ch = 0xf2; break; case 'u': ch = 0xf9; break; case 'A': ch = 0xc0; break; case 'E': ch = 0xc8; break; case 'I': ch = 0xcc; break; case 'O': ch = 0xd2; break; case 'U': ch = 0xd9; break; } } else if (strcmp(&buf[1], "tilde") == 0) { switch (*buf) { case 'a': ch = 0xe3; break; case 'o': ch = 0xf5; break; case 'n': ch = 0xf1; break; case 'A': ch = 0xe3; break; case 'O': ch = 0xd5; break; case 'N': ch = 0xd1; break; } } else if (strcmp(&buf[1], "circ") == 0) { switch (*buf) { case 'a': ch = 0xe2; break; case 'e': ch = 0xea; break; case 'i': ch = 0xee; break; case 'o': ch = 0xf4; break; case 'u': ch = 0xfb; break; case 'A': ch = 0xc2; break; case 'E': ch = 0xca; break; case 'I': ch = 0xce; break; case 'O': ch = 0xd4; break; case 'U': ch = 0xdb; break; } } else if (strcmp(&buf[1], "cedil") == 0) { switch (*buf) { case 'c': ch = 0xe7; break; case 'C': ch = 0xc7; 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[0] = 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[0] = 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 format[32]; static char *command; ispell.source = XawTextGetSource(ispell.ascii); if (command) XtFree(command); strcpy(format, "%s -a"); len = strlen(ispell.cmd) + 4; if (ispell.dictionary && *ispell.dictionary) { len += strlen(ispell.dictionary) + 6; strcat(format, " -d '%s'"); if (ispell.wchars && *ispell.wchars) { len += strlen(ispell.wchars + 6); strcat(format, " -w '%s'"); } } command = XtMalloc(len); XmuSnprintf(command, len, format, 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]); if (!international) setlocale(LC_ALL, "ISO-8859-1"); execl("/bin/sh", "sh", "-c", command, 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 IspellCheckProcess(void) { int status; if (ispell.pid) { waitpid(ispell.pid, &status, WNOHANG); if (WIFEXITED(status)) { ispell.pid = 0; } else return (True); } return (False); } static Bool IspellEndProcess(Bool killit, Bool killundo) { ispell.source = NULL; if (ispell.pid) { IgnoreEntry *ientry; ReplaceEntry *rentry; /* insert added words in private dictionary */ for (ientry = (IgnoreEntry *)hash_iter_first(ignore_hash); ientry; ientry = (IgnoreEntry *)hash_iter_next(ignore_hash)) { if (ientry->add) { if (ientry->add == UNCAP) write(ispell.ofd[1], "&", 1); else write(ispell.ofd[1], "*", 1); write(ispell.ofd[1], ientry->word->value, ientry->word->length); write(ispell.ofd[1], "\n", 1); } } write(ispell.ofd[1], "#\n", 2); /* save dictionary */ hash_clr(ignore_hash); if (killit) { 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; /* forget about replace matches */ for (rentry = (ReplaceEntry *)hash_iter_first(replace_hash); rentry; rentry = (ReplaceEntry *)hash_iter_next(replace_hash)) { XtFree(rentry->replace); } hash_clr(replace_hash); } 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[0] = check.ptr[0]; 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[0] = check.ptr[0]; 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) { if (IspellCheckProcess()) { 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; ispell_dict *dict, *prev_dict; int i; static XtResource text_res[] = { {"skipLines", "Skip", XtRString, sizeof(char*), XtOffsetOf(struct _ispell, skip), XtRString, "#"}, }; if (ispell.shell) return (False); replace_hash = hash_new(RSTRTBLSZ, NULL); ignore_hash = hash_new(ISTRTBLSZ, NULL); 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); 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); 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); ispell.dictMenu = XtCreatePopupShell("dictionaries", simpleMenuWidgetClass, ispell.options, NULL, 0); XtRealizeWidget(ispell.dictMenu); ispell.format = XtVaCreateManagedWidget("format", menuButtonWidgetClass, ispell.options, XtNmenuName, "formats", NULL); 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); /* Create first empty entry */ dict = XtNew(ispell_dict); dict->sme = XtCreateManagedWidget("", smeBSBObjectClass, ispell.dictMenu, NULL, 0); dict->wchars = ""; XtAddCallback(dict->sme, XtNcallback, ChangeDictionaryIspell, (XtPointer)dict); ispell.dict_info = prev_dict = dict; for (str = strtok(list, " \t,"); str; str = strtok(NULL, " \t,")) { dict = XtNew(ispell_dict); dict->sme = XtCreateManagedWidget(str, smeBSBObjectClass, ispell.dictMenu, NULL, 0); XtGetApplicationResources(dict->sme, (XtPointer)dict, &dict_res, 1, NULL, 0); XtAddCallback(dict->sme, XtNcallback, ChangeDictionaryIspell, (XtPointer)dict); prev_dict->next = dict; prev_dict = dict; dict->next = NULL; } XtFree(list); for (dict = ispell.dict_info; dict; dict = dict->next) { if (strcmp(XtName(dict->sme), ispell.dictionary) == 0) { Arg args[1]; XtSetArg(args[0], XtNleftBitmap, flist.pixmap); XtSetValues(dict->sme, args, 1); XtSetArg(args[0], XtNlabel, XtName(dict->sme)); XtSetValues(ispell.dict, args, 1); ispell.wchars = dict->wchars; break; } } delete_window = XInternAtom(XtDisplay(ispell.shell), "WM_DELETE_WINDOW", False); XSetWMProtocols(XtDisplay(ispell.shell), XtWindow(ispell.shell), &delete_window, 1); return (True); }