~ubuntu-branches/ubuntu/gutsy/vnc4/gutsy

« back to all changes in this revision

Viewing changes to unix/xc/programs/xedit/ispell.c

  • Committer: Bazaar Package Importer
  • Author(s): Ola Lundqvist
  • Date: 2006-05-15 20:35:17 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060515203517-l4lre1ku942mn26k
Tags: 4.1.1+X4.3.0-10
* Correction of critical security issue. Thanks to Martin Kogler
  <e9925248@student.tuwien.ac.at> that informed me about the issue,
  and provided the patch.
  This flaw was originally found by Steve Wiseman of intelliadmin.com.
* Applied patch from Javier Kohen <jkohen@users.sourceforge.net> that
  inform the user that only 8 first characters of the password will
  actually be used when typing more than 8 characters, closes:
  #355619.

Show diffs side-by-side

added added

removed removed

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