Edit

IABSD.fr/xenocara/app/xedit/ispell.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2008-10-13 20:53:31
    Hash : b44ff0aa
    Message : xedit 1.1.1

  • app/xedit/ispell.c
  • /*
     * 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 <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <ctype.h>
    #include <locale.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>
    #include <X11/Xos.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 _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);
    #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 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*/
    #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)
    {
        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);
    }