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