Logo Search packages:      
Sourcecode: x11-apps version File versions  Download package

ispell.c

/*
 * Copyright (c) 1999 by The XFree86 Project, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Except as contained in this notice, the name of the XFree86 Project shall
 * not be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization from the
 * XFree86 Project.
 *
 * Author: Paulo C├ęsar Pereira de Andrade
 */

/* $XdotOrg: app/xedit/ispell.c,v 1.7 2005/04/04 10:17:07 eich Exp $ */
/* $XFree86: xc/programs/xedit/ispell.c,v 1.19 2002/10/19 20:04:20 herrb Exp $ */

#include "xedit.h"
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <X11/Xaw/Toggle.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xos.h>

#define RECEIVE         1
#define SEND            2

#define CHECK           0
#define     ADD         1
#define REMOVE          2

#define     ASIS        1
#define UNCAP           2

/*
 * Types
 */
#define UNDO_DEPTH      16
typedef struct _ispell_undo {
    char *undo_str;
    int undo_count;
    XawTextPosition undo_pos;
    Boolean repeat;     /* two (misspelled?) words together */
    Boolean terse;
    int format;         /* remember text formatting style */
    struct _ispell_undo *next, *prev;
} ispell_undo;

typedef struct _ispell_dict {
    Widget sme;
    char *wchars;
    struct _ispell_dict *next;
} ispell_dict;

#define     TEXT  0
#define HTML      1
struct _ispell_format {
    char *name;
    int value;
    Widget sme;
};

static struct _ispell_format ispell_format[] = {
    {"text",      TEXT},
    {"html",      HTML},
};

struct _ispell {
    Widget shell, form, mispelled, repeated, word, replacement, text,
         suggestions, viewport, list, commands, replace, status,
         replaceAll, undo, ignore, ignoreAll, add, addUncap, suspend,
         cancel, check, look, terse, options, dict, dictMenu,
         format, formatMenu;

    Widget ascii, source;
    XtInputId id;
    int pid, ifd[2], ofd[2];
    XawTextPosition left, right;
    char *item;
    Bool lock;
    Bool repeat;
    Bool checkit;
    int stat;
    char *buf;
    int bufsiz;
    int buflen;
    char sendbuf[1024];
    char sentbuf[1024];

    int undo_depth;
    ispell_undo *undo_head, *undo_base;
    char *undo_for;

    char *wchars;
    char *cmd;
    char *skip;
    char *command;
    Boolean terse_mode, undo_terse_mode;
    char *guess_label, *miss_label, *root_label, *none_label, *eof_label,
       *compound_label, *ok_label, *repeat_label, *working_label, *look_label;
    char *look_cmd;
    char *words_file;

    char *dictionary;
    char *dict_list;
    ispell_dict *dict_info;

    int format_mode;    /* to undo correctly */
    char *formatting;
    struct _ispell_format *format_info;
};

typedef struct _ReplaceList {
    char *word;
    char *replace;
    struct _ReplaceList *next;
} ReplaceList;

typedef struct _IgnoreList {
    char *word;
    int add;
    struct _IgnoreList *next;
} IgnoreList;

/*
 * Prototypes
 */
static void AddIspell(Widget, XtPointer, XtPointer);
static void ChangeDictionaryIspell(Widget, XtPointer, XtPointer);
static void ChangeFormatIspell(Widget, XtPointer, XtPointer);
static void CheckIspell(Widget, XtPointer, XtPointer);
static void IgnoreIspell(Widget, XtPointer, XtPointer);
static Bool InitIspell(void);
static void IspellCheckUndo(void);
static int IspellConvertHtmlAmp(char*);
static Bool IspellDoIgnoredWord(char*, int, int);
static Bool IspellIgnoredWord(char*, int, int);
static void IspellInputCallback(XtPointer, int*, XtInputId*);
static void IspellKillUndoBuffer(void);
static Bool IspellReceive(void);
static char *IspellReplacedWord(char*, char*);
static int IspellSend(void);
static void IspellSetSelection(XawTextPosition, XawTextPosition);
static void IspellSetRepeated(Bool);
static void IspellSetSensitive(Bool);
static void IspellSetStatus(char*);
static void IspellSetTerseMode(Bool);
static Bool IspellStartProcess(void);
static Bool IspellEndProcess(Bool, Bool);
static void LookIspell(Widget, XtPointer, XtPointer);
static void PopdownIspell(Widget, XtPointer, XtPointer);
static void ReplaceIspell(Widget, XtPointer, XtPointer);
static void RevertIspell(Widget, XtPointer, XtPointer);
static void SelectIspell(Widget, XtPointer, XtPointer);
static void ToggleTerseIspell(Widget, XtPointer, XtPointer);
#ifndef SIGNALRETURNSINT
static void timeout_signal(int);
static void (*old_timeout)(int);
#else
static int timeout_signal(int);
static int (*old_timeout)(int);
#endif
static void UndoIspell(Widget, XtPointer, XtPointer);

Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*);

/*
 * Initialization
 */
static struct _ispell ispell;

#define RSTRTBLSZ 23
#define ISTRTBLSZ 71
static ReplaceList *replace_list[RSTRTBLSZ];
static IgnoreList *ignore_list[ISTRTBLSZ];

#ifndef XtCStatus
#define XtCStatus "Status"
#endif

#define Offset(field) XtOffsetOf(struct _ispell, field)
static XtResource resources[] = {
    {"wordChars", "Chars", XtRString, sizeof(char*),
      Offset(wchars), XtRString, ""},
    {"ispellCommand", "CommandLine", XtRString, sizeof(char*),
      Offset(cmd), XtRString, "/usr/local/bin/ispell"},
    {"terseMode", "Terse", XtRBoolean, sizeof(Boolean),
      Offset(terse_mode), XtRImmediate, (XtPointer)False},
    {"guessLabel", XtCStatus, XtRString, sizeof(String),
      Offset(guess_label), XtRString, "Guess"},
    {"missLabel", XtCStatus, XtRString, sizeof(String),
      Offset(miss_label), XtRString, "Miss"},
    {"rootLabel", XtCStatus, XtRString, sizeof(String),
      Offset(root_label), XtRString, "Root:"},
    {"noneLabel", XtCStatus, XtRString, sizeof(String),
      Offset(none_label), XtRString, "None"},
    {"compoundLabel", XtCStatus, XtRString, sizeof(String),
      Offset(compound_label), XtRString, "Compound"},
    {"okLabel", XtCStatus, XtRString, sizeof(String),
      Offset(ok_label), XtRString, "Ok"},
    {"eofLabel", XtCStatus, XtRString, sizeof(String),
      Offset(eof_label), XtRString, "End Of File"},
    {"repeatLabel", XtCStatus, XtRString, sizeof(String),
      Offset(repeat_label), XtRString, "Repeat"},
    {"workingLabel", XtCStatus, XtRString, sizeof(String),
      Offset(working_label), XtRString, "..."},
    {"lookLabel", XtCStatus, XtRString, sizeof(String),
      Offset(look_label), XtRString, "Look"},
    {"lookCommand", "CommandLine", XtRString, sizeof(char*),
      Offset(look_cmd), XtRString, "/usr/bin/egrep -i"},
    {"wordsFile", "Words", XtRString, sizeof(char*),
      Offset(words_file), XtRString, "/usr/share/dict/words"},
    {"dictionary", "Dictionary", XtRString, sizeof(char*),
      Offset(dictionary), XtRString, "american"},
    {"dictionaries", "Dictionary", XtRString, sizeof(char*),
      Offset(dict_list), XtRString, "american americanmed+ english"},
    {"formatting", "TextFormat", XtRString, sizeof(char*),
      Offset(formatting), XtRString, "text"},
};
#undef Offset

#ifdef NO_LIBC_I18N
static int
ToLower(int ch)
{
    char buf[2];

    *buf = ch;
    XmuNCopyISOLatin1Lowered(buf, buf, sizeof(buf));

    return (*buf);
}

static int
ToUpper(int ch)
{
    char buf[2];

    *buf = ch;
    XmuNCopyISOLatin1Uppered(buf, buf, sizeof(buf));

    return (*buf);
}

static int
IsLower(int ch)
{
    char upbuf[2];
    char lobuf[2];

    *upbuf = *lobuf = ch;
    XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
    XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));

    return (*lobuf != *upbuf && ch == *lobuf);
}

static int
IsUpper(int ch)
{
    char upbuf[2];
    char lobuf[2];

    *upbuf = *lobuf = ch;
    XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
    XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));

    return (*lobuf != *upbuf && ch == *upbuf);
}
#else
#define     ToLower     tolower
#define ToUpper   toupper
#define IsLower islower
#define IsUpper isupper
#endif

/*
 * Implementation
 */
#ifdef STDERR_FILENO
# define WRITES(s) write(STDERR_FILENO, s, strlen(s))
#else
# define WRITES(s) write(fileno(stderr), s, strlen(s))
#endif

/*ARGSUSED*/
#ifndef SIGNALRETURNSINT
static void
timeout_signal(int unused)
{
    int olderrno = errno;

    WRITES("Warning: Timeout waiting ispell process to die.\n");
    kill(ispell.pid, SIGTERM);
    errno = olderrno;
}
#else
static int
timeout_signal(int unused)
{
    int olderrno = errno;

    WRITES("Warning: Timeout waiting ispell process to die.\n");
    kill(ispell.pid, SIGTERM);
    
    errno = olderrno;
    return (0);
}
#endif

static void
IspellSetSelection(XawTextPosition left, XawTextPosition right)
{
    /* Try to make sure the selected word is completely visible */
    XawTextSetInsertionPoint(ispell.ascii, right);
    XawTextSetInsertionPoint(ispell.ascii, left);
    XawTextSetSelection(ispell.ascii, left, right);
}

static void
IspellSetStatus(char *label)
{
    Arg args[1];

    XtSetArg(args[0], XtNlabel, label);
    XtSetValues(ispell.status, args, 1);
}

static void
IspellSetRepeated(Bool state)
{
    static char *mispelled, *repeated;
    Arg args[1];

    if (mispelled == NULL) {
      XtSetArg(args[0], XtNlabel, &mispelled);
      XtGetValues(ispell.mispelled, args, 1);
      mispelled = XtNewString(mispelled);
    }
    if (repeated == NULL) {
      XtSetArg(args[0], XtNlabel, &repeated);
      XtGetValues(ispell.repeated, args, 1);
      repeated = XtNewString(repeated);
    }
    XtSetSensitive(ispell.replaceAll, !state);
    XtSetSensitive(ispell.ignoreAll, !state);
    XtSetSensitive(ispell.add, !state);
    XtSetSensitive(ispell.addUncap, !state);
    if (!state) {
      XtSetArg(args[0], XtNlabel, mispelled);
      XtSetValues(ispell.mispelled, args, 1);
    }
    else {
      XtSetArg(args[0], XtNlabel, repeated);
      XtSetValues(ispell.mispelled, args, 1);
    }
}

static void
IspellSetSensitive(Bool state)
{
    XtSetSensitive(ispell.replace, state);
    XtSetSensitive(ispell.replaceAll, state);
    XtSetSensitive(ispell.ignore, state);
    XtSetSensitive(ispell.ignoreAll, state);
    XtSetSensitive(ispell.add, state);
    XtSetSensitive(ispell.addUncap, state);
}

static void
IspellSetTerseMode(Bool mode)
{
    Arg args[1];

    XtSetArg(args[0], XtNstate, ispell.terse_mode = mode);
    XtSetValues(ispell.terse, args, 1);
    write(ispell.ofd[1], mode ? "!\n" : "%\n", 2);
}

static void
IspellCheckUndo(void)
{
    ispell_undo *undo = XtNew(ispell_undo);

    if (ispell.undo_for && strcmp(ispell.undo_for, ispell.dictionary)) {
      XeditPrintf("Undo: Dictionary changed. Previous undo information lost.\n");
      IspellKillUndoBuffer();
      Feep();
    }

    undo->next = NULL;
    undo->repeat = False;
    undo->terse = ispell.undo_terse_mode;
    undo->format = ispell.format_mode;
    if ((undo->prev = ispell.undo_head) != NULL)
      undo->prev->next = undo;
    else
      undo->prev = NULL;
    ++ispell.undo_depth;
    if (!ispell.undo_base) {
      ispell.undo_base = undo;
      XtSetSensitive(ispell.undo, True);
    }
    else if (ispell.undo_depth > UNDO_DEPTH) {
      ispell_undo *tmp;

      if (ispell.undo_base->undo_str)
          XtFree(ispell.undo_base->undo_str);
      tmp = ispell.undo_base->next;
      XtFree((char*)ispell.undo_base);
      tmp->prev = NULL;
      ispell.undo_base = tmp;
      ispell.undo_depth = UNDO_DEPTH;
    }
    ispell.undo_head = undo;
}

static char *
IspellReplacedWord(char *word, char *replace)
{
    ReplaceList *list;
    int ii = 0;
    char *pp = word;

    while (*pp)
      ii = (ii << 1) ^ *pp++;
    if (ii < 0)
      ii = -ii;
    ii %= RSTRTBLSZ;
    for (list = replace_list[ii]; list; list = list->next)
      if (strcmp(list->word, word) == 0) {
          if (replace) {
            XtFree(list->replace);
            list->replace = XtNewString(replace);
          }
          return (list->replace);
      }

    if (!replace)
      return (NULL);

    list = XtNew(ReplaceList);
    list->word = XtNewString(word);
    list->replace = XtNewString(replace);
    list->next = replace_list[ii];
    replace_list[ii] = list;

    return (list->replace);
}

static Bool
IspellDoIgnoredWord(char *word, int cmd, int add)
{
    IgnoreList *list, *prev;
    int ii = 0;
    char *pp = word;

    while (*pp)
      ii = (ii << 1) ^ *pp++;
    if (ii < 0)
      ii = -ii;
    ii %= ISTRTBLSZ;
    for (prev = list = ignore_list[ii]; list; prev = list, list = list->next)
      if (strcmp(list->word, word) == 0) {
          if (cmd == REMOVE) {
            XtFree(list->word);
            prev->next = list->next;
            XtFree((char*)list);
            if (prev == list)
                ignore_list[ii] = NULL;
            return (True);
          }
          return (cmd == CHECK);
      }

    if (cmd != ADD)
      return (False);

    list = XtNew(IgnoreList);
    list->word = XtNewString(word);
    list->add = add;
    list->next = ignore_list[ii];
    ignore_list[ii] = list;

    return (True);
}

static Bool
IspellIgnoredWord(char *word, int cmd, int add)
{
    if (add != UNCAP && IspellDoIgnoredWord(word, cmd, add))
      return (True);

    /* add/remove uncapped word to/of list,
     * or cheks for correct capitalization */
    if (add == UNCAP || cmd == CHECK) {
      unsigned char *str = (unsigned char*)word;
      unsigned char string[1024];
      Bool upper, status;
      int i;

      status = True;
      upper = IsUpper(*str);
      *string = upper ? ToLower(*str) : *str;
      if (*str)
          str++;
      if (IsLower(*str))
          upper = False;
      for (i = 1; *str && i < sizeof(string) - 1; i++, str++) {
          if (upper && IsLower(*str))
            status = False;
          else if (!upper && IsUpper(*str))
            status = False;
          string[i] = ToLower(*str);
      }
      string[i] = '\0';

      if ((cmd != CHECK || status) &&
          IspellDoIgnoredWord((char*)string, cmd, add))
          return (True);
    }

    return (False);
}

/*ARGSUSED*/
static Bool
IspellReceive(void)
{
    int i, len, old_len;
    Arg args[2];
    char *str, *end, **list, **old_list;
    char *tmp, word[1024];
    int j;

    if (ispell.lock || ispell.stat != RECEIVE)
      return (False);

    while (1) {         /* read the entire line */
      if (ispell.buflen >= ispell.bufsiz - 1)
          ispell.buf = XtRealloc(ispell.buf, ispell.bufsiz += BUFSIZ);
      if ((len = read(ispell.ifd[0], &ispell.buf[ispell.buflen],
                  ispell.bufsiz - ispell.buflen - 1)) <= 0)
          break;
      ispell.buflen += len;
    }
    if (ispell.buflen <= 0)
      return (False);
    len = 0;
    i = ispell.buflen - 1;
    while (i >= 0 && ispell.buf[i] == '\n') {
      ++len;
      --i;
    }
    if (len < 2 - ((ispell.terse_mode && i == -1) || ispell.buf[0] == '@'))
      return (False);
    ispell.buf[ispell.buflen - len] = '\0';
    ispell.buflen = 0;

    if ((tmp = strchr(ispell.sendbuf, '\n')) != NULL)
      *tmp = '\0';

    switch (ispell.buf[0]) {
      case '&':   /* MISS */
      case '?':   /* GUESS */
          str = strchr(&ispell.buf[2], ' ');
          if (!ispell.checkit) {
            *str = '\0';
            XtSetArg(args[0], XtNlabel, &ispell.buf[2]);
            XtSetValues(ispell.word, args, 1);
          }
          ++str;
          list = NULL;
          str = strchr(str, ':') + 1;
          for (i = 0; ; i++) {
            end = strchr(str, ',');
            if (end)    *end = '\0';
            if ((i % 16) == 0)
                list = (char**)XtRealloc((char*)list, (i + 16) * sizeof(char*));
            tmp = word;
            for (j = 1; j < sizeof(word) && str[j]; j++) {
                if (str[j] == '+')
                  continue;
                else if (str[j] == '-' && str[j+1] != '-' && str[j-1] != '-') {
                  char *p, string[256];
                  int k, l;

                  for (l = 0, k = j + 1; str[k] != '+' && str[k] != '-'
                       && str[k] && l < sizeof(string) - 1; k++, l++)
                      string[l] = str[k];
                  string[l] = '\0';
                  *tmp = '\0';
                  if (l && (p = strstr(word, string)) != NULL) {
                      char *sav = p;

                      while ((p = strstr(p + l, string)) != NULL)
                        sav = p;
                      p = sav;
                      if (strcmp(p, string) == 0) {
                        tmp = p;
                        j = k - 1;
                      }
                      else
                        *tmp++ = '-';
                  }
                  else
                      *tmp++ = '-';
                }
                else
                  *tmp++ = str[j];
            }
            *tmp = '\0';
            list[i] = XtNewString(word);

            if (end)    str = end + 1;
            else        break;
          }
          len = i + 1;

          XtSetArg(args[0], XtNlist, &old_list);
          XtSetArg(args[1], XtNnumberStrings, &old_len);
          XtGetValues(ispell.list, args, 2);

          ispell.item = NULL;
          if ((str = IspellReplacedWord(&ispell.buf[2], NULL)) != NULL)
            for (i = 0; i < len; i++) {
                if (strcmp(list[i], str) == 0) {
                  ispell.item = list[i];
                  break;
                }
            }
          else
            ispell.item = list[i = 0];
          if (!ispell.item) {
            list = (char**)XtRealloc((char*)list, (len + 1) * sizeof(char*));
            ispell.item = list[i] = XtNewString(str);
            ++len;
          }

          XtSetArg(args[0], XtNlist, list);
          XtSetArg(args[1], XtNnumberStrings, len);
          XtSetValues(ispell.list, args, 2);

          XtSetSensitive(ispell.list, True);
          if (!ispell.checkit)
            XawListHighlight(ispell.list, i);

          if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
            while (--old_len > -1)
                XtFree(old_list[old_len]);
            XtFree((char*)old_list);
          }

          if (!ispell.checkit) {
            XtSetArg(args[0], XtNstring, ispell.item);
            XtSetValues(ispell.text, args, 1);
            IspellSetSelection(ispell.left, ispell.right);
            if (ispell.repeat)
                IspellSetRepeated(ispell.repeat = False);
          }

          IspellSetStatus(ispell.buf[0] == '?' ?
                      ispell.guess_label : ispell.miss_label);
          ispell.undo_terse_mode = ispell.terse_mode;
          ispell.format_mode = ispell.format_info->value;
          ispell.lock = True;
          break;
      case '#':   /* NONE */
      case '-':   /* COMPOUND */
      case '+':   /* ROOT */
      check_label:
          str = &ispell.sendbuf[1];
          if (!ispell.checkit) {
            XtSetArg(args[0], XtNlabel, str);
            XtSetValues(ispell.word, args, 1);
          }

          XtSetArg(args[0], XtNlist, &old_list);
          XtSetArg(args[1], XtNnumberStrings, &old_len);
          XtGetValues(ispell.list, args, 2);
          ispell.item = NULL;

          list = (char**)XtMalloc(sizeof(char**));
          if ((tmp = IspellReplacedWord(str, NULL)) != NULL)
            str = tmp;
          if (tmp == NULL && ispell.buf[0] == '#')
            list[0] = XtNewString("");
          else
            list[0] = XtNewString(str);

          XtSetArg(args[0], XtNlist, list);
          XtSetArg(args[1], XtNnumberStrings, 1);
          XtSetValues(ispell.list, args, 2);

          if (tmp == NULL && ispell.buf[0] == '#') {
            XawListUnhighlight(ispell.list);
            XtSetSensitive(ispell.list, False);
          }
          else {
            XtSetSensitive(ispell.list, True);
            if (!ispell.checkit)
                XawListHighlight(ispell.list, 0);
          }
          if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
            while (--old_len > -1)
                XtFree(old_list[old_len]);
            XtFree((char*)old_list);
          }

          if (!ispell.checkit) {
            XtSetArg(args[0], XtNstring, str);
            XtSetValues(ispell.text, args, 1);
            IspellSetSelection(ispell.left, ispell.right);
            if (ispell.repeat)
                IspellSetRepeated(ispell.repeat = False);
          }

          ispell.undo_terse_mode = ispell.terse_mode;
          ispell.format_mode = ispell.format_info->value;
          ispell.lock = True;
          if (ispell.buf[0] == '+') {
            if ((tmp = strchr(&ispell.buf[2], '\n')) != NULL)
                *tmp = '\0';
            XmuSnprintf(word, sizeof(word), "%s %s",
                      ispell.root_label, &ispell.buf[2]);
            IspellSetStatus(word);
          }
          else
            IspellSetStatus(ispell.buf[0] == '#' ? ispell.none_label :
                        ispell.buf[0] == '-' ? ispell.compound_label :
                        ispell.ok_label);
          break;
      case '*':   /* OK */
      case '\0':  /* when running in terse mode */
          if (!ispell.checkit)
            (void)IspellIgnoredWord(&ispell.sendbuf[1], ADD, 0);
          else
            goto check_label;
          ispell.lock = False;
          break;
      case '@':   /* Ispell banner */
          /* it only happens when the dictionary is changed */
          if (!ispell.repeat) {
            XawTextPosition left, right;

            ispell.stat = SEND;
            while (IspellSend() == 0)
                ;
            /* word chars may have changed */
            XawTextGetSelectionPos(ispell.ascii, &left, &right);
            if (left != ispell.left || right != ispell.right) {
                XtSetArg(args[0], XtNstring, &ispell.sendbuf[1]);
                XtSetValues(ispell.text, args, 1);
                IspellSetSelection(ispell.left, ispell.right);
            }
            ispell.checkit = True;
          }
          else {
            IspellSetStatus(ispell.repeat_label);
            ispell.undo_terse_mode = ispell.terse_mode;
            ispell.format_mode = ispell.format_info->value;
            ispell.lock = True;
            return (True);
          }
          break;
      default:
          fprintf(stderr, "Unknown ispell command '%c'\n", ispell.buf[0]);
          return (False);
    }

    if (!ispell.lock && !ispell.checkit) {
      ispell.stat = SEND;
      while (IspellSend() == 0)
          ;
    }

    return (True);
}

static int
IspellConvertHtmlAmp(char *buf)
{
    int len, ch = '?';

    /* this function is static, so I can do it */
    *strchr(++buf, ';') = '\0';

    len = strlen(buf);
    if (len == 0)
      return ('&');
    if (len > 1) {
      if (strcasecmp(&buf[1], "lt") == 0)
          ch = '<';
      else if (strcasecmp(&buf[1], "gt") == 0)
          ch = '>';
      else if (strcasecmp(&buf[1], "nbsp") == 0)
          ch = ' ';
      else if (strcasecmp(&buf[1], "amp") == 0)
          ch = '&';
      else if (strcasecmp(&buf[1], "quot") == 0)
          ch = '"';
      else if (*buf == '#') {
          char *tmp;

          if (len == 1)
            return ('?');
          ch = strtol(&buf[1], &tmp, 10);
          if (*tmp)
            fprintf(stderr, "Warning: bad html interpreting '&#' mark.\n");
      }
      else if (strcmp(&buf[1], "acute") == 0) {
          switch (*buf) {
            case 'a': ch = 0xe1; break;
            case 'e': ch = 0xe9; break;
            case 'i': ch = 0xed; break;
            case 'o': ch = 0xf3; break;
            case 'u': ch = 0xfa; break;
            case 'A': ch = 0xc1; break;
            case 'E': ch = 0xc9; break;
            case 'I': ch = 0xcd; break;
            case 'O': ch = 0xd3; break;
            case 'U': ch = 0xda; break;
          }
      }
      else if (strcmp(&buf[1], "grave") == 0) {
          switch (*buf) {
            case 'a': ch = 0xe0; break;
            case 'e': ch = 0xe8; break;
            case 'i': ch = 0xec; break;
            case 'o': ch = 0xf2; break;
            case 'u': ch = 0xf9; break;
            case 'A': ch = 0xc0; break;
            case 'E': ch = 0xc8; break;
            case 'I': ch = 0xcc; break;
            case 'O': ch = 0xd2; break;
            case 'U': ch = 0xd9; break;
          }
      }
      else if (strcmp(&buf[1], "tilde") == 0) {
          switch (*buf) {
            case 'a': ch = 0xe3; break;
            case 'o': ch = 0xf5; break;
            case 'n': ch = 0xf1; break;
            case 'A': ch = 0xe3; break;
            case 'O': ch = 0xd5; break;
            case 'N': ch = 0xd1; break;
          }
      }
      else if (strcmp(&buf[1], "circ") == 0) {
          switch (*buf) {
            case 'a': ch = 0xe2; break;
            case 'e': ch = 0xea; break;
            case 'i': ch = 0xee; break;
            case 'o': ch = 0xf4; break;
            case 'u': ch = 0xfb; break;
            case 'A': ch = 0xc2; break;
            case 'E': ch = 0xca; break;
            case 'I': ch = 0xce; break;
            case 'O': ch = 0xd4; break;
            case 'U': ch = 0xdb; break;
          }
      }
      else if (strcmp(&buf[1], "cedil") == 0) {
          switch (*buf) {
            case 'c': ch = 0xe7; break;
            case 'C': ch = 0xc7; break;
          }
      }
      /* add more cases here */
    }

    return (ch);
}

/*ARGSUSED*/
static int
IspellSend(void)
{
    XawTextPosition position, old_left, pos;
    XawTextBlock block;
    int i, len, spaces, nls;
    Bool nl, html, inside_html;
    char ampbuf[32];
    int amplen;

    if (ispell.lock || ispell.stat != SEND)
      return (-1);

    len = 1;
    ispell.sendbuf[0] = '^';  /* don't evaluate following characters as commands */

    spaces = nls = 0;

    html = ispell.format_info->value == HTML;
    inside_html = False;
    amplen = 0;

    /* skip non word characters */
    pos = position = ispell.right;
    nl = False;
    while (1) {
      Bool done = False;
      char mb[sizeof(wchar_t)];

      retry_html_space:
      position = XawTextSourceRead(ispell.source, position,
                             &block, BUFSIZ);
      if (block.length == 0) {      /* end of file */
          ispell.stat = 0;
          ispell.lock = True;
          XawTextSetInsertionPoint(ispell.ascii, ispell.right);
          XawTextUnsetSelection(ispell.ascii);
          IspellSetSensitive(False);
          IspellSetStatus(ispell.eof_label);
          return (-1);
      }
      for (i = 0; i < block.length; i++) {
          wctomb(mb, ((wchar_t*)block.ptr)[i]);
          if (amplen) {
            if (amplen + 2 >= sizeof(ampbuf)) {
                if (!ispell.terse_mode)
                  fprintf(stderr, "Warning: error interpreting '&' mark.\n");
                amplen = 0;
                position = pos + 1;
                goto retry_html_space;
            }
            else if ((ampbuf[amplen++] = *mb) == ';') {
                int ch;

                ampbuf[amplen] = '\0';
                ch = IspellConvertHtmlAmp(ampbuf);
                amplen = 0;
                if (isalpha(ch) ||
                  (ch && strchr(ispell.wchars, ch))) {
                  /* interpret it again */
                  ispell.right = pos;
                  i = 0;
                  done = True;
                  break;
                }
                else if ((ch == '\n' || isspace(ch)) && spaces >= 0)
                  ++spaces;
                else
                  spaces = -1;
            }
          }
          else if (html && *mb == '&') {
            ampbuf[amplen++] = *mb;
            pos = block.firstPos + i;
            continue;
          }
          else if ((!html || !inside_html) && (isalpha(*mb) ||
            (*mb && strchr(ispell.wchars, *mb)))) {
            done = True;
            break;
          }
          else if (!html && *mb == '\n') {
            nl = True;
            if (++nls > 1 && (!html || !inside_html))
                spaces = -1;
            else if (spaces >= 0)
                ++spaces;
          }
          else if (nl) {
            nl = False;
            if (*mb && strchr(ispell.skip, *mb)) {
                position = ispell.right =
                  XawTextSourceScan(ispell.source, ispell.right + i,
                                XawstEOL, XawsdRight, 1, False);
                i = 0;
                break;
            }
            else if (spaces >= 0 && isspace(*mb))
                ++spaces;
            else
                spaces = -1;
          }
          else if (html && inside_html) {
            if (*mb == '>')
                inside_html = False;
          }
          else if (html && *mb == '<')
            inside_html = True;
          else if (spaces >= 0 && (isspace(*mb) || (html && *mb == '\n')))
            ++spaces;
          else
            spaces = -1;
      }

      ispell.right += i;
      if (done)
          break;
    }

    old_left = ispell.left;

    /* read a word */
    position = ispell.left = ispell.right;
    while (1) {
      Bool done = False;
      char mb[sizeof(wchar_t)];

      retry_html_word:
      position = XawTextSourceRead(ispell.source, position,
                             &block, BUFSIZ);
      if (block.length == 0 && len == 1) {      /* end of file */
          ispell.stat = 0;
          ispell.lock = True;
          XawTextSetInsertionPoint(ispell.ascii, ispell.right);
          XawTextUnsetSelection(ispell.ascii);
          IspellSetSensitive(False);
          IspellSetStatus(ispell.eof_label);
          return (-1);
      }
      for (i = 0; i < block.length; i++) {
          wctomb(mb, ((wchar_t*)block.ptr)[i]);
          if (amplen) {
            if (amplen + 2 >= sizeof(ampbuf)) {
                if (!ispell.terse_mode)
                  fprintf(stderr, "Warning: error interpreting '&' mark.\n");
                amplen = 0;
                position = pos + 1;
                if (strchr(ispell.wchars, '&')) {
                  if (len + 1 >= sizeof(ispell.sendbuf) - 1) {
                      done = True;
                      fprintf(stderr, "Warning: word is too large!\n");
                      break;
                  }
                  ispell.sendbuf[len++] = '&';
                  goto retry_html_word;
                }
                else {
                  ispell.right = position;
                  i = 0;
                  done = True;
                  break;
                }
            }
            else if ((ampbuf[amplen++] = *mb) == ';') {
                int ch;

                ampbuf[amplen] = '\0';
                ch = IspellConvertHtmlAmp(ampbuf);
                amplen = 0;
                if (!isalpha(ch) &&
                  (!ch || !strchr(ispell.wchars, ch))) {
                  ispell.right = pos;
                  i = 0;
                  done = True;
                  break;
                }
                *mb = ch;
            }
            else
                continue;
          }
          else if (html && *mb == '&') {
            ampbuf[amplen++] = *mb;
            pos = block.firstPos + i;
            continue;
          }
          else if (!isalpha(*mb) && (!*mb || !strchr(ispell.wchars, *mb))) {
            done = True;
            break;
          }
          ispell.sendbuf[len] = *mb;
          if (++len >= sizeof(ispell.sendbuf) - 1) {
            done = True;
            fprintf(stderr, "Warning: word is too large!\n");
            break;
          }
      }
      ispell.right += i;
      if (done || block.length == 0)
          break;
    }

    ispell.sendbuf[len] = '\0';

    if (spaces > 0 && spaces <= 32 && strcmp(ispell.sendbuf, ispell.sentbuf) == 0) {
      Arg args[2];
      int old_len;      
      char **list, **old_list;
      char label[sizeof(ispell.sendbuf) + sizeof(ispell.sentbuf) + 32];

      strcpy(label, &ispell.sendbuf[1]);
      for (i = 0; i < spaces; i++)
          label[len + i - 1] = ' ';
      strcpy(&label[len + i - 1], &ispell.sendbuf[1]);
      XtSetArg(args[0], XtNlabel, label);
      XtSetValues(ispell.word, args, 1);

      XtSetArg(args[0], XtNstring, &ispell.sendbuf[1]);
      XtSetValues(ispell.text, args, 1);

      XtSetArg(args[0], XtNlist, &old_list);
      XtSetArg(args[1], XtNnumberStrings, &old_len);
      XtGetValues(ispell.list, args, 2);
      list = (char**)XtMalloc(sizeof(char**));
      list[0] = XtNewString(&ispell.sendbuf[1]);
      XtSetArg(args[0], XtNlist, list);
      XtSetArg(args[1], XtNnumberStrings, 1);
      XtSetValues(ispell.list, args, 2);
      XtSetSensitive(ispell.list, True);
      XawListHighlight(ispell.list, 0);
      if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
          while (--old_len > -1)
            XtFree(old_list[old_len]);
          XtFree((char*)old_list);
      }

      IspellSetRepeated(True);
      IspellSetSelection(old_left, ispell.right);
      IspellSetStatus(ispell.repeat_label);
      ispell.repeat = ispell.lock = True;

      return (1);
    }
    strcpy(ispell.sentbuf, ispell.sendbuf);

    if (len <= 2 || IspellIgnoredWord(&ispell.sendbuf[1], CHECK, 0))
      return (0);

    ispell.sendbuf[len++] = '\n';

    write(ispell.ofd[1], ispell.sendbuf, len);

    ispell.stat = RECEIVE;

    return (1);
}

/*ARGSUSED*/
static void
IspellInputCallback(XtPointer closure, int *source, XtInputId *id)
{
    if (ispell.right < 0) {
      int len;
      char buf[1024];

      ispell.right = XawTextGetInsertionPoint(ispell.ascii);
      ispell.right = XawTextSourceScan(ispell.source, ispell.right,
                                    XawstEOL, XawsdLeft, 1, True);
      len = read(ispell.ifd[0], buf, sizeof(buf));
      if (strncmp(buf, "@(#)", 4) == 0) {
          Arg args[1];

          buf[len - 1] = '\0';
          XtSetArg(args[0], XtNtitle, &buf[5]);
          XtSetValues(ispell.shell, args, 1);
      }
      else
          fprintf(stderr, "Error: is ispell talking with me?\n");
      IspellSetTerseMode(ispell.terse_mode);
      while (IspellSend() == 0)
          ;
    }
    else if (ispell.source)
      IspellReceive();
}

/*ARGSUSED*/
void
IspellCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
    Cardinal zero = 0;

    IspellAction(textwindow, NULL, NULL, &zero);
}

/*ARGSUSED*/
void
IspellAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    Arg args[3];
    Cardinal num_args;
    char **strs, **list;
    int n_strs;
    Bool first_time = InitIspell();

    if (*num_params == 1 && (params[0][0] == 'e' || params[0][0] == 'E')) {
      PopdownIspell(w, (XtPointer)True, NULL);
      return;
    }

    if (!XtIsSubclass(w, textWidgetClass) || ispell.source) {
      Feep();
      return;
    }

    ispell.source = XawTextGetSource(ispell.ascii = w);

    if (first_time) {
      /* let the user choose the better position for the ispell window */
      Dimension width, height, b_width;
      Position x, y, max_x, max_y;

      x = y = -1;
      if (event) {
          switch (event->type) {
            case ButtonPress:
            case ButtonRelease:
                x = event->xbutton.x_root;
                y = event->xbutton.y_root;
                break;
            case KeyPress:
            case KeyRelease:
                x = event->xkey.x_root;
                y = event->xkey.y_root;
                break;
          }
      }
      if (x < 0 || y < 0) {
          Window r, c;
          int rx, ry, wx, wy;
          unsigned mask;

          XQueryPointer(XtDisplay(ispell.shell), XtWindow(ispell.shell),
                    &r, &c, &rx, &ry, &wx, &wy, &mask);
          x = rx;
          y = ry;
      }

      num_args = 0;
      XtSetArg(args[num_args], XtNwidth, &width);           num_args++;
      XtSetArg(args[num_args], XtNheight, &height);         num_args++;
      XtSetArg(args[num_args], XtNborderWidth, &b_width);   num_args++;
      XtGetValues(ispell.shell, args, num_args);

      width += b_width << 1;
      height += b_width << 1;

      x -= (Position)(width >> 1);
      if (x < 0)
          x = 0;
      if (x > (max_x = (Position)(XtScreen(w)->width - width)))
          x = max_x;

      y -= (Position)(height >> 1);
      if (y < 0)
          y = 0;
      if (y > (max_y = (Position)(XtScreen(w)->height - height)))
          y = max_y;

      num_args = 0;
      XtSetArg(args[num_args], XtNx, x);  num_args++;
      XtSetArg(args[num_args], XtNy, y);  num_args++;
      XtSetValues(ispell.shell, args, num_args);
    }

    if (ispell.repeat)
      IspellSetRepeated(False);
    ispell.lock = ispell.repeat = ispell.checkit = False;
    ispell.stat = SEND;

    IspellSetSensitive(True);
    XtSetSensitive(ispell.undo, False);

    XtSetArg(args[0], XtNlabel, "");
    XtSetValues(ispell.word, args, 1);

    XtSetArg(args[0], XtNstring, "");
    XtSetValues(ispell.text, args, 1);

    XtSetArg(args[0], XtNlist, &strs);
    XtSetArg(args[1], XtNnumberStrings, &n_strs);
    XtGetValues(ispell.list, args, 2);

    list = (char**)XtMalloc(sizeof(char**));
    list[0] = XtNewString("");
    XtSetArg(args[0], XtNlist, list);
    XtSetArg(args[1], XtNnumberStrings, 1);
    XtSetValues(ispell.list, args, 2);

    if (n_strs > 1 || (XtName(ispell.list) != strs[0])) {
      while (--n_strs > -1)
          XtFree(strs[n_strs]);
      XtFree((char*)strs);
    }

    IspellSetStatus(ispell.working_label);

    if (!ispell.pid)
      (void)IspellStartProcess();
    else {
      ispell.right = XawTextGetInsertionPoint(ispell.ascii);
      ispell.right = XawTextSourceScan(ispell.source, ispell.right,
                                    XawstEOL, XawsdLeft, 1, True);
      while (IspellSend() == 0)
          ;
    }

    XtPopup(ispell.shell, XtGrabExclusive);
    XtSetKeyboardFocus(ispell.shell, ispell.text);
}

static Bool
IspellStartProcess(void)
{
    if (!ispell.pid) {
      int len;
      char *command;

      ispell.source = XawTextGetSource(ispell.ascii);

      len = strlen(ispell.cmd) + strlen(ispell.dictionary) +
            strlen(ispell.wchars) + 16;
      command = XtMalloc(len);
      XmuSnprintf(command, len, "%s -a -d '%s' -w '%s'",
                ispell.cmd, ispell.dictionary, ispell.wchars);

      pipe(ispell.ifd);
      pipe(ispell.ofd);
      if ((ispell.pid = fork()) == 0) {
          close(0);
          close(1);
          dup2(ispell.ofd[0], 0);
          dup2(ispell.ifd[1], 1);
          close(ispell.ofd[0]);
          close(ispell.ofd[1]);
          close(ispell.ifd[0]);
          close(ispell.ifd[1]);
          execl("/bin/sh", "sh", "-c", command, (void *)NULL);
          exit(-127);
      }
      else if (ispell.pid < 0) {
          fprintf(stderr, "Cannot fork\n");
          exit(1);
      }
      ispell.buf = XtMalloc(ispell.bufsiz = BUFSIZ);
      ispell.right = -1;
      ispell.id = XtAppAddInput(XtWidgetToApplicationContext(ispell.shell),
                          ispell.ifd[0], (XtPointer)XtInputReadMask,
                          IspellInputCallback, NULL);
      fcntl(ispell.ifd[0], F_SETFL, O_NONBLOCK);
    }
    else
      return (False);

    return (True);
}

/*ARGSUSED*/
static void
PopdownIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    (void)IspellEndProcess((Bool)(long)client_data, True);
    XtPopdown(ispell.shell);
    *ispell.sentbuf = '\0';
}

static Bool
IspellEndProcess(Bool killit, Bool killundo)
{
    ispell.source = NULL;

    if (ispell.pid) {
      IgnoreList *il, *pil, *nil;
      int i;

      /* insert added words in private dictionary */
      for (i = 0; i < ISTRTBLSZ; i++) {
          pil = il = ignore_list[i];
          while (il) {
            if (il->add) {
                nil = il->next;
                if (il == pil)
                  ignore_list[i] = nil;
                else
                  pil->next = nil;
                if (il->add == UNCAP)
                  write(ispell.ofd[1], "&", 1);
                else
                  write(ispell.ofd[1], "*", 1);
                write(ispell.ofd[1], il->word, strlen(il->word));
                write(ispell.ofd[1], "\n", 1);
                XtFree(il->word);
                XtFree((char*)il);
                il = nil;
            }
            else
                il = il->next;
            pil = il;
          }
      }
      write(ispell.ofd[1], "#\n", 2);           /* save dictionary */

      if (killit) {
          ReplaceList *rl, *prl;

          XtRemoveInput(ispell.id);

          close(ispell.ofd[0]);
          close(ispell.ofd[1]);
          close(ispell.ifd[0]);
          close(ispell.ifd[1]);

          /* if something goes wrong, we don't want to block here forever */
          old_timeout = signal(SIGALRM, timeout_signal);
          alarm(10);
          waitpid(ispell.pid, NULL, 0);
          alarm(0);
          signal(SIGALRM, old_timeout);

          ispell.pid = 0;
          if (ispell.buf)
            XtFree(ispell.buf);
          ispell.buf = NULL;

          for (i = 0; i < RSTRTBLSZ; i++) {
            prl = rl = replace_list[i];
            while (prl) {
                rl = rl->next;
                XtFree(prl->word);
                XtFree(prl->replace);
                XtFree((char*)prl);
                prl = rl;
            }
            replace_list[i] = NULL;
          }
          for (i = 0; i < ISTRTBLSZ; i++) {
            pil = il = ignore_list[i];
            while (pil) {
                il = il->next;
                XtFree(pil->word);
                XtFree((char*)pil);
                pil = il;
            }
            ignore_list[i] = NULL;
          }
      }

      if (killundo)
          IspellKillUndoBuffer();
    }
    else
      return (False);

    return (True);
}

static void
IspellKillUndoBuffer(void)
{
    ispell_undo *undo, *pundo;

    undo = pundo = ispell.undo_base;
    while (undo) {
      undo = undo->next;
      if (pundo->undo_str)
          XtFree(pundo->undo_str);
      XtFree((char*)pundo);
      pundo = undo;
    }
    ispell.undo_base = ispell.undo_head = NULL;
    ispell.undo_for = NULL;
    ispell.undo_depth = 0;
    XtSetSensitive(ispell.undo, False);
}

/*ARGSUSED*/
static void
RevertIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    Arg args[1];
    char *string, *repstr = NULL;

    XtSetArg(args[0], XtNlabel, &string);
    XtGetValues(ispell.word, args, 1);
    if ((repstr = strchr(string, ' ')) != NULL) {
      string = repstr = XtNewString(string);
      *strchr(repstr, ' ') = '\0';
    }
    XtSetArg(args[0], XtNstring, string);
    XtSetValues(ispell.text, args, 1);
    if (repstr)
      XtFree(repstr);
}

/*ARGSUSED*/
static void
SelectIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    XawListReturnStruct *info = (XawListReturnStruct *)call_data;
    Arg args[1];

    XtSetArg(args[0], XtNstring, ispell.item = info->string);
    XtSetValues(ispell.text, args, 1);
}

/*ARGSUSED*/
void
ReplaceIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    XawTextPosition pos = XawTextGetInsertionPoint(ispell.ascii);
    XawTextBlock check, search, replace;
    Arg args[1];
    char *text;

    if (!ispell.lock)
      return;

    XtSetArg(args[0], XtNlabel, &text);
    XtGetValues(ispell.word, args, 1);
    search.ptr = text;
    search.format = XawFmt8Bit;
    search.firstPos = 0;
    search.length = ispell.right - pos;

    XtSetArg(args[0], XtNstring, &text);
    XtGetValues(ispell.text, args, 1);
    replace.ptr = text;
    replace.format = XawFmt8Bit;
    replace.firstPos = 0;
    replace.length = strlen(text);

    if (strcmp(search.ptr, replace.ptr) != 0 &&
      XawTextReplace(ispell.ascii, pos, pos + search.length,
                   &replace) == XawEditDone) {
      ispell.right += replace.length - search.length;
      IspellCheckUndo();
      ispell.undo_head->undo_str = NULL;
      ispell.undo_head->undo_pos = pos;
      ispell.undo_head->undo_count = 1;

      if (ispell.repeat) {
          ispell.undo_head->repeat = 2; /* To recognize later it was replaced */
          ispell.undo_head->undo_count = ispell.right;
          ispell.undo_head->undo_str = XtNewString(search.ptr);
      }
      if (client_data && !ispell.repeat) {
          XawTextDisableRedisplay(ispell.ascii);
          pos = ispell.right;
          while ((pos = XawTextSourceSearch(ispell.source, pos, XawsdRight, &search))
            != XawTextSearchError) {
            Bool do_replace = True;
            char mb[sizeof(wchar_t)];

            if (XawTextSourceRead(ispell.source, pos - 1, &check, 1) > 0) {
                wctomb(mb, *(wchar_t*)check.ptr);
                do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb);
            }
            if (do_replace &&
                XawTextSourceRead(ispell.source, pos + search.length, &check, 1) > 0) {
                wctomb(mb, *(wchar_t*)check.ptr);
                do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb);
            }
            if (do_replace) {
                XawTextReplace(ispell.ascii, pos, pos + search.length, &replace);
                ++ispell.undo_head->undo_count;
            }
            pos += search.length;
          }
          XawTextEnableRedisplay(ispell.ascii);
      }
      (void)IspellReplacedWord(search.ptr, replace.ptr);

      strncpy(&ispell.sentbuf[1], replace.ptr, sizeof(ispell.sentbuf) - 2);
      ispell.sentbuf[sizeof(ispell.sentbuf) - 1] = '\0';
    }
    else
      Feep();

    if (ispell.repeat)
      ispell.right = ispell.left = XawTextGetInsertionPoint(ispell.ascii);
    else if (!ispell.terse_mode || !ispell.item ||
           strcmp(ispell.item, replace.ptr))
      ispell.right = ispell.left;   /* check it again! */

    ispell.lock = ispell.checkit = False;

    ispell.stat = SEND;
    IspellSetStatus(ispell.working_label);
    while (IspellSend() == 0)
      ;
}

/*ARGSUSED*/
void
IgnoreIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    Arg args[1];
    char *text;

    if (!ispell.lock)
      return;

    XtSetArg(args[0], XtNlabel, &text);
    XtGetValues(ispell.word, args, 1);

    IspellCheckUndo();

    if ((ispell.undo_head->repeat = ispell.repeat) != False) {
      ispell.undo_head->undo_count = ispell.right;
      ispell.undo_head->undo_str = XtNewString(text);
    }
    else
      ispell.undo_head->undo_count = 0;

    ispell.undo_head->undo_pos = XawTextGetInsertionPoint(ispell.ascii);

    if (!ispell.repeat) {
      if (client_data) {
          IspellIgnoredWord(text, ADD, 0);
          ispell.undo_head->undo_str = XtNewString(text);
      }
      else 
          ispell.undo_head->undo_str = NULL;
    }

    ispell.lock = ispell.checkit = False;

    ispell.stat = SEND;
    IspellSetStatus(ispell.working_label);
    while (IspellSend() == 0)
      ;
}

/*ARGSUSED*/
void
AddIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    Arg args[1];
    char *text;
    int cmd = (long)client_data;

    if (!ispell.lock || ispell.repeat)
      return;

    XtSetArg(args[0], XtNlabel, &text);
    XtGetValues(ispell.word, args, 1);

    IspellCheckUndo();
    ispell.undo_head->undo_str = XtNewString(text);
    ispell.undo_head->undo_pos = XawTextGetInsertionPoint(ispell.ascii);
    ispell.undo_head->undo_count = -cmd;

    (void)IspellIgnoredWord(text, ADD, cmd);

    ispell.lock = ispell.checkit = False;
    ispell.stat = SEND;
    IspellSetStatus(ispell.working_label);
    while (IspellSend() == 0)
      ;
}

/*ARGSUSED*/
static void
UndoIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    Bool enable_redisplay = False;
    ispell_undo *undo = ispell.undo_head;

    if ((!ispell.lock && ispell.stat) || !undo)
      return;

    if (ispell.undo_for && strcmp(ispell.undo_for, ispell.dictionary)) {
      XeditPrintf("Undo: Dictionary changed. Undo information was lost.\n");
      IspellKillUndoBuffer();
      Feep();
      return;
    }

    if (undo->terse != ispell.terse_mode)
      IspellSetTerseMode(undo->terse);

    if (undo->format != ispell.format_info->value) {
      struct _ispell_format *fmt = &ispell_format[undo->format];
      ChangeFormatIspell(fmt->sme, (XtPointer)fmt, NULL);
    }

    if (undo->undo_count > 0 && !undo->repeat) {
      XawTextPosition tmp;

      enable_redisplay = undo->undo_count > 1;
      if (enable_redisplay)
          XawTextDisableRedisplay(ispell.ascii);
      while (undo->undo_count--)
          if (!_XawTextSrcUndo((TextSrcObject)ispell.source, &tmp)) {
            Feep();
            break;
          }
    }
    else if (undo->undo_count < 0) {
      if (undo->undo_str)
          (void)IspellIgnoredWord(undo->undo_str, REMOVE, -undo->undo_count);
    }
    else if (undo->undo_str) {
      if (!undo->repeat)
          IspellIgnoredWord(undo->undo_str, REMOVE, 0);
    }

    XawTextSetInsertionPoint(ispell.ascii,
                       ispell.right = ispell.left = undo->undo_pos);
    if (enable_redisplay)
      XawTextEnableRedisplay(ispell.ascii);

    /* need to do it because may be two misspelled words together */
    if (undo->repeat) {
      char **list, **old_list;
      int old_len;
      Arg args[2];

      if (undo->repeat > 1) {
          XawTextDisableRedisplay(ispell.ascii);
          if (!_XawTextSrcUndo((TextSrcObject)ispell.source, &ispell.right))
            Feep();
          XawTextEnableRedisplay(ispell.ascii);
      }
      else
          ispell.right = (XawTextPosition)undo->undo_count;
      IspellSetRepeated(ispell.repeat = True);
      XtSetArg(args[0], XtNlabel, undo->undo_str);
      XtSetValues(ispell.word, args, 1);
      XmuSnprintf(ispell.sentbuf, sizeof(ispell.sentbuf), "^%s",
                strrchr(undo->undo_str, ' ') + 1);
      strcpy(ispell.sendbuf, ispell.sentbuf);
      XtSetArg(args[0], XtNstring, &ispell.sentbuf[1]);
      XtSetValues(ispell.text, args, 1);

      XtSetArg(args[0], XtNlist, &old_list);
      XtSetArg(args[1], XtNnumberStrings, &old_len);
      XtGetValues(ispell.list, args, 2);

      list = (char **)XtMalloc(sizeof(char*));
      list[0] = XtNewString(&ispell.sentbuf[1]);
      XtSetArg(args[0], XtNlist, list);
      XtSetArg(args[1], XtNnumberStrings, 1);
      XtSetValues(ispell.list, args, 2);
      XtSetSensitive(ispell.list, True);
      XawListHighlight(ispell.list, 0);

      if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
          while (--old_len > -1)
            XtFree(old_list[old_len]);
          XtFree((char*)old_list);
      }

      IspellSetSelection(ispell.left, ispell.right);
      IspellSetStatus(ispell.repeat_label);
      ispell.lock = True;
      ispell.checkit = False;
    }
    else if (ispell.repeat) {
      *ispell.sentbuf = '\0';
      IspellSetRepeated(ispell.repeat = False);
    }

    if (undo->prev)
      undo->prev->next = NULL;
    ispell.undo_head = undo->prev;
    if (undo == ispell.undo_base) {
      ispell.undo_base = NULL;
      ispell.undo_for = NULL;
      XtSetSensitive(ispell.undo, False);
    }
    if (undo->undo_str)
      XtFree(undo->undo_str);
    XtFree((char*)undo);
    --ispell.undo_depth;

    if (!ispell.stat || ispell.checkit)
      IspellSetSensitive(True);

    if (!ispell.repeat) {
      ispell.lock = ispell.checkit = False;
      ispell.stat = SEND;
      IspellSetStatus(ispell.working_label);
      while (IspellSend() == 0)
          ;
    }
}

/*ARGSUSED*/
static void
CheckIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    Arg args[1];
    char *text, *str, string[1024];
    int i, len;

    if (!ispell.lock)
      return;

    XtSetArg(args[0], XtNstring, &text);
    XtGetValues(ispell.text, args, 1);

    /* Check only a word at a time */
    len = 0;
    str = text;
    while (*str) {
      if (isalpha(*str) || strchr(ispell.wchars, *str))
          break;
      ++str;
      ++len;
    }
    i = 0;
    while (*str) {
      if (isalpha(*str) || strchr(ispell.wchars, *str))
          string[i++] = *str++;
      else
          break;
    }
    string[i] = '\0';

    if (strcmp(text, string)) {
      XawTextPosition pos = XawTextGetInsertionPoint(ispell.text) - len;

      XtSetArg(args[0], XtNstring, string);
      XtSetValues(ispell.text, args, 1);
      XawTextSetInsertionPoint(ispell.text, pos);
      Feep();
    }

    if (i == 0) {
      Feep();
      return;
    }

    len = XmuSnprintf(ispell.sendbuf, sizeof(ispell.sendbuf), "^%s\n", string);

    ispell.sendbuf[sizeof(ispell.sendbuf) - 1] = '\n';

    write(ispell.ofd[1], ispell.sendbuf, len);

    ispell.lock = False;
    ispell.checkit = True;
    ispell.stat = RECEIVE;
}

/*ARGSUSED*/
static void
LookIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    int len, old_len;
    FILE *fd;
    Arg args[2];
    char *text, *str, **list, **old_list, command[1024], buffer[1024];
    Bool sensitive = True;

    if (!ispell.lock)
      return;

    XtSetArg(args[0], XtNstring, &text);
    XtGetValues(ispell.text, args, 1);

    if (!*text) {
      Feep();
      return;
    }

    if (strlen(ispell.look_cmd) + strlen(text) + strlen(ispell.words_file) + 8
      > sizeof(command) - 1) {
      fprintf(stderr, "Command line too large\n");
      return;
    }

    XmuSnprintf(command, sizeof(command), "%s '^%s.*$' %s",
            ispell.look_cmd, text, ispell.words_file);

    if ((fd = popen(command, "r")) == NULL) {
      fprintf(stderr, "Cannot popen '%s'\n", ispell.look_cmd);
      return;
    }

    list = NULL;
    len = 0;

#define     MAX_LOOK_RESULTS  256
    while (fgets(buffer, sizeof(buffer), fd) != NULL) {
      if ((str = strchr(buffer, '\n')) == NULL) {
          fprintf(stderr, "String is too large\n");
          break;
      }
      *str = '\0';
      if ((len % 16) == 0)
          list = (char**)XtRealloc((char*)list, sizeof(char*) * (len + 16));
      list[len] = XtNewString(buffer);
      if (++len >= MAX_LOOK_RESULTS) {
          Feep();
          break;
      }
    }
#undef MAX_LOOK_RESULTS

    XtSetArg(args[0], XtNlist, &old_list);
    XtSetArg(args[1], XtNnumberStrings, &old_len);
    XtGetValues(ispell.list, args, 2);

    if (len == 0) {
      list = (char**)XtMalloc(sizeof(char*));
      list[0] = XtNewString("");
      len = 1;
      sensitive = False;
    }

    XtSetArg(args[0], XtNlist, list);
    XtSetArg(args[1], XtNnumberStrings, len);
    XtSetValues(ispell.list, args, 2);

    XtSetSensitive(ispell.list, sensitive);
    IspellSetStatus(sensitive ? ispell.look_label : ispell.none_label);

    if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
      while (--old_len > -1)
          XtFree(old_list[old_len]);
      XtFree((char*)old_list);
    }

    pclose(fd);
}

/*ARGSUSED*/
static void
ToggleTerseIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    if (!ispell.lock)
      return;

    ispell.terse_mode = !ispell.terse_mode;
    write(ispell.ofd[1], ispell.terse_mode ? "!\n" : "%\n", 2);
}

/*ARGSUSED*/
static void
ChangeDictionaryIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    ispell_dict *tmp, *dic = (ispell_dict*)client_data;
    XawTextPosition pos = XawTextGetInsertionPoint(ispell.ascii);
    XawTextPosition right = ispell.right;
    Arg args[1];

    if (strcmp(XtName(dic->sme), ispell.dictionary) == 0)
      return;

    if (!ispell.lock) {
      Feep();
      return;
    }

    for (tmp = ispell.dict_info; tmp; tmp = tmp->next)
      if (strcmp(XtName(tmp->sme), ispell.dictionary) == 0) {
          XtSetArg(args[0], XtNleftBitmap, None);
          XtSetValues(tmp->sme, args, 1);
      }

    if (ispell.undo_base && !ispell.undo_for)
      ispell.undo_for = ispell.dictionary;

    XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
    XtSetValues(dic->sme, args, 1);
    ispell.dictionary = XtName(dic->sme);
    ispell.wchars = dic->wchars;
    XtSetArg(args[0], XtNlabel, XtName(dic->sme));
    XtSetValues(ispell.dict, args, 1);

    IspellSetStatus(ispell.working_label);

    (void)IspellEndProcess(True, False);
    ispell.lock = ispell.checkit = False;
    (void)IspellStartProcess();

    ispell.stat = RECEIVE;

    /* restart at the same selected word */
    if (ispell.repeat == False)
      ispell.left = ispell.right = pos;
    else
      ispell.right = right;
}

/*ARGSUSED*/
static void
ChangeFormatIspell(Widget w, XtPointer client_data, XtPointer call_data)
{
    struct _ispell_format *fmt = (struct _ispell_format*)client_data;
    Arg args[1];

    if (strcmp(fmt->name, ispell.formatting) == 0)
      return;

    if (!ispell.lock) {
      Feep();
      return;
    }

    XtSetArg(args[0], XtNleftBitmap, None);
    XtSetValues(ispell.format_info->sme, args, 1);

    XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
    XtSetValues(fmt->sme, args, 1);
    ispell.formatting = fmt->name;
    ispell.format_info = fmt;
    XtSetArg(args[0], XtNlabel, fmt->name);
    XtSetValues(ispell.format, args, 1);
}

static Bool
InitIspell(void)
{
    Atom delete_window;
    char *str, *list;
    XtResource dict_res;
    int i;
    static XtResource text_res[] = {
      {"skipLines", "Skip", XtRString, sizeof(char*),
       XtOffsetOf(struct _ispell, skip), XtRString, "#"},
    };

    if (ispell.shell)
      return (False);

    ispell.shell  = XtCreatePopupShell("ispell", transientShellWidgetClass,
                                   topwindow, NULL, 0);

    XtGetApplicationResources(ispell.shell, (XtPointer)&ispell, resources,
                        XtNumber(resources), NULL, 0);

    ispell.form         = XtCreateManagedWidget("form", formWidgetClass,
                                    ispell.shell, NULL, 0);
    ispell.mispelled    = XtCreateManagedWidget("mispelled", labelWidgetClass,
                                    ispell.form, NULL, 0);
    ispell.repeated     = XtCreateWidget("repeated", labelWidgetClass,
                               ispell.form, NULL, 0);
    ispell.word         = XtCreateManagedWidget("word", commandWidgetClass,
                                    ispell.form, NULL, 0);
    XtAddCallback(ispell.word, XtNcallback, RevertIspell, NULL);
    ispell.replacement  = XtCreateManagedWidget("replacement", labelWidgetClass,
                                    ispell.form, NULL, 0);
    ispell.text         = XtVaCreateManagedWidget("text", asciiTextWidgetClass,
                                    ispell.form,
                                    XtNeditType, XawtextEdit,
                                    NULL);
    ispell.suggestions  = XtCreateManagedWidget("suggestions", labelWidgetClass,
                                    ispell.form, NULL, 0);
    ispell.viewport     = XtCreateManagedWidget("viewport", viewportWidgetClass,
                                    ispell.form, NULL, 0);
    ispell.list         = XtCreateManagedWidget("list", listWidgetClass,
                                    ispell.viewport, NULL, 0);
    XtAddCallback(ispell.list, XtNcallback, SelectIspell, NULL);
    ispell.commands     = XtCreateManagedWidget("commands", formWidgetClass,
                                    ispell.form, NULL, 0);
    ispell.check  = XtCreateManagedWidget("check", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.check, XtNcallback, CheckIspell, NULL);
    ispell.look         = XtCreateManagedWidget("look", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.look, XtNcallback, LookIspell, NULL);
    ispell.undo         = XtCreateManagedWidget("undo", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.undo, XtNcallback, UndoIspell, NULL);
    ispell.replace      = XtCreateManagedWidget("replace", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.replace, XtNcallback, ReplaceIspell, (XtPointer)False);
    ispell.replaceAll   = XtCreateManagedWidget("replaceAll", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.replaceAll, XtNcallback, ReplaceIspell, (XtPointer)True);
    ispell.ignore = XtCreateManagedWidget("ignore", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.ignore, XtNcallback, IgnoreIspell, (XtPointer)False);
    ispell.ignoreAll    = XtCreateManagedWidget("ignoreAll", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.ignoreAll, XtNcallback, IgnoreIspell, (XtPointer)True);
    ispell.add          = XtCreateManagedWidget("add", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.add, XtNcallback, AddIspell, (XtPointer)ASIS);
    ispell.addUncap     = XtCreateManagedWidget("addUncap", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.addUncap, XtNcallback, AddIspell, (XtPointer)UNCAP);
    ispell.suspend      = XtCreateManagedWidget("suspend", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.suspend, XtNcallback, PopdownIspell, (XtPointer)False);
    ispell.cancel = XtCreateManagedWidget("cancel", commandWidgetClass,
                                    ispell.commands, NULL, 0);
    XtAddCallback(ispell.cancel, XtNcallback, PopdownIspell, (XtPointer)True);
    ispell.terse  = XtVaCreateManagedWidget("terse", toggleWidgetClass,
                                      ispell.commands,
                                      XtNstate, ispell.terse_mode,
                                      NULL);
    XtAddCallback(ispell.terse, XtNcallback, ToggleTerseIspell, NULL);
    ispell.status = XtCreateManagedWidget("status", labelWidgetClass,
                                    ispell.form, NULL, 0);
    ispell.options      = XtCreateManagedWidget("options", formWidgetClass,
                                    ispell.form, NULL, 0);
    ispell.dict         = XtVaCreateManagedWidget("dict", menuButtonWidgetClass,
                                      ispell.options,
                                      XtNmenuName, "dictionaries",
                                      NULL);
    ispell.dictMenu     = XtCreatePopupShell("dictionaries", simpleMenuWidgetClass,
                                   ispell.options, NULL, 0);
    XtRealizeWidget(ispell.dictMenu);

    ispell.format = XtVaCreateManagedWidget("format", menuButtonWidgetClass,
                                      ispell.options,
                                      XtNmenuName, "formats",
                                      NULL);
    ispell.formatMenu   = XtCreatePopupShell("formats", simpleMenuWidgetClass,
                                   ispell.options, NULL, 0);
    XtRealizeWidget(ispell.formatMenu);

    XtRealizeWidget(ispell.shell);

    for (i = 0; i < sizeof(ispell_format) / sizeof(ispell_format[0]); i++) {
      struct _ispell_format *fmt = &ispell_format[i];

      fmt->sme = XtCreateManagedWidget(fmt->name, smeBSBObjectClass,
                               ispell.formatMenu, NULL, 0);
      XtAddCallback(fmt->sme, XtNcallback, ChangeFormatIspell, (XtPointer)fmt);

      if (strcmp(fmt->name, ispell.formatting) == 0) {
          Arg args[1];

          XtSetArg(args[0], XtNlabel, ispell.formatting);
          XtSetValues(ispell.format, args, 1);
          XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
          XtSetValues(fmt->sme, args, 1);
          ispell.format_info = fmt;
      }
    }
    if (ispell.format_info == NULL) {
      Arg args[1];
      char msg[256];

      ispell.format_info = &ispell_format[TEXT];

      XmuSnprintf(msg, sizeof(msg),
                "Unrecognized formatting type \"%s\", will use \"%s\"",
                ispell.formatting, ispell.format_info->name);
      XtAppWarning(XtWidgetToApplicationContext(ispell.shell), msg);
      ispell.formatting = ispell.format_info->name;

      XtSetArg(args[0], XtNlabel, ispell.format_info->name);
      XtSetValues(ispell.format, args, 1);
      XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
      XtSetValues(ispell.format_info->sme, args, 1);
    }
    XtGetApplicationResources(ispell_format[TEXT].sme, (XtPointer)&ispell,
                        text_res, XtNumber(text_res), NULL, 0);

    dict_res.resource_name = "wordChars";
    dict_res.resource_class = "Chars";
    dict_res.resource_type = XtRString;
    dict_res.resource_size = sizeof(char*);
    dict_res.resource_offset = XtOffsetOf(ispell_dict, wchars);
    dict_res.default_type = XtRString;
    dict_res.default_addr = "";

    list = XtNewString(ispell.dict_list);
    for (str = strtok(list, " \t,"); str; str = strtok(NULL, " \t,")) {
      ispell_dict *dic = XtNew(ispell_dict);

      dic->sme = XtCreateManagedWidget(str, smeBSBObjectClass,
                               ispell.dictMenu, NULL, 0);
      XtGetApplicationResources(dic->sme, (XtPointer)dic, &dict_res,
                          1, NULL, 0);
      XtAddCallback(dic->sme, XtNcallback, ChangeDictionaryIspell,
                  (XtPointer)dic);
      dic->next = NULL;
      if (!ispell.dict_info)
          ispell.dict_info = dic;
      else {
          ispell_dict *tmp = ispell.dict_info;

          for (; tmp->next; tmp = tmp->next)
            ;
          tmp->next = dic;
      }
      if (strcmp(str, ispell.dictionary) == 0) {
          Arg args[1];

          XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
          XtSetValues(dic->sme, args, 1);
          XtSetArg(args[0], XtNlabel, str);
          XtSetValues(ispell.dict, args, 1);
          ispell.wchars = dic->wchars;
      }
    }
    XtFree(list);

    delete_window = XInternAtom(XtDisplay(ispell.shell), "WM_DELETE_WINDOW", False);
    XSetWMProtocols(XtDisplay(ispell.shell), XtWindow(ispell.shell), &delete_window, 1);

    return (True);
}

Generated by  Doxygen 1.6.0   Back to index