ispell.c revision f14f4646
1/*
2 * Copyright (c) 1999 by The XFree86 Project, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Except as contained in this notice, the name of the XFree86 Project shall
23 * not be used in advertising or otherwise to promote the sale, use or other
24 * dealings in this Software without prior written authorization from the
25 * XFree86 Project.
26 *
27 * Author: Paulo César Pereira de Andrade
28 */
29
30/* $XdotOrg: xc/programs/xedit/ispell.c,v 1.6 2004/12/04 00:43:13 kuhn Exp $ */
31/* $XFree86: xc/programs/xedit/ispell.c,v 1.19 2002/10/19 20:04:20 herrb Exp $ */
32
33#include "xedit.h"
34#include "util.h"
35#include <stdlib.h>
36#include <unistd.h>
37#include <fcntl.h>
38#include <signal.h>
39#include <ctype.h>
40#include <locale.h>
41#include <errno.h>
42#include <sys/types.h>
43#include <sys/wait.h>
44#include <X11/Xaw/Toggle.h>
45#include <X11/Xaw/MenuButton.h>
46#include <X11/Xaw/SmeBSB.h>
47#include <X11/Xaw/SimpleMenu.h>
48#include <X11/Xos.h>
49
50#define RECEIVE		1
51#define SEND		2
52
53#define CHECK		0
54#define	ADD		1
55#define REMOVE		2
56
57#define	ASIS		1
58#define UNCAP		2
59
60/*
61 * Types
62 */
63#define UNDO_DEPTH	16
64typedef struct _ispell_undo {
65    char *undo_str;
66    int undo_count;
67    XawTextPosition undo_pos;
68    Boolean repeat;	/* two (misspelled?) words together */
69    Boolean terse;
70    int format;		/* remember text formatting style */
71    struct _ispell_undo *next, *prev;
72} ispell_undo;
73
74typedef struct _ispell_dict {
75    Widget sme;
76    char *wchars;
77    struct _ispell_dict *next;
78} ispell_dict;
79
80#define	TEXT	0
81#define HTML	1
82struct _ispell_format {
83    char *name;
84    int value;
85    Widget sme;
86};
87
88static struct _ispell_format ispell_format[] = {
89    {"text",	TEXT},
90    {"html",	HTML},
91};
92
93struct _ispell {
94    Widget shell, form, mispelled, repeated, word, replacement, text,
95	   suggestions, viewport, list, commands, replace, status,
96	   replaceAll, undo, ignore, ignoreAll, add, addUncap, suspend,
97	   cancel, check, look, terse, options, dict, dictMenu,
98	   format, formatMenu;
99
100    Widget ascii, source;
101    XtInputId id;
102    int pid, ifd[2], ofd[2];
103    XawTextPosition left, right;
104    char *item;
105    Bool lock;
106    Bool repeat;
107    Bool checkit;
108    int stat;
109    char *buf;
110    int bufsiz;
111    int buflen;
112    char sendbuf[1024];
113    char sentbuf[1024];
114
115    int undo_depth;
116    ispell_undo *undo_head, *undo_base;
117    char *undo_for;
118
119    char *wchars;
120    char *cmd;
121    char *skip;
122    char *command;
123    Boolean terse_mode, undo_terse_mode;
124    char *guess_label, *miss_label, *root_label, *none_label, *eof_label,
125	 *compound_label, *ok_label, *repeat_label, *working_label, *look_label;
126    char *look_cmd;
127    char *words_file;
128
129    char *dictionary;
130    char *dict_list;
131    ispell_dict *dict_info;
132
133    int format_mode;	/* to undo correctly */
134    char *formatting;
135    struct _ispell_format *format_info;
136};
137
138typedef struct _ReplaceEntry ReplaceEntry;
139struct _ReplaceEntry {
140    hash_key	*word;
141    ReplaceEntry*next;
142    char	*replace;
143};
144
145typedef struct _IgnoreEntry IgnoreEntry;
146struct _IgnoreEntry {
147    hash_key	*word;
148    IgnoreEntry	*next;
149    int		add;
150};
151
152/*
153 * Prototypes
154 */
155static void AddIspell(Widget, XtPointer, XtPointer);
156static void ChangeDictionaryIspell(Widget, XtPointer, XtPointer);
157static void ChangeFormatIspell(Widget, XtPointer, XtPointer);
158static void CheckIspell(Widget, XtPointer, XtPointer);
159static void IgnoreIspell(Widget, XtPointer, XtPointer);
160static Bool InitIspell(void);
161static void IspellCheckUndo(void);
162static int IspellConvertHtmlAmp(char*);
163static Bool IspellDoIgnoredWord(char*, int, int);
164static Bool IspellIgnoredWord(char*, int, int);
165static void IspellInputCallback(XtPointer, int*, XtInputId*);
166static void IspellKillUndoBuffer(void);
167static Bool IspellReceive(void);
168static char *IspellReplacedWord(char*, char*);
169static int IspellSend(void);
170static void IspellSetSelection(XawTextPosition, XawTextPosition);
171static void IspellSetRepeated(Bool);
172static void IspellSetSensitive(Bool);
173static void IspellSetStatus(char*);
174static void IspellSetTerseMode(Bool);
175static Bool IspellStartProcess(void);
176static Bool IspellCheckProcess(void);
177static Bool IspellEndProcess(Bool, Bool);
178static void LookIspell(Widget, XtPointer, XtPointer);
179static void PopdownIspell(Widget, XtPointer, XtPointer);
180static void ReplaceIspell(Widget, XtPointer, XtPointer);
181static void RevertIspell(Widget, XtPointer, XtPointer);
182static void SelectIspell(Widget, XtPointer, XtPointer);
183static void ToggleTerseIspell(Widget, XtPointer, XtPointer);
184#ifndef SIGNALRETURNSINT
185static void timeout_signal(int);
186static void (*old_timeout)(int);
187#else
188static int timeout_signal(int);
189static int (*old_timeout)(int);
190#endif
191static void UndoIspell(Widget, XtPointer, XtPointer);
192
193Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*);
194
195/*
196 * Initialization
197 */
198static struct _ispell ispell;
199
200#define RSTRTBLSZ	23
201#define ISTRTBLSZ	71
202static hash_table *replace_hash;
203static hash_table *ignore_hash;
204
205#ifndef XtCStatus
206#define XtCStatus	"Status"
207#endif
208
209#define Offset(field) XtOffsetOf(struct _ispell, field)
210static XtResource resources[] = {
211    {"wordChars", "Chars", XtRString, sizeof(char*),
212	Offset(wchars), XtRString, ""},
213    {"ispellCommand", "CommandLine", XtRString, sizeof(char*),
214	Offset(cmd), XtRString, "/usr/local/bin/ispell"},
215    {"terseMode", "Terse", XtRBoolean, sizeof(Boolean),
216	Offset(terse_mode), XtRImmediate, (XtPointer)False},
217    {"guessLabel", XtCStatus, XtRString, sizeof(String),
218	Offset(guess_label), XtRString, "Guess"},
219    {"missLabel", XtCStatus, XtRString, sizeof(String),
220	Offset(miss_label), XtRString, "Miss"},
221    {"rootLabel", XtCStatus, XtRString, sizeof(String),
222	Offset(root_label), XtRString, "Root:"},
223    {"noneLabel", XtCStatus, XtRString, sizeof(String),
224	Offset(none_label), XtRString, "None"},
225    {"compoundLabel", XtCStatus, XtRString, sizeof(String),
226	Offset(compound_label), XtRString, "Compound"},
227    {"okLabel", XtCStatus, XtRString, sizeof(String),
228	Offset(ok_label), XtRString, "Ok"},
229    {"eofLabel", XtCStatus, XtRString, sizeof(String),
230	Offset(eof_label), XtRString, "End Of File"},
231    {"repeatLabel", XtCStatus, XtRString, sizeof(String),
232	Offset(repeat_label), XtRString, "Repeat"},
233    {"workingLabel", XtCStatus, XtRString, sizeof(String),
234	Offset(working_label), XtRString, "..."},
235    {"lookLabel", XtCStatus, XtRString, sizeof(String),
236	Offset(look_label), XtRString, "Look"},
237    {"lookCommand", "CommandLine", XtRString, sizeof(char*),
238	Offset(look_cmd), XtRString, "/usr/bin/egrep -i"},
239    {"wordsFile", "Words", XtRString, sizeof(char*),
240	Offset(words_file), XtRString, "/usr/share/dict/words"},
241    {"dictionary", "Dictionary", XtRString, sizeof(char*),
242	Offset(dictionary), XtRString, "american"},
243    {"dictionaries", "Dictionary", XtRString, sizeof(char*),
244	Offset(dict_list), XtRString, "american americanmed+ english"},
245    {"formatting", "TextFormat", XtRString, sizeof(char*),
246	Offset(formatting), XtRString, "text"},
247};
248#undef Offset
249
250#ifdef NO_LIBC_I18N
251static int
252ToLower(int ch)
253{
254    char buf[2];
255
256    *buf = ch;
257    XmuNCopyISOLatin1Lowered(buf, buf, sizeof(buf));
258
259    return (*buf);
260}
261
262static int
263ToUpper(int ch)
264{
265    char buf[2];
266
267    *buf = ch;
268    XmuNCopyISOLatin1Uppered(buf, buf, sizeof(buf));
269
270    return (*buf);
271}
272
273static int
274IsLower(int ch)
275{
276    char upbuf[2];
277    char lobuf[2];
278
279    *upbuf = *lobuf = ch;
280    XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
281    XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));
282
283    return (*lobuf != *upbuf && ch == *lobuf);
284}
285
286static int
287IsUpper(int ch)
288{
289    char upbuf[2];
290    char lobuf[2];
291
292    *upbuf = *lobuf = ch;
293    XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
294    XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));
295
296    return (*lobuf != *upbuf && ch == *upbuf);
297}
298#else
299#define	ToLower	tolower
300#define ToUpper	toupper
301#define IsLower islower
302#define IsUpper isupper
303#endif
304
305/*
306 * Implementation
307 */
308#ifdef STDERR_FILENO
309# define WRITES(s) write(STDERR_FILENO, s, strlen(s))
310#else
311# define WRITES(s) write(fileno(stderr), s, strlen(s))
312#endif
313
314/*ARGSUSED*/
315#ifndef SIGNALRETURNSINT
316static void
317timeout_signal(int unused)
318{
319    int olderrno = errno;
320
321    WRITES("Warning: Timeout waiting ispell process to die.\n");
322    kill(ispell.pid, SIGTERM);
323    errno = olderrno;
324}
325#else
326static int
327timeout_signal(int unused)
328{
329    int olderrno = errno;
330
331    WRITES("Warning: Timeout waiting ispell process to die.\n");
332    kill(ispell.pid, SIGTERM);
333
334    errno = olderrno;
335    return (0);
336}
337#endif
338
339static void
340IspellSetSelection(XawTextPosition left, XawTextPosition right)
341{
342    /* Try to make sure the selected word is completely visible */
343    XawTextSetInsertionPoint(ispell.ascii, right);
344    XawTextSetInsertionPoint(ispell.ascii, left);
345    XawTextSetSelection(ispell.ascii, left, right);
346}
347
348static void
349IspellSetStatus(char *label)
350{
351    Arg args[1];
352
353    XtSetArg(args[0], XtNlabel, label);
354    XtSetValues(ispell.status, args, 1);
355}
356
357static void
358IspellSetRepeated(Bool state)
359{
360    static char *mispelled, *repeated;
361    Arg args[1];
362
363    if (mispelled == NULL) {
364	XtSetArg(args[0], XtNlabel, &mispelled);
365	XtGetValues(ispell.mispelled, args, 1);
366	mispelled = XtNewString(mispelled);
367    }
368    if (repeated == NULL) {
369	XtSetArg(args[0], XtNlabel, &repeated);
370	XtGetValues(ispell.repeated, args, 1);
371	repeated = XtNewString(repeated);
372    }
373    XtSetSensitive(ispell.replaceAll, !state);
374    XtSetSensitive(ispell.ignoreAll, !state);
375    XtSetSensitive(ispell.add, !state);
376    XtSetSensitive(ispell.addUncap, !state);
377    if (!state) {
378	XtSetArg(args[0], XtNlabel, mispelled);
379	XtSetValues(ispell.mispelled, args, 1);
380    }
381    else {
382	XtSetArg(args[0], XtNlabel, repeated);
383	XtSetValues(ispell.mispelled, args, 1);
384    }
385}
386
387static void
388IspellSetSensitive(Bool state)
389{
390    XtSetSensitive(ispell.replace, state);
391    XtSetSensitive(ispell.replaceAll, state);
392    XtSetSensitive(ispell.ignore, state);
393    XtSetSensitive(ispell.ignoreAll, state);
394    XtSetSensitive(ispell.add, state);
395    XtSetSensitive(ispell.addUncap, state);
396}
397
398static void
399IspellSetTerseMode(Bool mode)
400{
401    Arg args[1];
402
403    XtSetArg(args[0], XtNstate, ispell.terse_mode = mode);
404    XtSetValues(ispell.terse, args, 1);
405    write(ispell.ofd[1], mode ? "!\n" : "%\n", 2);
406}
407
408static void
409IspellCheckUndo(void)
410{
411    ispell_undo *undo = XtNew(ispell_undo);
412
413    if (ispell.undo_for && strcmp(ispell.undo_for, ispell.dictionary)) {
414	XeditPrintf("Undo: Dictionary changed. Previous undo information lost.\n");
415	IspellKillUndoBuffer();
416	Feep();
417    }
418
419    undo->next = NULL;
420    undo->repeat = False;
421    undo->terse = ispell.undo_terse_mode;
422    undo->format = ispell.format_mode;
423    if ((undo->prev = ispell.undo_head) != NULL)
424	undo->prev->next = undo;
425    else
426	undo->prev = NULL;
427    ++ispell.undo_depth;
428    if (!ispell.undo_base) {
429	ispell.undo_base = undo;
430	XtSetSensitive(ispell.undo, True);
431    }
432    else if (ispell.undo_depth > UNDO_DEPTH) {
433	ispell_undo *tmp;
434
435	if (ispell.undo_base->undo_str)
436	    XtFree(ispell.undo_base->undo_str);
437	tmp = ispell.undo_base->next;
438	XtFree((char*)ispell.undo_base);
439	tmp->prev = NULL;
440	ispell.undo_base = tmp;
441	ispell.undo_depth = UNDO_DEPTH;
442    }
443    ispell.undo_head = undo;
444}
445
446static char *
447IspellReplacedWord(char *word, char *replace)
448{
449    int			word_len;
450    hash_key		*word_key;
451    ReplaceEntry	*entry;
452
453    word_len = strlen(word);
454    entry = (ReplaceEntry *)hash_check(replace_hash, word, word_len);
455    if (entry == NULL) {
456	word_key = XtNew(hash_key);
457	word_key->value = XtNewString(word);
458	word_key->length = word_len;
459	entry = XtNew(ReplaceEntry);
460	entry->word = word_key;
461	entry->replace = NULL;
462	entry->next = NULL;
463	hash_put(replace_hash, (hash_entry *)entry);
464    }
465
466    if (replace) {
467	XtFree(entry->replace);
468	entry->replace = XtNewString(replace);
469    }
470
471    return (entry->replace);
472}
473
474static Bool
475IspellDoIgnoredWord(char *word, int cmd, int add)
476{
477    int		word_len;
478    hash_key	*word_key;
479    IgnoreEntry	*entry;
480
481    word_len = strlen(word);
482    entry = (IgnoreEntry *)hash_check(ignore_hash, word, word_len);
483    if (entry == NULL) {
484	if (cmd != ADD)
485	    return (False);
486
487	word_key = XtNew(hash_key);
488	word_key->value = XtNewString(word);
489	word_key->length = word_len;
490	entry = XtNew(IgnoreEntry);
491	entry->word = word_key;
492	entry->add = add;
493	entry->next = NULL;
494	hash_put(ignore_hash, (hash_entry *)entry);
495
496	return (True);
497    }
498    else if (cmd == REMOVE)
499	hash_rem(ignore_hash, (hash_entry *)entry);
500
501    return (cmd == CHECK);
502}
503
504static Bool
505IspellIgnoredWord(char *word, int cmd, int add)
506{
507    if (add != UNCAP && IspellDoIgnoredWord(word, cmd, add))
508	return (True);
509
510    /* add/remove uncapped word to/of list,
511     * or cheks for correct capitalization */
512    if (add == UNCAP || cmd == CHECK) {
513	unsigned char *str = (unsigned char*)word;
514	unsigned char string[1024];
515	Bool upper, status;
516	int i;
517
518	status = True;
519	upper = IsUpper(*str);
520	*string = upper ? ToLower(*str) : *str;
521	if (*str)
522	    str++;
523	if (IsLower(*str))
524	    upper = False;
525	for (i = 1; *str && i < sizeof(string) - 1; i++, str++) {
526	    if (upper && IsLower(*str))
527		status = False;
528	    else if (!upper && IsUpper(*str))
529		status = False;
530	    string[i] = ToLower(*str);
531	}
532	string[i] = '\0';
533
534	if ((cmd != CHECK || status) &&
535	    IspellDoIgnoredWord((char*)string, cmd, add))
536	    return (True);
537    }
538
539    return (False);
540}
541
542/*ARGSUSED*/
543static Bool
544IspellReceive(void)
545{
546    int i, len, old_len;
547    Arg args[2];
548    char *str, *end, **list, **old_list;
549    char *tmp, word[1024];
550    int j;
551
552    if (ispell.lock || ispell.stat != RECEIVE)
553	return (False);
554
555    while (1) {		/* read the entire line */
556	if (ispell.buflen >= ispell.bufsiz - 1)
557	    ispell.buf = XtRealloc(ispell.buf, ispell.bufsiz += BUFSIZ);
558	if ((len = read(ispell.ifd[0], &ispell.buf[ispell.buflen],
559			ispell.bufsiz - ispell.buflen - 1)) <= 0)
560	    break;
561	ispell.buflen += len;
562    }
563    if (ispell.buflen <= 0)
564	return (False);
565    len = 0;
566    i = ispell.buflen - 1;
567    while (i >= 0 && ispell.buf[i] == '\n') {
568	++len;
569	--i;
570    }
571    if (len < 2 - ((ispell.terse_mode && i == -1) || ispell.buf[0] == '@'))
572	return (False);
573    ispell.buf[ispell.buflen - len] = '\0';
574    ispell.buflen = 0;
575
576    if ((tmp = strchr(ispell.sendbuf, '\n')) != NULL)
577	*tmp = '\0';
578
579    switch (ispell.buf[0]) {
580	case '&':	/* MISS */
581	case '?':	/* GUESS */
582	    str = strchr(&ispell.buf[2], ' ');
583	    if (!ispell.checkit) {
584		*str = '\0';
585		XtSetArg(args[0], XtNlabel, &ispell.buf[2]);
586		XtSetValues(ispell.word, args, 1);
587	    }
588	    ++str;
589	    list = NULL;
590	    str = strchr(str, ':') + 1;
591	    for (i = 0; ; i++) {
592		end = strchr(str, ',');
593		if (end)	*end = '\0';
594		if ((i % 16) == 0)
595		    list = (char**)XtRealloc((char*)list, (i + 16) * sizeof(char*));
596		tmp = word;
597		for (j = 1; j < sizeof(word) && str[j]; j++) {
598		    if (str[j] == '+')
599			continue;
600		    else if (str[j] == '-' && str[j+1] != '-' && str[j-1] != '-') {
601			char *p, string[256];
602			int k, l;
603
604			for (l = 0, k = j + 1; str[k] != '+' && str[k] != '-'
605			     && str[k] && l < sizeof(string) - 1; k++, l++)
606			    string[l] = str[k];
607			string[l] = '\0';
608			*tmp = '\0';
609			if (l && (p = strstr(word, string)) != NULL) {
610			    char *sav = p;
611
612			    while ((p = strstr(p + l, string)) != NULL)
613				sav = p;
614			    p = sav;
615			    if (strcmp(p, string) == 0) {
616				tmp = p;
617				j = k - 1;
618			    }
619			    else
620				*tmp++ = '-';
621			}
622			else
623			    *tmp++ = '-';
624		    }
625		    else
626			*tmp++ = str[j];
627		}
628		*tmp = '\0';
629		list[i] = XtNewString(word);
630
631		if (end)	str = end + 1;
632		else		break;
633	    }
634	    len = i + 1;
635
636	    XtSetArg(args[0], XtNlist, &old_list);
637	    XtSetArg(args[1], XtNnumberStrings, &old_len);
638	    XtGetValues(ispell.list, args, 2);
639
640	    ispell.item = NULL;
641	    if ((str = IspellReplacedWord(&ispell.buf[2], NULL)) != NULL)
642		for (i = 0; i < len; i++) {
643		    if (strcmp(list[i], str) == 0) {
644			ispell.item = list[i];
645			break;
646		    }
647		}
648	    else
649		ispell.item = list[i = 0];
650	    if (!ispell.item) {
651		list = (char**)XtRealloc((char*)list, (len + 1) * sizeof(char*));
652		ispell.item = list[i] = XtNewString(str);
653		++len;
654	    }
655
656	    XtSetArg(args[0], XtNlist, list);
657	    XtSetArg(args[1], XtNnumberStrings, len);
658	    XtSetValues(ispell.list, args, 2);
659
660	    XtSetSensitive(ispell.list, True);
661	    if (!ispell.checkit)
662		XawListHighlight(ispell.list, i);
663
664	    if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
665		while (--old_len > -1)
666		    XtFree(old_list[old_len]);
667		XtFree((char*)old_list);
668	    }
669
670	    if (!ispell.checkit) {
671		XtSetArg(args[0], XtNstring, ispell.item);
672		XtSetValues(ispell.text, args, 1);
673		IspellSetSelection(ispell.left, ispell.right);
674		if (ispell.repeat)
675		    IspellSetRepeated(ispell.repeat = False);
676	    }
677
678	    IspellSetStatus(ispell.buf[0] == '?' ?
679			    ispell.guess_label : ispell.miss_label);
680	    ispell.undo_terse_mode = ispell.terse_mode;
681	    ispell.format_mode = ispell.format_info->value;
682	    ispell.lock = True;
683	    break;
684	case '#':	/* NONE */
685	case '-':	/* COMPOUND */
686	case '+':	/* ROOT */
687	check_label:
688	    str = &ispell.sendbuf[1];
689	    if (!ispell.checkit) {
690		XtSetArg(args[0], XtNlabel, str);
691		XtSetValues(ispell.word, args, 1);
692	    }
693
694	    XtSetArg(args[0], XtNlist, &old_list);
695	    XtSetArg(args[1], XtNnumberStrings, &old_len);
696	    XtGetValues(ispell.list, args, 2);
697	    ispell.item = NULL;
698
699	    list = (char**)XtMalloc(sizeof(char**));
700	    if ((tmp = IspellReplacedWord(str, NULL)) != NULL)
701		str = tmp;
702	    if (tmp == NULL && ispell.buf[0] == '#')
703		list[0] = XtNewString("");
704	    else
705		list[0] = XtNewString(str);
706
707	    XtSetArg(args[0], XtNlist, list);
708	    XtSetArg(args[1], XtNnumberStrings, 1);
709	    XtSetValues(ispell.list, args, 2);
710
711	    if (tmp == NULL && ispell.buf[0] == '#') {
712		XawListUnhighlight(ispell.list);
713		XtSetSensitive(ispell.list, False);
714	    }
715	    else {
716		XtSetSensitive(ispell.list, True);
717		if (!ispell.checkit)
718		    XawListHighlight(ispell.list, 0);
719	    }
720	    if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
721		while (--old_len > -1)
722		    XtFree(old_list[old_len]);
723		XtFree((char*)old_list);
724	    }
725
726	    if (!ispell.checkit) {
727		XtSetArg(args[0], XtNstring, str);
728		XtSetValues(ispell.text, args, 1);
729		IspellSetSelection(ispell.left, ispell.right);
730		if (ispell.repeat)
731		    IspellSetRepeated(ispell.repeat = False);
732	    }
733
734	    ispell.undo_terse_mode = ispell.terse_mode;
735	    ispell.format_mode = ispell.format_info->value;
736	    ispell.lock = True;
737	    if (ispell.buf[0] == '+') {
738		if ((tmp = strchr(&ispell.buf[2], '\n')) != NULL)
739		    *tmp = '\0';
740		XmuSnprintf(word, sizeof(word), "%s %s",
741			    ispell.root_label, &ispell.buf[2]);
742		IspellSetStatus(word);
743	    }
744	    else
745		IspellSetStatus(ispell.buf[0] == '#' ? ispell.none_label :
746				ispell.buf[0] == '-' ? ispell.compound_label :
747				ispell.ok_label);
748	    break;
749	case '*':	/* OK */
750	case '\0':	/* when running in terse mode */
751	    if (!ispell.checkit)
752		(void)IspellIgnoredWord(&ispell.sendbuf[1], ADD, 0);
753	    else
754		goto check_label;
755	    ispell.lock = False;
756	    break;
757	case '@':	/* Ispell banner */
758	    /* it only happens when the dictionary is changed */
759	    if (!ispell.repeat) {
760		XawTextPosition left, right;
761
762		ispell.stat = SEND;
763		while (IspellSend() == 0)
764		    ;
765		/* word chars may have changed */
766		XawTextGetSelectionPos(ispell.ascii, &left, &right);
767		if (left != ispell.left || right != ispell.right) {
768		    XtSetArg(args[0], XtNstring, &ispell.sendbuf[1]);
769		    XtSetValues(ispell.text, args, 1);
770		    IspellSetSelection(ispell.left, ispell.right);
771		}
772		ispell.checkit = True;
773	    }
774	    else {
775		IspellSetStatus(ispell.repeat_label);
776		ispell.undo_terse_mode = ispell.terse_mode;
777		ispell.format_mode = ispell.format_info->value;
778		ispell.lock = True;
779		return (True);
780	    }
781	    break;
782	default:
783	    fprintf(stderr, "Unknown ispell command '%c'\n", ispell.buf[0]);
784	    return (False);
785    }
786
787    if (!ispell.lock && !ispell.checkit) {
788	ispell.stat = SEND;
789	while (IspellSend() == 0)
790	    ;
791    }
792
793    return (True);
794}
795
796static int
797IspellConvertHtmlAmp(char *buf)
798{
799    int len, ch = '?';
800
801    /* this function is static, so I can do it */
802    *strchr(++buf, ';') = '\0';
803
804    len = strlen(buf);
805    if (len == 0)
806	return ('&');
807    if (len > 1) {
808	if (strcasecmp(&buf[1], "lt") == 0)
809	    ch = '<';
810	else if (strcasecmp(&buf[1], "gt") == 0)
811	    ch = '>';
812	else if (strcasecmp(&buf[1], "nbsp") == 0)
813	    ch = ' ';
814	else if (strcasecmp(&buf[1], "amp") == 0)
815	    ch = '&';
816	else if (strcasecmp(&buf[1], "quot") == 0)
817	    ch = '"';
818	else if (*buf == '#') {
819	    char *tmp;
820
821	    if (len == 1)
822		return ('?');
823	    ch = strtol(&buf[1], &tmp, 10);
824	    if (*tmp)
825		fprintf(stderr, "Warning: bad html interpreting '&#' mark.\n");
826	}
827	else if (strcmp(&buf[1], "acute") == 0) {
828	    switch (*buf) {
829		case 'a': ch = 0xe1; break;
830		case 'e': ch = 0xe9; break;
831		case 'i': ch = 0xed; break;
832		case 'o': ch = 0xf3; break;
833		case 'u': ch = 0xfa; break;
834		case 'A': ch = 0xc1; break;
835		case 'E': ch = 0xc9; break;
836		case 'I': ch = 0xcd; break;
837		case 'O': ch = 0xd3; break;
838		case 'U': ch = 0xda; break;
839	    }
840	}
841	else if (strcmp(&buf[1], "grave") == 0) {
842	    switch (*buf) {
843		case 'a': ch = 0xe0; break;
844		case 'e': ch = 0xe8; break;
845		case 'i': ch = 0xec; break;
846		case 'o': ch = 0xf2; break;
847		case 'u': ch = 0xf9; break;
848		case 'A': ch = 0xc0; break;
849		case 'E': ch = 0xc8; break;
850		case 'I': ch = 0xcc; break;
851		case 'O': ch = 0xd2; break;
852		case 'U': ch = 0xd9; break;
853	    }
854	}
855	else if (strcmp(&buf[1], "tilde") == 0) {
856	    switch (*buf) {
857		case 'a': ch = 0xe3; break;
858		case 'o': ch = 0xf5; break;
859		case 'n': ch = 0xf1; break;
860		case 'A': ch = 0xe3; break;
861		case 'O': ch = 0xd5; break;
862		case 'N': ch = 0xd1; break;
863	    }
864	}
865	else if (strcmp(&buf[1], "circ") == 0) {
866	    switch (*buf) {
867		case 'a': ch = 0xe2; break;
868		case 'e': ch = 0xea; break;
869		case 'i': ch = 0xee; break;
870		case 'o': ch = 0xf4; break;
871		case 'u': ch = 0xfb; break;
872		case 'A': ch = 0xc2; break;
873		case 'E': ch = 0xca; break;
874		case 'I': ch = 0xce; break;
875		case 'O': ch = 0xd4; break;
876		case 'U': ch = 0xdb; break;
877	    }
878	}
879	else if (strcmp(&buf[1], "cedil") == 0) {
880	    switch (*buf) {
881		case 'c': ch = 0xe7; break;
882		case 'C': ch = 0xc7; break;
883	    }
884	}
885	/* add more cases here */
886    }
887
888    return (ch);
889}
890
891/*ARGSUSED*/
892static int
893IspellSend(void)
894{
895    XawTextPosition position, old_left, pos;
896    XawTextBlock block;
897    int i, len, spaces, nls;
898    Bool nl, html, inside_html;
899    char ampbuf[32];
900    int amplen;
901
902    if (ispell.lock || ispell.stat != SEND)
903	return (-1);
904
905    len = 1;
906    ispell.sendbuf[0] = '^';	/* don't evaluate following characters as commands */
907
908    spaces = nls = 0;
909
910    html = ispell.format_info->value == HTML;
911    inside_html = False;
912    amplen = 0;
913
914    /* skip non word characters */
915    pos = position = ispell.right;
916    nl = False;
917    while (1) {
918	Bool done = False;
919	char mb[sizeof(wchar_t)];
920
921	retry_html_space:
922	position = XawTextSourceRead(ispell.source, position,
923				     &block, BUFSIZ);
924	if (block.length == 0) {	/* end of file */
925	    ispell.stat = 0;
926	    ispell.lock = True;
927	    XawTextSetInsertionPoint(ispell.ascii, ispell.right);
928	    XawTextUnsetSelection(ispell.ascii);
929	    IspellSetSensitive(False);
930	    IspellSetStatus(ispell.eof_label);
931	    return (-1);
932	}
933	for (i = 0; i < block.length; i++) {
934	    if (international)
935		wctomb(mb, ((wchar_t*)block.ptr)[i]);
936	    else
937		mb[0] = block.ptr[i];
938	    if (amplen) {
939		if (amplen + 2 >= sizeof(ampbuf)) {
940		    if (!ispell.terse_mode)
941			fprintf(stderr, "Warning: error interpreting '&' mark.\n");
942		    amplen = 0;
943		    position = pos + 1;
944		    goto retry_html_space;
945		}
946		else if ((ampbuf[amplen++] = *mb) == ';') {
947		    int ch;
948
949		    ampbuf[amplen] = '\0';
950		    ch = IspellConvertHtmlAmp(ampbuf);
951		    amplen = 0;
952		    if (isalpha(ch) ||
953			(ch && strchr(ispell.wchars, ch))) {
954			/* interpret it again */
955			ispell.right = pos;
956			i = 0;
957			done = True;
958			break;
959		    }
960		    else if ((ch == '\n' || isspace(ch)) && spaces >= 0)
961			++spaces;
962		    else
963			spaces = -1;
964		}
965	    }
966	    else if (html && *mb == '&') {
967		ampbuf[amplen++] = *mb;
968		pos = block.firstPos + i;
969		continue;
970	    }
971	    else if ((!html || !inside_html) && (isalpha(*mb) ||
972		(*mb && strchr(ispell.wchars, *mb)))) {
973		done = True;
974		break;
975	    }
976	    else if (!html && *mb == '\n') {
977		nl = True;
978		if (++nls > 1 && (!html || !inside_html))
979		    spaces = -1;
980		else if (spaces >= 0)
981		    ++spaces;
982	    }
983	    else if (nl) {
984		nl = False;
985		if (*mb && strchr(ispell.skip, *mb)) {
986		    position = ispell.right =
987			XawTextSourceScan(ispell.source, ispell.right + i,
988					  XawstEOL, XawsdRight, 1, False);
989		    i = 0;
990		    break;
991		}
992		else if (spaces >= 0 && isspace(*mb))
993		    ++spaces;
994		else
995		    spaces = -1;
996	    }
997	    else if (html && inside_html) {
998		if (*mb == '>')
999		    inside_html = False;
1000	    }
1001	    else if (html && *mb == '<')
1002		inside_html = True;
1003	    else if (spaces >= 0 && (isspace(*mb) || (html && *mb == '\n')))
1004		++spaces;
1005	    else
1006		spaces = -1;
1007	}
1008
1009	ispell.right += i;
1010	if (done)
1011	    break;
1012    }
1013
1014    old_left = ispell.left;
1015
1016    /* read a word */
1017    position = ispell.left = ispell.right;
1018    while (1) {
1019	Bool done = False;
1020	char mb[sizeof(wchar_t)];
1021
1022	retry_html_word:
1023	position = XawTextSourceRead(ispell.source, position,
1024				     &block, BUFSIZ);
1025	if (block.length == 0 && len == 1) {	/* end of file */
1026	    ispell.stat = 0;
1027	    ispell.lock = True;
1028	    XawTextSetInsertionPoint(ispell.ascii, ispell.right);
1029	    XawTextUnsetSelection(ispell.ascii);
1030	    IspellSetSensitive(False);
1031	    IspellSetStatus(ispell.eof_label);
1032	    return (-1);
1033	}
1034	for (i = 0; i < block.length; i++) {
1035	    if (international)
1036		wctomb(mb, ((wchar_t*)block.ptr)[i]);
1037	    else
1038		mb[0] = block.ptr[i];
1039	    if (amplen) {
1040		if (amplen + 2 >= sizeof(ampbuf)) {
1041		    if (!ispell.terse_mode)
1042			fprintf(stderr, "Warning: error interpreting '&' mark.\n");
1043		    amplen = 0;
1044		    position = pos + 1;
1045		    if (strchr(ispell.wchars, '&')) {
1046			if (len + 1 >= sizeof(ispell.sendbuf) - 1) {
1047			    done = True;
1048			    fprintf(stderr, "Warning: word is too large!\n");
1049			    break;
1050			}
1051			ispell.sendbuf[len++] = '&';
1052			goto retry_html_word;
1053		    }
1054		    else {
1055			ispell.right = position;
1056			i = 0;
1057			done = True;
1058			break;
1059		    }
1060		}
1061		else if ((ampbuf[amplen++] = *mb) == ';') {
1062		    int ch;
1063
1064		    ampbuf[amplen] = '\0';
1065		    ch = IspellConvertHtmlAmp(ampbuf);
1066		    amplen = 0;
1067		    if (!isalpha(ch) &&
1068			(!ch || !strchr(ispell.wchars, ch))) {
1069			ispell.right = pos;
1070			i = 0;
1071			done = True;
1072			break;
1073		    }
1074		    *mb = ch;
1075		}
1076		else
1077		    continue;
1078	    }
1079	    else if (html && *mb == '&') {
1080		ampbuf[amplen++] = *mb;
1081		pos = block.firstPos + i;
1082		continue;
1083	    }
1084	    else if (!isalpha(*mb) && (!*mb || !strchr(ispell.wchars, *mb))) {
1085		done = True;
1086		break;
1087	    }
1088	    ispell.sendbuf[len] = *mb;
1089	    if (++len >= sizeof(ispell.sendbuf) - 1) {
1090		done = True;
1091		fprintf(stderr, "Warning: word is too large!\n");
1092		break;
1093	    }
1094	}
1095	ispell.right += i;
1096	if (done || block.length == 0)
1097	    break;
1098    }
1099
1100    ispell.sendbuf[len] = '\0';
1101
1102    if (spaces > 0 && spaces <= 32 && strcmp(ispell.sendbuf, ispell.sentbuf) == 0) {
1103	Arg args[2];
1104	int old_len;
1105	char **list, **old_list;
1106	char label[sizeof(ispell.sendbuf) + sizeof(ispell.sentbuf) + 32];
1107
1108	strcpy(label, &ispell.sendbuf[1]);
1109	for (i = 0; i < spaces; i++)
1110	    label[len + i - 1] = ' ';
1111	strcpy(&label[len + i - 1], &ispell.sendbuf[1]);
1112	XtSetArg(args[0], XtNlabel, label);
1113	XtSetValues(ispell.word, args, 1);
1114
1115	XtSetArg(args[0], XtNstring, &ispell.sendbuf[1]);
1116	XtSetValues(ispell.text, args, 1);
1117
1118	XtSetArg(args[0], XtNlist, &old_list);
1119	XtSetArg(args[1], XtNnumberStrings, &old_len);
1120	XtGetValues(ispell.list, args, 2);
1121	list = (char**)XtMalloc(sizeof(char**));
1122	list[0] = XtNewString(&ispell.sendbuf[1]);
1123	XtSetArg(args[0], XtNlist, list);
1124	XtSetArg(args[1], XtNnumberStrings, 1);
1125	XtSetValues(ispell.list, args, 2);
1126	XtSetSensitive(ispell.list, True);
1127	XawListHighlight(ispell.list, 0);
1128	if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
1129	    while (--old_len > -1)
1130		XtFree(old_list[old_len]);
1131	    XtFree((char*)old_list);
1132	}
1133
1134	IspellSetRepeated(True);
1135	IspellSetSelection(old_left, ispell.right);
1136	IspellSetStatus(ispell.repeat_label);
1137	ispell.repeat = ispell.lock = True;
1138
1139	return (1);
1140    }
1141    strcpy(ispell.sentbuf, ispell.sendbuf);
1142
1143    if (len <= 2 || IspellIgnoredWord(&ispell.sendbuf[1], CHECK, 0))
1144	return (0);
1145
1146    ispell.sendbuf[len++] = '\n';
1147
1148    write(ispell.ofd[1], ispell.sendbuf, len);
1149
1150    ispell.stat = RECEIVE;
1151
1152    return (1);
1153}
1154
1155/*ARGSUSED*/
1156static void
1157IspellInputCallback(XtPointer closure, int *source, XtInputId *id)
1158{
1159    if (ispell.right < 0) {
1160	int len;
1161	char buf[1024];
1162
1163	ispell.right = XawTextGetInsertionPoint(ispell.ascii);
1164	ispell.right = XawTextSourceScan(ispell.source, ispell.right,
1165					      XawstEOL, XawsdLeft, 1, True);
1166	len = read(ispell.ifd[0], buf, sizeof(buf));
1167	if (strncmp(buf, "@(#)", 4) == 0) {
1168	    Arg args[1];
1169
1170	    buf[len - 1] = '\0';
1171	    XtSetArg(args[0], XtNtitle, &buf[5]);
1172	    XtSetValues(ispell.shell, args, 1);
1173	}
1174	else
1175	    fprintf(stderr, "Error: is ispell talking with me?\n");
1176	IspellSetTerseMode(ispell.terse_mode);
1177	while (IspellSend() == 0)
1178	    ;
1179    }
1180    else if (ispell.source)
1181	IspellReceive();
1182}
1183
1184/*ARGSUSED*/
1185void
1186IspellCallback(Widget w, XtPointer client_data, XtPointer call_data)
1187{
1188    Cardinal zero = 0;
1189
1190    IspellAction(textwindow, NULL, NULL, &zero);
1191}
1192
1193/*ARGSUSED*/
1194void
1195IspellAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
1196{
1197    Arg args[3];
1198    Cardinal num_args;
1199    char **strs, **list;
1200    int n_strs;
1201    Bool first_time = InitIspell();
1202
1203    if (*num_params == 1 && (params[0][0] == 'e' || params[0][0] == 'E')) {
1204	PopdownIspell(w, (XtPointer)True, NULL);
1205	return;
1206    }
1207
1208    if (!XtIsSubclass(w, textWidgetClass) || ispell.source) {
1209	Feep();
1210	return;
1211    }
1212
1213    ispell.source = XawTextGetSource(ispell.ascii = w);
1214
1215    if (first_time) {
1216	/* let the user choose the better position for the ispell window */
1217	Dimension width, height, b_width;
1218	Position x, y, max_x, max_y;
1219
1220	x = y = -1;
1221	if (event) {
1222	    switch (event->type) {
1223		case ButtonPress:
1224		case ButtonRelease:
1225		    x = event->xbutton.x_root;
1226		    y = event->xbutton.y_root;
1227		    break;
1228		case KeyPress:
1229		case KeyRelease:
1230		    x = event->xkey.x_root;
1231		    y = event->xkey.y_root;
1232		    break;
1233	    }
1234	}
1235	if (x < 0 || y < 0) {
1236	    Window r, c;
1237	    int rx, ry, wx, wy;
1238	    unsigned mask;
1239
1240	    XQueryPointer(XtDisplay(ispell.shell), XtWindow(ispell.shell),
1241			  &r, &c, &rx, &ry, &wx, &wy, &mask);
1242	    x = rx;
1243	    y = ry;
1244	}
1245
1246	num_args = 0;
1247	XtSetArg(args[num_args], XtNwidth, &width);		num_args++;
1248	XtSetArg(args[num_args], XtNheight, &height);		num_args++;
1249	XtSetArg(args[num_args], XtNborderWidth, &b_width);	num_args++;
1250	XtGetValues(ispell.shell, args, num_args);
1251
1252	width += b_width << 1;
1253	height += b_width << 1;
1254
1255	x -= (Position)(width >> 1);
1256	if (x < 0)
1257	    x = 0;
1258	if (x > (max_x = (Position)(XtScreen(w)->width - width)))
1259	    x = max_x;
1260
1261	y -= (Position)(height >> 1);
1262	if (y < 0)
1263	    y = 0;
1264	if (y > (max_y = (Position)(XtScreen(w)->height - height)))
1265	    y = max_y;
1266
1267	num_args = 0;
1268	XtSetArg(args[num_args], XtNx, x);	num_args++;
1269	XtSetArg(args[num_args], XtNy, y);	num_args++;
1270	XtSetValues(ispell.shell, args, num_args);
1271    }
1272
1273    if (ispell.repeat)
1274	IspellSetRepeated(False);
1275    ispell.lock = ispell.repeat = ispell.checkit = False;
1276    ispell.stat = SEND;
1277
1278    IspellSetSensitive(True);
1279    XtSetSensitive(ispell.undo, False);
1280
1281    XtSetArg(args[0], XtNlabel, "");
1282    XtSetValues(ispell.word, args, 1);
1283
1284    XtSetArg(args[0], XtNstring, "");
1285    XtSetValues(ispell.text, args, 1);
1286
1287    XtSetArg(args[0], XtNlist, &strs);
1288    XtSetArg(args[1], XtNnumberStrings, &n_strs);
1289    XtGetValues(ispell.list, args, 2);
1290
1291    list = (char**)XtMalloc(sizeof(char**));
1292    list[0] = XtNewString("");
1293    XtSetArg(args[0], XtNlist, list);
1294    XtSetArg(args[1], XtNnumberStrings, 1);
1295    XtSetValues(ispell.list, args, 2);
1296
1297    if (n_strs > 1 || (XtName(ispell.list) != strs[0])) {
1298	while (--n_strs > -1)
1299	    XtFree(strs[n_strs]);
1300	XtFree((char*)strs);
1301    }
1302
1303    IspellSetStatus(ispell.working_label);
1304
1305    if (!ispell.pid)
1306	(void)IspellStartProcess();
1307    else {
1308	ispell.right = XawTextGetInsertionPoint(ispell.ascii);
1309	ispell.right = XawTextSourceScan(ispell.source, ispell.right,
1310					      XawstEOL, XawsdLeft, 1, True);
1311	while (IspellSend() == 0)
1312	    ;
1313    }
1314
1315    XtPopup(ispell.shell, XtGrabExclusive);
1316    XtSetKeyboardFocus(ispell.shell, ispell.text);
1317}
1318
1319static Bool
1320IspellStartProcess(void)
1321{
1322    if (!ispell.pid) {
1323	int len;
1324	char format[32];
1325	static char *command;
1326
1327	ispell.source = XawTextGetSource(ispell.ascii);
1328
1329	if (command)
1330	    XtFree(command);
1331
1332	strcpy(format, "%s -a");
1333	len = strlen(ispell.cmd) + 4;
1334	if (ispell.dictionary && *ispell.dictionary) {
1335	    len += strlen(ispell.dictionary) + 6;
1336	    strcat(format, " -d '%s'");
1337	    if (ispell.wchars && *ispell.wchars) {
1338		len += strlen(ispell.wchars + 6);
1339		strcat(format, " -w '%s'");
1340	    }
1341	}
1342	command = XtMalloc(len);
1343	XmuSnprintf(command, len, format,
1344		    ispell.cmd, ispell.dictionary, ispell.wchars);
1345
1346	pipe(ispell.ifd);
1347	pipe(ispell.ofd);
1348	if ((ispell.pid = fork()) == 0) {
1349	    close(0);
1350	    close(1);
1351	    dup2(ispell.ofd[0], 0);
1352	    dup2(ispell.ifd[1], 1);
1353	    close(ispell.ofd[0]);
1354	    close(ispell.ofd[1]);
1355	    close(ispell.ifd[0]);
1356	    close(ispell.ifd[1]);
1357	    if (!international)
1358		setlocale(LC_ALL, "ISO-8859-1");
1359	    execl("/bin/sh", "sh", "-c", command, NULL);
1360	    exit(-127);
1361	}
1362	else if (ispell.pid < 0) {
1363	    fprintf(stderr, "Cannot fork\n");
1364	    exit(1);
1365	}
1366	ispell.buf = XtMalloc(ispell.bufsiz = BUFSIZ);
1367	ispell.right = -1;
1368	ispell.id = XtAppAddInput(XtWidgetToApplicationContext(ispell.shell),
1369				  ispell.ifd[0], (XtPointer)XtInputReadMask,
1370				  IspellInputCallback, NULL);
1371	fcntl(ispell.ifd[0], F_SETFL, O_NONBLOCK);
1372    }
1373    else
1374	return (False);
1375
1376    return (True);
1377}
1378
1379/*ARGSUSED*/
1380static void
1381PopdownIspell(Widget w, XtPointer client_data, XtPointer call_data)
1382{
1383    (void)IspellEndProcess((Bool)(long)client_data, True);
1384    XtPopdown(ispell.shell);
1385    *ispell.sentbuf = '\0';
1386}
1387
1388static Bool
1389IspellCheckProcess(void)
1390{
1391    int status;
1392
1393    if (ispell.pid) {
1394	waitpid(ispell.pid, &status, WNOHANG);
1395	if (WIFEXITED(status)) {
1396	    ispell.pid = 0;
1397	}
1398	else
1399	    return (True);
1400    }
1401
1402    return (False);
1403}
1404
1405static Bool
1406IspellEndProcess(Bool killit, Bool killundo)
1407{
1408    ispell.source = NULL;
1409
1410    if (ispell.pid) {
1411	IgnoreEntry	*ientry;
1412	ReplaceEntry	*rentry;
1413
1414	/* insert added words in private dictionary */
1415	for (ientry = (IgnoreEntry *)hash_iter_first(ignore_hash);
1416	     ientry;
1417	     ientry = (IgnoreEntry *)hash_iter_next(ignore_hash)) {
1418	    if (ientry->add) {
1419		if (ientry->add == UNCAP)
1420		    write(ispell.ofd[1], "&", 1);
1421		else
1422		    write(ispell.ofd[1], "*", 1);
1423		write(ispell.ofd[1], ientry->word->value, ientry->word->length);
1424		write(ispell.ofd[1], "\n", 1);
1425	    }
1426	}
1427	write(ispell.ofd[1], "#\n", 2);		/* save dictionary */
1428	hash_clr(ignore_hash);
1429
1430	if (killit) {
1431	    XtRemoveInput(ispell.id);
1432
1433	    close(ispell.ofd[0]);
1434	    close(ispell.ofd[1]);
1435	    close(ispell.ifd[0]);
1436	    close(ispell.ifd[1]);
1437
1438	    /* if something goes wrong, we don't want to block here forever */
1439	    old_timeout = signal(SIGALRM, timeout_signal);
1440	    alarm(10);
1441	    waitpid(ispell.pid, NULL, 0);
1442	    alarm(0);
1443	    signal(SIGALRM, old_timeout);
1444
1445	    ispell.pid = 0;
1446	    if (ispell.buf)
1447		XtFree(ispell.buf);
1448	    ispell.buf = NULL;
1449
1450	    /* forget about replace matches */
1451	    for (rentry = (ReplaceEntry *)hash_iter_first(replace_hash);
1452		 rentry;
1453		 rentry = (ReplaceEntry *)hash_iter_next(replace_hash)) {
1454		XtFree(rentry->replace);
1455	    }
1456	    hash_clr(replace_hash);
1457	}
1458
1459	if (killundo)
1460	    IspellKillUndoBuffer();
1461    }
1462    else
1463	return (False);
1464
1465    return (True);
1466}
1467
1468static void
1469IspellKillUndoBuffer(void)
1470{
1471    ispell_undo *undo, *pundo;
1472
1473    undo = pundo = ispell.undo_base;
1474    while (undo) {
1475	undo = undo->next;
1476	if (pundo->undo_str)
1477	    XtFree(pundo->undo_str);
1478	XtFree((char*)pundo);
1479	pundo = undo;
1480    }
1481    ispell.undo_base = ispell.undo_head = NULL;
1482    ispell.undo_for = NULL;
1483    ispell.undo_depth = 0;
1484    XtSetSensitive(ispell.undo, False);
1485}
1486
1487/*ARGSUSED*/
1488static void
1489RevertIspell(Widget w, XtPointer client_data, XtPointer call_data)
1490{
1491    Arg args[1];
1492    char *string, *repstr = NULL;
1493
1494    XtSetArg(args[0], XtNlabel, &string);
1495    XtGetValues(ispell.word, args, 1);
1496    if ((repstr = strchr(string, ' ')) != NULL) {
1497	string = repstr = XtNewString(string);
1498	*strchr(repstr, ' ') = '\0';
1499    }
1500    XtSetArg(args[0], XtNstring, string);
1501    XtSetValues(ispell.text, args, 1);
1502    if (repstr)
1503	XtFree(repstr);
1504}
1505
1506/*ARGSUSED*/
1507static void
1508SelectIspell(Widget w, XtPointer client_data, XtPointer call_data)
1509{
1510    XawListReturnStruct *info = (XawListReturnStruct *)call_data;
1511    Arg args[1];
1512
1513    XtSetArg(args[0], XtNstring, ispell.item = info->string);
1514    XtSetValues(ispell.text, args, 1);
1515}
1516
1517/*ARGSUSED*/
1518void
1519ReplaceIspell(Widget w, XtPointer client_data, XtPointer call_data)
1520{
1521    XawTextPosition pos = XawTextGetInsertionPoint(ispell.ascii);
1522    XawTextBlock check, search, replace;
1523    Arg args[1];
1524    char *text;
1525
1526    if (!ispell.lock)
1527	return;
1528
1529    XtSetArg(args[0], XtNlabel, &text);
1530    XtGetValues(ispell.word, args, 1);
1531    search.ptr = text;
1532    search.format = XawFmt8Bit;
1533    search.firstPos = 0;
1534    search.length = ispell.right - pos;
1535
1536    XtSetArg(args[0], XtNstring, &text);
1537    XtGetValues(ispell.text, args, 1);
1538    replace.ptr = text;
1539    replace.format = XawFmt8Bit;
1540    replace.firstPos = 0;
1541    replace.length = strlen(text);
1542
1543    if (strcmp(search.ptr, replace.ptr) != 0 &&
1544	XawTextReplace(ispell.ascii, pos, pos + search.length,
1545		       &replace) == XawEditDone) {
1546	ispell.right += replace.length - search.length;
1547	IspellCheckUndo();
1548	ispell.undo_head->undo_str = NULL;
1549	ispell.undo_head->undo_pos = pos;
1550	ispell.undo_head->undo_count = 1;
1551
1552	if (ispell.repeat) {
1553	    ispell.undo_head->repeat = 2; /* To recognize later it was replaced */
1554	    ispell.undo_head->undo_count = ispell.right;
1555	    ispell.undo_head->undo_str = XtNewString(search.ptr);
1556	}
1557	if (client_data && !ispell.repeat) {
1558	    XawTextDisableRedisplay(ispell.ascii);
1559	    pos = ispell.right;
1560	    while ((pos = XawTextSourceSearch(ispell.source, pos, XawsdRight, &search))
1561		!= XawTextSearchError) {
1562		Bool do_replace = True;
1563		char mb[sizeof(wchar_t)];
1564
1565		if (XawTextSourceRead(ispell.source, pos - 1, &check, 1) > 0) {
1566		    if (international)
1567			wctomb(mb, *(wchar_t*)check.ptr);
1568		    else
1569			mb[0] = check.ptr[0];
1570		    do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb);
1571		}
1572		if (do_replace &&
1573		    XawTextSourceRead(ispell.source, pos + search.length, &check, 1) > 0) {
1574		    if (international)
1575			wctomb(mb, *(wchar_t*)check.ptr);
1576		    else
1577			mb[0] = check.ptr[0];
1578		    do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb);
1579		}
1580		if (do_replace) {
1581		    XawTextReplace(ispell.ascii, pos, pos + search.length, &replace);
1582		    ++ispell.undo_head->undo_count;
1583		}
1584		pos += search.length;
1585	    }
1586	    XawTextEnableRedisplay(ispell.ascii);
1587	}
1588	(void)IspellReplacedWord(search.ptr, replace.ptr);
1589
1590	strncpy(&ispell.sentbuf[1], replace.ptr, sizeof(ispell.sentbuf) - 2);
1591	ispell.sentbuf[sizeof(ispell.sentbuf) - 1] = '\0';
1592    }
1593    else
1594	Feep();
1595
1596    if (ispell.repeat)
1597	ispell.right = ispell.left = XawTextGetInsertionPoint(ispell.ascii);
1598    else if (!ispell.terse_mode || !ispell.item ||
1599	     strcmp(ispell.item, replace.ptr))
1600	ispell.right = ispell.left;	/* check it again! */
1601
1602    ispell.lock = ispell.checkit = False;
1603
1604    ispell.stat = SEND;
1605    IspellSetStatus(ispell.working_label);
1606    while (IspellSend() == 0)
1607	;
1608}
1609
1610/*ARGSUSED*/
1611void
1612IgnoreIspell(Widget w, XtPointer client_data, XtPointer call_data)
1613{
1614    Arg args[1];
1615    char *text;
1616
1617    if (!ispell.lock)
1618	return;
1619
1620    XtSetArg(args[0], XtNlabel, &text);
1621    XtGetValues(ispell.word, args, 1);
1622
1623    IspellCheckUndo();
1624
1625    if ((ispell.undo_head->repeat = ispell.repeat) != False) {
1626	ispell.undo_head->undo_count = ispell.right;
1627	ispell.undo_head->undo_str = XtNewString(text);
1628    }
1629    else
1630	ispell.undo_head->undo_count = 0;
1631
1632    ispell.undo_head->undo_pos = XawTextGetInsertionPoint(ispell.ascii);
1633
1634    if (!ispell.repeat) {
1635	if (client_data) {
1636	    IspellIgnoredWord(text, ADD, 0);
1637	    ispell.undo_head->undo_str = XtNewString(text);
1638	}
1639	else
1640	    ispell.undo_head->undo_str = NULL;
1641    }
1642
1643    ispell.lock = ispell.checkit = False;
1644
1645    ispell.stat = SEND;
1646    IspellSetStatus(ispell.working_label);
1647    while (IspellSend() == 0)
1648	;
1649}
1650
1651/*ARGSUSED*/
1652void
1653AddIspell(Widget w, XtPointer client_data, XtPointer call_data)
1654{
1655    Arg args[1];
1656    char *text;
1657    int cmd = (long)client_data;
1658
1659    if (!ispell.lock || ispell.repeat)
1660	return;
1661
1662    XtSetArg(args[0], XtNlabel, &text);
1663    XtGetValues(ispell.word, args, 1);
1664
1665    IspellCheckUndo();
1666    ispell.undo_head->undo_str = XtNewString(text);
1667    ispell.undo_head->undo_pos = XawTextGetInsertionPoint(ispell.ascii);
1668    ispell.undo_head->undo_count = -cmd;
1669
1670    (void)IspellIgnoredWord(text, ADD, cmd);
1671
1672    ispell.lock = ispell.checkit = False;
1673    ispell.stat = SEND;
1674    IspellSetStatus(ispell.working_label);
1675    while (IspellSend() == 0)
1676	;
1677}
1678
1679/*ARGSUSED*/
1680static void
1681UndoIspell(Widget w, XtPointer client_data, XtPointer call_data)
1682{
1683    Bool enable_redisplay = False;
1684    ispell_undo *undo = ispell.undo_head;
1685
1686    if ((!ispell.lock && ispell.stat) || !undo)
1687	return;
1688
1689    if (ispell.undo_for && strcmp(ispell.undo_for, ispell.dictionary)) {
1690	XeditPrintf("Undo: Dictionary changed. Undo information was lost.\n");
1691	IspellKillUndoBuffer();
1692	Feep();
1693	return;
1694    }
1695
1696    if (undo->terse != ispell.terse_mode)
1697	IspellSetTerseMode(undo->terse);
1698
1699    if (undo->format != ispell.format_info->value) {
1700	struct _ispell_format *fmt = &ispell_format[undo->format];
1701	ChangeFormatIspell(fmt->sme, (XtPointer)fmt, NULL);
1702    }
1703
1704    if (undo->undo_count > 0 && !undo->repeat) {
1705	XawTextPosition tmp;
1706
1707	enable_redisplay = undo->undo_count > 1;
1708	if (enable_redisplay)
1709	    XawTextDisableRedisplay(ispell.ascii);
1710	while (undo->undo_count--)
1711	    if (!_XawTextSrcUndo((TextSrcObject)ispell.source, &tmp)) {
1712		Feep();
1713		break;
1714	    }
1715    }
1716    else if (undo->undo_count < 0) {
1717	if (undo->undo_str)
1718	    (void)IspellIgnoredWord(undo->undo_str, REMOVE, -undo->undo_count);
1719    }
1720    else if (undo->undo_str) {
1721	if (!undo->repeat)
1722	    IspellIgnoredWord(undo->undo_str, REMOVE, 0);
1723    }
1724
1725    XawTextSetInsertionPoint(ispell.ascii,
1726			     ispell.right = ispell.left = undo->undo_pos);
1727    if (enable_redisplay)
1728	XawTextEnableRedisplay(ispell.ascii);
1729
1730    /* need to do it because may be two misspelled words together */
1731    if (undo->repeat) {
1732	char **list, **old_list;
1733	int old_len;
1734	Arg args[2];
1735
1736	if (undo->repeat > 1) {
1737	    XawTextDisableRedisplay(ispell.ascii);
1738	    if (!_XawTextSrcUndo((TextSrcObject)ispell.source, &ispell.right))
1739		Feep();
1740	    XawTextEnableRedisplay(ispell.ascii);
1741	}
1742	else
1743	    ispell.right = (XawTextPosition)undo->undo_count;
1744	IspellSetRepeated(ispell.repeat = True);
1745	XtSetArg(args[0], XtNlabel, undo->undo_str);
1746	XtSetValues(ispell.word, args, 1);
1747	XmuSnprintf(ispell.sentbuf, sizeof(ispell.sentbuf), "^%s",
1748		    strrchr(undo->undo_str, ' ') + 1);
1749	strcpy(ispell.sendbuf, ispell.sentbuf);
1750	XtSetArg(args[0], XtNstring, &ispell.sentbuf[1]);
1751	XtSetValues(ispell.text, args, 1);
1752
1753	XtSetArg(args[0], XtNlist, &old_list);
1754	XtSetArg(args[1], XtNnumberStrings, &old_len);
1755	XtGetValues(ispell.list, args, 2);
1756
1757	list = (char **)XtMalloc(sizeof(char*));
1758	list[0] = XtNewString(&ispell.sentbuf[1]);
1759	XtSetArg(args[0], XtNlist, list);
1760	XtSetArg(args[1], XtNnumberStrings, 1);
1761	XtSetValues(ispell.list, args, 2);
1762	XtSetSensitive(ispell.list, True);
1763	XawListHighlight(ispell.list, 0);
1764
1765	if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
1766	    while (--old_len > -1)
1767		XtFree(old_list[old_len]);
1768	    XtFree((char*)old_list);
1769	}
1770
1771	IspellSetSelection(ispell.left, ispell.right);
1772	IspellSetStatus(ispell.repeat_label);
1773	ispell.lock = True;
1774	ispell.checkit = False;
1775    }
1776    else if (ispell.repeat) {
1777	*ispell.sentbuf = '\0';
1778	IspellSetRepeated(ispell.repeat = False);
1779    }
1780
1781    if (undo->prev)
1782	undo->prev->next = NULL;
1783    ispell.undo_head = undo->prev;
1784    if (undo == ispell.undo_base) {
1785	ispell.undo_base = NULL;
1786	ispell.undo_for = NULL;
1787	XtSetSensitive(ispell.undo, False);
1788    }
1789    if (undo->undo_str)
1790	XtFree(undo->undo_str);
1791    XtFree((char*)undo);
1792    --ispell.undo_depth;
1793
1794    if (!ispell.stat || ispell.checkit)
1795	IspellSetSensitive(True);
1796
1797    if (!ispell.repeat) {
1798	ispell.lock = ispell.checkit = False;
1799	ispell.stat = SEND;
1800	IspellSetStatus(ispell.working_label);
1801	while (IspellSend() == 0)
1802	    ;
1803    }
1804}
1805
1806/*ARGSUSED*/
1807static void
1808CheckIspell(Widget w, XtPointer client_data, XtPointer call_data)
1809{
1810    Arg args[1];
1811    char *text, *str, string[1024];
1812    int i, len;
1813
1814    if (!ispell.lock)
1815	return;
1816
1817    XtSetArg(args[0], XtNstring, &text);
1818    XtGetValues(ispell.text, args, 1);
1819
1820    /* Check only a word at a time */
1821    len = 0;
1822    str = text;
1823    while (*str) {
1824	if (isalpha(*str) || strchr(ispell.wchars, *str))
1825	    break;
1826	++str;
1827	++len;
1828    }
1829    i = 0;
1830    while (*str) {
1831	if (isalpha(*str) || strchr(ispell.wchars, *str))
1832	    string[i++] = *str++;
1833	else
1834	    break;
1835    }
1836    string[i] = '\0';
1837
1838    if (strcmp(text, string)) {
1839	XawTextPosition pos = XawTextGetInsertionPoint(ispell.text) - len;
1840
1841	XtSetArg(args[0], XtNstring, string);
1842	XtSetValues(ispell.text, args, 1);
1843	XawTextSetInsertionPoint(ispell.text, pos);
1844	Feep();
1845    }
1846
1847    if (i == 0) {
1848	Feep();
1849	return;
1850    }
1851
1852    len = XmuSnprintf(ispell.sendbuf, sizeof(ispell.sendbuf), "^%s\n", string);
1853
1854    ispell.sendbuf[sizeof(ispell.sendbuf) - 1] = '\n';
1855
1856    write(ispell.ofd[1], ispell.sendbuf, len);
1857
1858    ispell.lock = False;
1859    ispell.checkit = True;
1860    ispell.stat = RECEIVE;
1861}
1862
1863/*ARGSUSED*/
1864static void
1865LookIspell(Widget w, XtPointer client_data, XtPointer call_data)
1866{
1867    int len, old_len;
1868    FILE *fd;
1869    Arg args[2];
1870    char *text, *str, **list, **old_list, command[1024], buffer[1024];
1871    Bool sensitive = True;
1872
1873    if (!ispell.lock)
1874	return;
1875
1876    XtSetArg(args[0], XtNstring, &text);
1877    XtGetValues(ispell.text, args, 1);
1878
1879    if (!*text) {
1880	Feep();
1881	return;
1882    }
1883
1884    if (strlen(ispell.look_cmd) + strlen(text) + strlen(ispell.words_file) + 8
1885	> sizeof(command) - 1) {
1886	fprintf(stderr, "Command line too large\n");
1887	return;
1888    }
1889
1890    XmuSnprintf(command, sizeof(command), "%s '^%s.*$' %s",
1891		ispell.look_cmd, text, ispell.words_file);
1892
1893    if ((fd = popen(command, "r")) == NULL) {
1894	fprintf(stderr, "Cannot popen '%s'\n", ispell.look_cmd);
1895	return;
1896    }
1897
1898    list = NULL;
1899    len = 0;
1900
1901#define	MAX_LOOK_RESULTS	256
1902    while (fgets(buffer, sizeof(buffer), fd) != NULL) {
1903	if ((str = strchr(buffer, '\n')) == NULL) {
1904	    fprintf(stderr, "String is too large\n");
1905	    break;
1906	}
1907	*str = '\0';
1908	if ((len % 16) == 0)
1909	    list = (char**)XtRealloc((char*)list, sizeof(char*) * (len + 16));
1910	list[len] = XtNewString(buffer);
1911	if (++len >= MAX_LOOK_RESULTS) {
1912	    Feep();
1913	    break;
1914	}
1915    }
1916#undef MAX_LOOK_RESULTS
1917
1918    XtSetArg(args[0], XtNlist, &old_list);
1919    XtSetArg(args[1], XtNnumberStrings, &old_len);
1920    XtGetValues(ispell.list, args, 2);
1921
1922    if (len == 0) {
1923	list = (char**)XtMalloc(sizeof(char*));
1924	list[0] = XtNewString("");
1925	len = 1;
1926	sensitive = False;
1927    }
1928
1929    XtSetArg(args[0], XtNlist, list);
1930    XtSetArg(args[1], XtNnumberStrings, len);
1931    XtSetValues(ispell.list, args, 2);
1932
1933    XtSetSensitive(ispell.list, sensitive);
1934    IspellSetStatus(sensitive ? ispell.look_label : ispell.none_label);
1935
1936    if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
1937	while (--old_len > -1)
1938	    XtFree(old_list[old_len]);
1939	XtFree((char*)old_list);
1940    }
1941
1942    pclose(fd);
1943}
1944
1945/*ARGSUSED*/
1946static void
1947ToggleTerseIspell(Widget w, XtPointer client_data, XtPointer call_data)
1948{
1949    if (!ispell.lock)
1950	return;
1951
1952    ispell.terse_mode = !ispell.terse_mode;
1953    write(ispell.ofd[1], ispell.terse_mode ? "!\n" : "%\n", 2);
1954}
1955
1956/*ARGSUSED*/
1957static void
1958ChangeDictionaryIspell(Widget w, XtPointer client_data, XtPointer call_data)
1959{
1960    ispell_dict *tmp, *dic = (ispell_dict*)client_data;
1961    XawTextPosition pos = XawTextGetInsertionPoint(ispell.ascii);
1962    XawTextPosition right = ispell.right;
1963    Arg args[1];
1964
1965    if (strcmp(XtName(dic->sme), ispell.dictionary) == 0)
1966	return;
1967
1968    if (!ispell.lock) {
1969	if (IspellCheckProcess()) {
1970	    Feep();
1971	    return;
1972	}
1973    }
1974
1975    for (tmp = ispell.dict_info; tmp; tmp = tmp->next)
1976	if (strcmp(XtName(tmp->sme), ispell.dictionary) == 0) {
1977	    XtSetArg(args[0], XtNleftBitmap, None);
1978	    XtSetValues(tmp->sme, args, 1);
1979	}
1980
1981    if (ispell.undo_base && !ispell.undo_for)
1982	ispell.undo_for = ispell.dictionary;
1983
1984    XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
1985    XtSetValues(dic->sme, args, 1);
1986    ispell.dictionary = XtName(dic->sme);
1987    ispell.wchars = dic->wchars;
1988    XtSetArg(args[0], XtNlabel, XtName(dic->sme));
1989    XtSetValues(ispell.dict, args, 1);
1990
1991    IspellSetStatus(ispell.working_label);
1992
1993    (void)IspellEndProcess(True, False);
1994    ispell.lock = ispell.checkit = False;
1995    (void)IspellStartProcess();
1996
1997    ispell.stat = RECEIVE;
1998
1999    /* restart at the same selected word */
2000    if (ispell.repeat == False)
2001	ispell.left = ispell.right = pos;
2002    else
2003	ispell.right = right;
2004}
2005
2006/*ARGSUSED*/
2007static void
2008ChangeFormatIspell(Widget w, XtPointer client_data, XtPointer call_data)
2009{
2010    struct _ispell_format *fmt = (struct _ispell_format*)client_data;
2011    Arg args[1];
2012
2013    if (strcmp(fmt->name, ispell.formatting) == 0)
2014	return;
2015
2016    if (!ispell.lock) {
2017	Feep();
2018	return;
2019    }
2020
2021    XtSetArg(args[0], XtNleftBitmap, None);
2022    XtSetValues(ispell.format_info->sme, args, 1);
2023
2024    XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
2025    XtSetValues(fmt->sme, args, 1);
2026    ispell.formatting = fmt->name;
2027    ispell.format_info = fmt;
2028    XtSetArg(args[0], XtNlabel, fmt->name);
2029    XtSetValues(ispell.format, args, 1);
2030}
2031
2032static Bool
2033InitIspell(void)
2034{
2035    Atom delete_window;
2036    char *str, *list;
2037    XtResource dict_res;
2038    ispell_dict *dict, *prev_dict;
2039    int i;
2040    static XtResource text_res[] = {
2041	{"skipLines", "Skip", XtRString, sizeof(char*),
2042	 XtOffsetOf(struct _ispell, skip), XtRString, "#"},
2043    };
2044
2045    if (ispell.shell)
2046	return (False);
2047
2048    replace_hash = hash_new(RSTRTBLSZ, NULL);
2049    ignore_hash = hash_new(ISTRTBLSZ, NULL);
2050
2051    ispell.shell	= XtCreatePopupShell("ispell", transientShellWidgetClass,
2052					     topwindow, NULL, 0);
2053
2054    XtGetApplicationResources(ispell.shell, (XtPointer)&ispell, resources,
2055			      XtNumber(resources), NULL, 0);
2056
2057    ispell.form		= XtCreateManagedWidget("form", formWidgetClass,
2058						ispell.shell, NULL, 0);
2059    ispell.mispelled	= XtCreateManagedWidget("mispelled", labelWidgetClass,
2060						ispell.form, NULL, 0);
2061    ispell.repeated	= XtCreateWidget("repeated", labelWidgetClass,
2062					 ispell.form, NULL, 0);
2063    ispell.word		= XtCreateManagedWidget("word", commandWidgetClass,
2064						ispell.form, NULL, 0);
2065    XtAddCallback(ispell.word, XtNcallback, RevertIspell, NULL);
2066    ispell.replacement	= XtCreateManagedWidget("replacement", labelWidgetClass,
2067						ispell.form, NULL, 0);
2068    ispell.text		= XtVaCreateManagedWidget("text", asciiTextWidgetClass,
2069						ispell.form,
2070						XtNeditType, XawtextEdit,
2071						NULL);
2072    ispell.suggestions	= XtCreateManagedWidget("suggestions", labelWidgetClass,
2073						ispell.form, NULL, 0);
2074    ispell.viewport	= XtCreateManagedWidget("viewport", viewportWidgetClass,
2075						ispell.form, NULL, 0);
2076    ispell.list		= XtCreateManagedWidget("list", listWidgetClass,
2077						ispell.viewport, NULL, 0);
2078    XtAddCallback(ispell.list, XtNcallback, SelectIspell, NULL);
2079    ispell.commands	= XtCreateManagedWidget("commands", formWidgetClass,
2080						ispell.form, NULL, 0);
2081    ispell.check	= XtCreateManagedWidget("check", commandWidgetClass,
2082						ispell.commands, NULL, 0);
2083    XtAddCallback(ispell.check, XtNcallback, CheckIspell, NULL);
2084    ispell.look		= XtCreateManagedWidget("look", commandWidgetClass,
2085						ispell.commands, NULL, 0);
2086    XtAddCallback(ispell.look, XtNcallback, LookIspell, NULL);
2087    ispell.undo		= XtCreateManagedWidget("undo", commandWidgetClass,
2088						ispell.commands, NULL, 0);
2089    XtAddCallback(ispell.undo, XtNcallback, UndoIspell, NULL);
2090    ispell.replace	= XtCreateManagedWidget("replace", commandWidgetClass,
2091						ispell.commands, NULL, 0);
2092    XtAddCallback(ispell.replace, XtNcallback, ReplaceIspell, (XtPointer)False);
2093    ispell.replaceAll	= XtCreateManagedWidget("replaceAll", commandWidgetClass,
2094						ispell.commands, NULL, 0);
2095    XtAddCallback(ispell.replaceAll, XtNcallback, ReplaceIspell, (XtPointer)True);
2096    ispell.ignore	= XtCreateManagedWidget("ignore", commandWidgetClass,
2097						ispell.commands, NULL, 0);
2098    XtAddCallback(ispell.ignore, XtNcallback, IgnoreIspell, (XtPointer)False);
2099    ispell.ignoreAll	= XtCreateManagedWidget("ignoreAll", commandWidgetClass,
2100						ispell.commands, NULL, 0);
2101    XtAddCallback(ispell.ignoreAll, XtNcallback, IgnoreIspell, (XtPointer)True);
2102    ispell.add		= XtCreateManagedWidget("add", commandWidgetClass,
2103						ispell.commands, NULL, 0);
2104    XtAddCallback(ispell.add, XtNcallback, AddIspell, (XtPointer)ASIS);
2105    ispell.addUncap	= XtCreateManagedWidget("addUncap", commandWidgetClass,
2106						ispell.commands, NULL, 0);
2107    XtAddCallback(ispell.addUncap, XtNcallback, AddIspell, (XtPointer)UNCAP);
2108    ispell.suspend	= XtCreateManagedWidget("suspend", commandWidgetClass,
2109						ispell.commands, NULL, 0);
2110    XtAddCallback(ispell.suspend, XtNcallback, PopdownIspell, (XtPointer)False);
2111    ispell.cancel	= XtCreateManagedWidget("cancel", commandWidgetClass,
2112						ispell.commands, NULL, 0);
2113    XtAddCallback(ispell.cancel, XtNcallback, PopdownIspell, (XtPointer)True);
2114    ispell.terse	= XtVaCreateManagedWidget("terse", toggleWidgetClass,
2115						  ispell.commands,
2116						  XtNstate, ispell.terse_mode,
2117						  NULL);
2118    XtAddCallback(ispell.terse, XtNcallback, ToggleTerseIspell, NULL);
2119    ispell.status	= XtCreateManagedWidget("status", labelWidgetClass,
2120						ispell.form, NULL, 0);
2121    ispell.options	= XtCreateManagedWidget("options", formWidgetClass,
2122						ispell.form, NULL, 0);
2123    ispell.dict		= XtVaCreateManagedWidget("dict", menuButtonWidgetClass,
2124						  ispell.options,
2125						  XtNmenuName, "dictionaries",
2126						  NULL);
2127    ispell.dictMenu	= XtCreatePopupShell("dictionaries", simpleMenuWidgetClass,
2128					     ispell.options, NULL, 0);
2129    XtRealizeWidget(ispell.dictMenu);
2130
2131    ispell.format	= XtVaCreateManagedWidget("format", menuButtonWidgetClass,
2132						  ispell.options,
2133						  XtNmenuName, "formats",
2134						  NULL);
2135    ispell.formatMenu	= XtCreatePopupShell("formats", simpleMenuWidgetClass,
2136					     ispell.options, NULL, 0);
2137    XtRealizeWidget(ispell.formatMenu);
2138
2139    XtRealizeWidget(ispell.shell);
2140
2141    for (i = 0; i < sizeof(ispell_format) / sizeof(ispell_format[0]); i++) {
2142	struct _ispell_format *fmt = &ispell_format[i];
2143
2144	fmt->sme = XtCreateManagedWidget(fmt->name, smeBSBObjectClass,
2145					 ispell.formatMenu, NULL, 0);
2146	XtAddCallback(fmt->sme, XtNcallback, ChangeFormatIspell, (XtPointer)fmt);
2147
2148	if (strcmp(fmt->name, ispell.formatting) == 0) {
2149	    Arg args[1];
2150
2151	    XtSetArg(args[0], XtNlabel, ispell.formatting);
2152	    XtSetValues(ispell.format, args, 1);
2153	    XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
2154	    XtSetValues(fmt->sme, args, 1);
2155	    ispell.format_info = fmt;
2156	}
2157    }
2158    if (ispell.format_info == NULL) {
2159	Arg args[1];
2160	char msg[256];
2161
2162	ispell.format_info = &ispell_format[TEXT];
2163
2164	XmuSnprintf(msg, sizeof(msg),
2165		    "Unrecognized formatting type \"%s\", will use \"%s\"",
2166		    ispell.formatting, ispell.format_info->name);
2167	XtAppWarning(XtWidgetToApplicationContext(ispell.shell), msg);
2168	ispell.formatting = ispell.format_info->name;
2169
2170	XtSetArg(args[0], XtNlabel, ispell.format_info->name);
2171	XtSetValues(ispell.format, args, 1);
2172	XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
2173	XtSetValues(ispell.format_info->sme, args, 1);
2174    }
2175    XtGetApplicationResources(ispell_format[TEXT].sme, (XtPointer)&ispell,
2176			      text_res, XtNumber(text_res), NULL, 0);
2177
2178    dict_res.resource_name = "wordChars";
2179    dict_res.resource_class = "Chars";
2180    dict_res.resource_type = XtRString;
2181    dict_res.resource_size = sizeof(char*);
2182    dict_res.resource_offset = XtOffsetOf(ispell_dict, wchars);
2183    dict_res.default_type = XtRString;
2184    dict_res.default_addr = "";
2185
2186    list = XtNewString(ispell.dict_list);
2187
2188    /* Create first empty entry */
2189    dict = XtNew(ispell_dict);
2190    dict->sme = XtCreateManagedWidget("", smeBSBObjectClass,
2191				      ispell.dictMenu, NULL, 0);
2192    dict->wchars = "";
2193    XtAddCallback(dict->sme, XtNcallback, ChangeDictionaryIspell,
2194		  (XtPointer)dict);
2195    ispell.dict_info = prev_dict = dict;
2196
2197    for (str = strtok(list, " \t,"); str; str = strtok(NULL, " \t,")) {
2198	dict = XtNew(ispell_dict);
2199	dict->sme = XtCreateManagedWidget(str, smeBSBObjectClass,
2200					  ispell.dictMenu, NULL, 0);
2201	XtGetApplicationResources(dict->sme, (XtPointer)dict, &dict_res,
2202				  1, NULL, 0);
2203	XtAddCallback(dict->sme, XtNcallback, ChangeDictionaryIspell,
2204		      (XtPointer)dict);
2205	prev_dict->next = dict;
2206	prev_dict = dict;
2207	dict->next = NULL;
2208    }
2209    XtFree(list);
2210
2211    for (dict = ispell.dict_info; dict; dict = dict->next) {
2212	if (strcmp(XtName(dict->sme), ispell.dictionary) == 0) {
2213	    Arg args[1];
2214
2215	    XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
2216	    XtSetValues(dict->sme, args, 1);
2217	    XtSetArg(args[0], XtNlabel, XtName(dict->sme));
2218	    XtSetValues(ispell.dict, args, 1);
2219	    ispell.wchars = dict->wchars;
2220	    break;
2221	}
2222    }
2223
2224
2225    delete_window = XInternAtom(XtDisplay(ispell.shell), "WM_DELETE_WINDOW", False);
2226    XSetWMProtocols(XtDisplay(ispell.shell), XtWindow(ispell.shell), &delete_window, 1);
2227
2228    return (True);
2229}
2230